diff --git a/.cargo/config.toml.in b/.cargo/config.toml.in index a003413c5d5d1..b5d751cf1bc70 100644 --- a/.cargo/config.toml.in +++ b/.cargo/config.toml.in @@ -75,9 +75,9 @@ git = "https://github.com/jfkthame/mapped_hyph.git" rev = "eff105f6ad7ec9b79816cfc1985a28e5340ad14b" replace-with = "vendored-sources" -[source."git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2"] +[source."git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56"] git = "https://github.com/mozilla/application-services" -rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" replace-with = "vendored-sources" [source."git+https://github.com/mozilla/audioipc?rev=82fe7fa7e3aaa35468137239a0e4c2f867457214"] diff --git a/Cargo.lock b/Cargo.lock index 6d349ee38186a..0a0312bad0fe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "askama" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ "askama_derive", "itoa", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ "askama_parser", "basic-toml", @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "askama_parser" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" dependencies = [ "memchr", "serde", @@ -987,7 +987,7 @@ dependencies = [ [[package]] name = "context_id" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "chrono", "error-support", @@ -1946,7 +1946,7 @@ dependencies = [ [[package]] name = "error-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "env_logger", "error-support-macros", @@ -1961,7 +1961,7 @@ dependencies = [ [[package]] name = "error-support-macros" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "proc-macro2", "quote", @@ -2056,7 +2056,7 @@ dependencies = [ [[package]] name = "filter_adult" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "base64 0.22.1", "error-support", @@ -2092,7 +2092,7 @@ dependencies = [ [[package]] name = "firefox-versioning" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "serde_json", "thiserror 2.0.12", @@ -2766,9 +2766,9 @@ dependencies = [ [[package]] name = "glean" -version = "66.3.0" +version = "67.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cff88cbb3ec5ecd45a8ba642772a5fb88cc61350b6960d72a7452de138d9e2" +checksum = "8b43d851e3966fafee2933c2891e6b7f7f346866bab7835cb0410700336f424b" dependencies = [ "crossbeam-channel", "glean-core", @@ -2781,9 +2781,9 @@ dependencies = [ [[package]] name = "glean-core" -version = "66.3.0" +version = "67.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34409d1d4bc10da8ddcb584a0a1825d8ad6a5a268bf711320da95c615c30245" +checksum = "b8146e9829a5fe15343fd3c42d8d41e478b02f68b8359346273cc56d7f7feff8" dependencies = [ "android_logger", "bincode", @@ -3467,7 +3467,7 @@ dependencies = [ [[package]] name = "init_rust_components" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "nss", "uniffi", @@ -3476,7 +3476,7 @@ dependencies = [ [[package]] name = "interrupt-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "lazy_static", "parking_lot", @@ -3647,7 +3647,7 @@ dependencies = [ [[package]] name = "jwcrypto" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "base64 0.21.999", "error-support", @@ -4030,7 +4030,7 @@ checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "logins" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "async-trait", @@ -5083,7 +5083,7 @@ dependencies = [ [[package]] name = "nss" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "base64 0.21.999", "error-support", @@ -5112,12 +5112,12 @@ dependencies = [ [[package]] name = "nss_build_common" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" [[package]] name = "nss_sys" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "libsqlite3-sys", "nss_build_common", @@ -5449,7 +5449,7 @@ checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "payload-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "serde", "serde_derive", @@ -5552,7 +5552,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "rc_crypto" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "base64 0.21.999", "error-support", @@ -6072,7 +6072,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relevancy" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "base64 0.21.999", @@ -6096,7 +6096,7 @@ dependencies = [ [[package]] name = "remote_settings" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "camino", @@ -6367,7 +6367,7 @@ dependencies = [ [[package]] name = "search" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "error-support", "firefox-versioning", @@ -6617,13 +6617,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "siphasher" -version = "0.3.999" -dependencies = [ - "siphasher 1.0.1", -] - [[package]] name = "siphasher" version = "1.0.1" @@ -6692,7 +6685,7 @@ dependencies = [ [[package]] name = "sql-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "error-support", "interrupt-support", @@ -6883,7 +6876,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "suggest" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "chrono", @@ -6936,7 +6929,7 @@ dependencies = [ [[package]] name = "sync-guid" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "base64 0.21.999", "rand", @@ -6947,7 +6940,7 @@ dependencies = [ [[package]] name = "sync15" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "base16", @@ -6991,7 +6984,7 @@ dependencies = [ [[package]] name = "tabs" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "error-support", @@ -7340,7 +7333,7 @@ dependencies = [ [[package]] name = "tracing-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "parking_lot", "serde_json", @@ -7420,7 +7413,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "types" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "rusqlite 0.37.0", "serde", @@ -7502,9 +7495,9 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "uniffi" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b334fd69b3cf198b63616c096aabf9820ab21ed9b2aa1367ddd4b411068bf520" +checksum = "b8c6dec3fc6645f71a16a3fa9ff57991028153bd194ca97f4b55e610c73ce66a" dependencies = [ "anyhow", "cargo_metadata", @@ -7546,9 +7539,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff0132b533483cf19abb30bba5c72c24d9f3e4d9a2ff71cb3e22e73899fd46e" +checksum = "4ed0150801958d4825da56a41c71f000a457ac3a4613fa9647df78ac4b6b6881" dependencies = [ "anyhow", "askama", @@ -7563,7 +7556,7 @@ dependencies = [ "serde", "tempfile", "textwrap", - "toml 0.5.999", + "toml 0.9.8", "uniffi_internal_macros", "uniffi_meta", "uniffi_pipeline", @@ -7600,9 +7593,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d84d607076008df3c32dd2100ee4e727269f11d3faa35691af70d144598f666" +checksum = "b78fd9271a4c2e85bd2c266c5a9ede1fac676eb39fd77f636c27eaf67426fd5f" dependencies = [ "anyhow", "camino", @@ -7611,9 +7604,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e3b997192dc15ef1778c842001811ec7f241a093a693ac864e1fc938e64fa9" +checksum = "b0ef62e69762fbb9386dcb6c87cd3dd05d525fa8a3a579a290892e60ddbda47e" dependencies = [ "anyhow", "bytes", @@ -7623,9 +7616,9 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64bec2f3a33f2f08df8150e67fa45ba59a2ca740bf20c1beb010d4d791f9a1b" +checksum = "98f51ebca0d9a4b2aa6c644d5ede45c56f73906b96403c08a1985e75ccb64a01" dependencies = [ "anyhow", "indexmap", @@ -7636,9 +7629,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8708716d2582e4f3d7e9f320290b5966eb951ca421d7630571183615453efc" +checksum = "db9d12529f1223d014fd501e5f29ca0884d15d6ed5ddddd9f506e55350327dc3" dependencies = [ "camino", "fs-err", @@ -7647,27 +7640,27 @@ dependencies = [ "quote", "serde", "syn", - "toml 0.5.999", + "toml 0.9.8", "uniffi_meta", ] [[package]] name = "uniffi_meta" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d226fc167754ce548c5ece9828c8a06f03bf1eea525d2659ba6bd648bd8e2f3" +checksum = "9df6d413db2827c68588f8149d30d49b71d540d46539e435b23a7f7dbd4d4f86" dependencies = [ "anyhow", - "siphasher 0.3.999", + "siphasher", "uniffi_internal_macros", "uniffi_pipeline", ] [[package]] name = "uniffi_pipeline" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b925b6421df15cf4bedee27714022cd9626fb4d7eee0923522a608b274ba4371" +checksum = "a806dddc8208f22efd7e95a5cdf88ed43d0f3271e8f63b47e757a8bbdb43b63a" dependencies = [ "anyhow", "heck", @@ -7678,9 +7671,9 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a8386f86b6f986bc01d6cdaec395980b9125ae493634ddbdc9feef5f50d80af" +checksum = "4802bed208a296657eef3451a961a6502e1d7aa8930b4b0cd952ed4f81a3957e" dependencies = [ "anyhow", "camino", @@ -7691,9 +7684,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.29.3" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c42649b721df759d9d4692a376b82b62ce3028ec9fc466f4780fb8cdf728996" +checksum = "0d1a7339539bf6f6fa3e9b534dece13f778bda2d54b1a6d4e40b4d6090ac26e7" dependencies = [ "anyhow", "textwrap", @@ -7801,7 +7794,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "viaduct" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "async-trait", "error-support", @@ -7976,7 +7969,7 @@ dependencies = [ [[package]] name = "webext-storage" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2#0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" +source = "git+https://github.com/mozilla/application-services?rev=900d0b03aeb82245c28a11a9adc1cbbead31ce56#900d0b03aeb82245c28a11a9adc1cbbead31ce56" dependencies = [ "anyhow", "error-support", diff --git a/Cargo.toml b/Cargo.toml index 6f669ec9414bc..029ced130c1f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,13 +71,13 @@ rust-version = "1.90.0" [workspace.dependencies] # Shared across multiple UniFFI consumers. -uniffi = "0.29.3" -uniffi_bindgen = "0.29.3" -uniffi_pipeline = "0.29.3" +uniffi = "0.31" +uniffi_bindgen = "0.31" +uniffi_pipeline = "0.31" # Shared across multiple application-services consumers. rusqlite = "0.37" # Shared across multiple glean consumers. -glean = "=66.3.0" +glean = "=67.0.0" # Explicitly specify what our profiles use. The opt-level setting here is # a total fiction; see the setup of MOZ_RUST_DEFAULT_FLAGS for what the @@ -172,10 +172,6 @@ bindgen = { path = "build/rust/bindgen" } # Patch nix 0.29 to 0.30+ nix = { path = "build/rust/nix" } -# Patch siphasher 0.3 to 1.0 -# TODO: Remove once https://github.com/mozilla/uniffi-rs/pull/2709 lands in m-c. -siphasher = { path = "build/rust/siphasher" } - # Patch autocfg to hide rustc output. Workaround for https://github.com/cuviper/autocfg/issues/30 autocfg = { path = "third_party/rust/autocfg" } @@ -270,21 +266,21 @@ objc = { git = "https://github.com/glandium/rust-objc", rev = "4de89f5aa9851ceca allocator-api2 = { git = "https://github.com/glandium/allocator-api2", rev = "ad5f3d56a5a4519eff52af4ff85293431466ef5c" } # application-services overrides to make updating them all simpler. -context_id = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -error-support = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -filter_adult = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -relevancy = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -search = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -sql-support = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -suggest = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -sync15 = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -tabs = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -tracing-support = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -viaduct = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -webext-storage = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -logins = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } -init_rust_components = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } +context_id = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +error-support = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +filter_adult = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +relevancy = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +search = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +sql-support = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +suggest = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +sync15 = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +tabs = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +tracing-support = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +viaduct = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +webext-storage = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +logins = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } +init_rust_components = { git = "https://github.com/mozilla/application-services", rev = "900d0b03aeb82245c28a11a9adc1cbbead31ce56" } # Patched version of zip 2.4.2 to allow for reading omnijars. zip = { path = "third_party/rust/zip" } diff --git a/browser/components/aiwindow/ui/assets/input-cta-arrow-icon.svg b/browser/components/aiwindow/ui/assets/input-cta-arrow-icon.svg index 2f3ade0977c7c..53f8960e011a8 100644 --- a/browser/components/aiwindow/ui/assets/input-cta-arrow-icon.svg +++ b/browser/components/aiwindow/ui/assets/input-cta-arrow-icon.svg @@ -2,5 +2,5 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at https://mozilla.org/MPL/2.0/. --> - + diff --git a/browser/components/aiwindow/ui/assets/memories-off.svg b/browser/components/aiwindow/ui/assets/memories-off.svg index df5dabd625432..03c7e6a86055d 100644 --- a/browser/components/aiwindow/ui/assets/memories-off.svg +++ b/browser/components/aiwindow/ui/assets/memories-off.svg @@ -3,13 +3,13 @@ - file, You can obtain one at https://mozilla.org/MPL/2.0/. --> - + diff --git a/browser/components/aiwindow/ui/assets/memories-on.svg b/browser/components/aiwindow/ui/assets/memories-on.svg index c02e3cff8ad04..6f45ab5470c9a 100644 --- a/browser/components/aiwindow/ui/assets/memories-on.svg +++ b/browser/components/aiwindow/ui/assets/memories-on.svg @@ -3,7 +3,7 @@ - file, You can obtain one at https://mozilla.org/MPL/2.0/. --> - + diff --git a/browser/components/aiwindow/ui/components/input-cta/input-cta.css b/browser/components/aiwindow/ui/components/input-cta/input-cta.css index e0a3e1f6eee32..3a0dfbafc561d 100644 --- a/browser/components/aiwindow/ui/components/input-cta/input-cta.css +++ b/browser/components/aiwindow/ui/components/input-cta/input-cta.css @@ -13,11 +13,12 @@ moz-button.input-cta { --button-background-color-active: color-mix(in srgb, black 25%, transparent); - --button-background-color-disabled: var(--color-gray-20); + --button-background-color-disabled: light-dark(var(--color-gray-20), var(--color-gray-60)); --button-background-color-hover: color-mix(in srgb, black 15%, transparent); --button-background-color-selected: color-mix(in srgb, black 25%, transparent); --button-background-color: transparent; --button-border-radius: var(--border-radius-circle); + --button-icon-fill: light-dark(var(--color-gray-60), white); --button-padding: 0 var(--space-small) 0 var(--space-large); --button-text-color-active: white; --button-text-color-disabled: white; diff --git a/browser/components/aiwindow/ui/components/memories-icon-button/memories-icon-button.css b/browser/components/aiwindow/ui/components/memories-icon-button/memories-icon-button.css index 3a3d068688e4e..56c5e7fe5bfe2 100644 --- a/browser/components/aiwindow/ui/components/memories-icon-button/memories-icon-button.css +++ b/browser/components/aiwindow/ui/components/memories-icon-button/memories-icon-button.css @@ -3,6 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ moz-button.memories-icon-button { - --button-icon-fill: var(--color-gray-70); + --button-icon-fill: light-dark(var(--color-gray-70), white); --button-background-color-ghost-selected: transparent; } diff --git a/browser/components/ipprotection/IPPNimbusHelper.sys.mjs b/browser/components/ipprotection/IPPNimbusHelper.sys.mjs index bc13e1eb7d178..a226fda851820 100644 --- a/browser/components/ipprotection/IPPNimbusHelper.sys.mjs +++ b/browser/components/ipprotection/IPPNimbusHelper.sys.mjs @@ -33,21 +33,22 @@ class IPPNimbusHelperSingleton { } /** - * Check if this device is in the experiment with a variant branch. + * Check that this device is not in the control branch of an experiment. * * @returns {boolean} */ get isEligible() { let inExperiment = lazy.NimbusFeatures.ipProtection.getEnrollmentMetadata(); - let isEligible = inExperiment?.branch && inExperiment.branch !== "control"; if (inExperiment) { lazy.NimbusFeatures.ipProtection.recordExposureEvent({ once: true, }); + + return inExperiment.branch !== "control"; } - return isEligible; + return true; } } diff --git a/browser/components/ipprotection/tests/browser/browser_IPPProxyManager.js b/browser/components/ipprotection/tests/browser/browser_IPPProxyManager.js index 044d14d598bb7..ae76d38745041 100644 --- a/browser/components/ipprotection/tests/browser/browser_IPPProxyManager.js +++ b/browser/components/ipprotection/tests/browser/browser_IPPProxyManager.js @@ -8,15 +8,13 @@ const { IPProtectionServerlist } = ChromeUtils.importESModule( "moz-src:///browser/components/ipprotection/IPProtectionServerlist.sys.mjs" ); -// Don't add an experiment so we can test adding and removing it. -DEFAULT_EXPERIMENT = null; - add_task(async function test_IPPProxyManager_handleProxyErrorEvent() { setupService({ isSignedIn: true, canEnroll: true, }); - let cleanupAlpha = await setupExperiment({ enabled: true, variant: "alpha" }); + + IPProtectionService.updateState(); await IPProtectionServerlist.maybeFetchList(); @@ -115,7 +113,6 @@ add_task(async function test_IPPProxyManager_handleProxyErrorEvent() { "Should not return a promise when connection is inactive" ); - await cleanupAlpha(); cleanupService(); }); @@ -128,6 +125,8 @@ add_task(async function test_IPPProxyManager_bug_1999946() { "moz-src:///browser/components/ipprotection/IPPChannelFilter.sys.mjs" ); + Services.prefs.clearUserPref("browser.ipProtection.enabled"); + // Hook the Call to create to capture the created channel filter let channelFilterRef = null; const sandbox = sinon.createSandbox(); @@ -145,7 +144,9 @@ add_task(async function test_IPPProxyManager_bug_1999946() { canEnroll: true, }); - let cleanupAlpha = await setupExperiment({ enabled: true, variant: "alpha" }); + await SpecialPowers.pushPrefEnv({ + set: [["browser.ipProtection.enabled", true]], + }); await IPProtectionServerlist.maybeFetchList(); @@ -158,6 +159,5 @@ add_task(async function test_IPPProxyManager_bug_1999946() { ); sandbox.restore(); - await cleanupAlpha(); cleanupService(); }); diff --git a/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js b/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js index 55b9be0ae3e47..2274317ef04a7 100644 --- a/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js +++ b/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js @@ -22,9 +22,6 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule( AddonTestUtils.initMochitest(this); -// Don't add an experiment so we can test adding and removing it. -DEFAULT_EXPERIMENT = null; - /** * Tests getting eligibility from a Nimbus experiment and * creating and destroying the widget. @@ -64,6 +61,7 @@ add_task(async function test_IPProtectionService_updateEligibility() { * Tests a user who was previously enrolled will be shown the widget. */ add_task(async function test_IPProtectionService_updateEnrollment() { + Services.prefs.clearUserPref("browser.ipProtection.enabled"); setupService({ isSignedIn: true, isEnrolledAndEntitled: true, @@ -132,6 +130,7 @@ add_task(async function test_IPProtectionService_enroll() { */ add_task( async function test_IPProtectionService_enroll_when_enrolled_in_experiment() { + Services.prefs.clearUserPref("browser.ipProtection.enabled"); setupService({ isEnrolledAndEntitled: false, isSignedIn: true, @@ -210,6 +209,7 @@ add_task( * Tests the entitlement updates when not in the experiment. */ add_task(async function test_IPProtectionService_updateEntitlement() { + Services.prefs.clearUserPref("browser.ipProtection.enabled"); setupService({ isSignedIn: true, isEnrolledAndEntitled: true, @@ -232,6 +232,7 @@ add_task(async function test_IPProtectionService_updateEntitlement() { }); add_task(async function test_ipprotection_ready() { + Services.prefs.clearUserPref("browser.ipProtection.enabled"); setupService({ isSignedIn: true, isEnrolledAndEntitled: true, diff --git a/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js b/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js index bc461cc069155..fb7b2e34e9d46 100644 --- a/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js +++ b/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js @@ -36,12 +36,14 @@ add_task(async function toolbar_added_and_removed() { "IP Protection widget added in the correct position" ); // Disable the feature - await cleanupExperiment(); + Services.prefs.clearUserPref("browser.ipProtection.enabled"); widget = document.getElementById(IPProtectionWidget.WIDGET_ID); Assert.equal(widget, null, "IP Protection widget is removed"); // Reenable the feature - await setupExperiment(); + await SpecialPowers.pushPrefEnv({ + set: [["browser.ipProtection.enabled", true]], + }); widget = document.getElementById(IPProtectionWidget.WIDGET_ID); Assert.ok( BrowserTestUtils.isVisible(widget), @@ -232,7 +234,8 @@ add_task(async function toolbar_placement_customized() { ); // Disable the feature - await cleanupExperiment(); + Services.prefs.clearUserPref("browser.ipProtection.enabled"); + let widget = document.getElementById(IPProtectionWidget.WIDGET_ID); Assert.equal(widget, null, "IP Protection widget is removed"); @@ -244,7 +247,9 @@ add_task(async function toolbar_placement_customized() { ); // Reenable the feature - await setupExperiment(); + await SpecialPowers.pushPrefEnv({ + set: [["browser.ipProtection.enabled", true]], + }); await waitForStateChange; @@ -287,7 +292,7 @@ add_task(async function toolbar_removed() { Assert.equal(end, null, "IP Protection widget is removed"); // Disable the feature - await cleanupExperiment(); + Services.prefs.clearUserPref("browser.ipProtection.enabled"); const waitForStateChange = BrowserTestUtils.waitForEvent( lazy.IPProtectionService, @@ -297,7 +302,9 @@ add_task(async function toolbar_removed() { ); // Reenable the feature - await setupExperiment(); + await SpecialPowers.pushPrefEnv({ + set: [["browser.ipProtection.enabled", true]], + }); await waitForStateChange; diff --git a/browser/components/ipprotection/tests/browser/head.js b/browser/components/ipprotection/tests/browser/head.js index 73730a1eb5e0b..9cea829a351c9 100644 --- a/browser/components/ipprotection/tests/browser/head.js +++ b/browser/components/ipprotection/tests/browser/head.js @@ -295,17 +295,15 @@ add_setup(async function setupVPN() { setupService(); await putServerInRemoteSettings(DEFAULT_SERVICE_STATUS.serverList); - await IPProtectionService.init(); - if (DEFAULT_EXPERIMENT) { - await setupExperiment(); - } + await SpecialPowers.pushPrefEnv({ + set: [["browser.ipProtection.enabled", true]], + }); registerCleanupFunction(async () => { cleanupService(); - IPProtectionService.uninit(); + Services.prefs.clearUserPref("browser.ipProtection.enabled"); setupSandbox.restore(); - cleanupExperiment(); CustomizableUI.reset(); Services.prefs.clearUserPref(IPProtectionWidget.ADDED_PREF); Services.prefs.clearUserPref("browser.ipProtection.panelOpenCount"); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPPAutoRestore.js b/browser/components/ipprotection/tests/xpcshell/test_IPPAutoRestore.js index f171cbfbb93e9..7d4a8a69aa409 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPPAutoRestore.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPPAutoRestore.js @@ -124,8 +124,8 @@ add_task(async function test_IPPAutoRestore_if_notReady() { Assert.equal( IPProtectionService.state, - IPProtectionStates.UNAVAILABLE, - "State is UNAVAILABLE when user is not signed in" + IPProtectionStates.UNAUTHENTICATED, + "State is UNAUTHENTICATED when user is not signed in" ); let autoRestore = new IPPAutoRestoreSingleton(); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPPOnboardingMessageHelper.js b/browser/components/ipprotection/tests/xpcshell/test_IPPOnboardingMessageHelper.js index b914ec16682fe..279acd3ac746f 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPPOnboardingMessageHelper.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPPOnboardingMessageHelper.js @@ -24,14 +24,16 @@ add_task(async function test_IPPOnboardingMessage() { let sandbox = sinon.createSandbox(); setupStubs(sandbox); - IPProtectionService.init(); - - await waitForEvent( + let readyEventPromise = waitForEvent( IPProtectionService, "IPProtectionService:StateChanged", () => IPProtectionService.state === IPProtectionStates.READY ); + IPProtectionService.init(); + + await readyEventPromise; + Assert.ok( !IPPProxyManager.activatedAt, "IP Protection service should not be active initially" diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js index 2119654942686..a767eccc10d4a 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js @@ -174,7 +174,7 @@ add_task(async function test_IPProtectionPanel_signedIn() { }); /** - * Tests that IPProtectionService unavailable state event updates the state. + * Tests that IPProtectionService unauthenticated state event updates the state. */ add_task(async function test_IPProtectionPanel_signedOut() { let sandbox = sinon.createSandbox(); @@ -189,7 +189,7 @@ add_task(async function test_IPProtectionPanel_signedOut() { let signedOutEventPromise = waitForEvent( IPProtectionService, "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.UNAVAILABLE + () => IPProtectionService.state === IPProtectionStates.UNAUTHENTICATED ); IPProtectionService.updateState(); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js index b02f76d26cf4a..d1825d750fcf9 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js @@ -77,7 +77,7 @@ add_task(async function test_IPProtectionService_updateState_signedOut() { let signedOutEventPromise = waitForEvent( IPProtectionService, "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.UNAVAILABLE + () => IPProtectionService.state === IPProtectionStates.UNAUTHENTICATED ); IPProtectionService.updateState(); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js index 43b75adb9f4ab..47a008a31ef44 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js @@ -35,7 +35,7 @@ add_task(async function test_IPProtectionStates_uninitialized() { Assert.equal( IPProtectionService.state, - IPProtectionStates.UNAVAILABLE, + IPProtectionStates.UNAUTHENTICATED, "IP Protection service should be initialized" ); @@ -57,6 +57,7 @@ add_task(async function test_IPProtectionStates_uninitialized() { sandbox .stub(IPProtectionService.guardian, "isLinkedToGuardian") .resolves(false); + sandbox.stub(IPPNimbusHelper, "isEligible").get(() => false); await IPProtectionService.init(); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js b/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js index 662a59b8d481a..5e22e0d38d8e4 100644 --- a/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js +++ b/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js @@ -19,14 +19,16 @@ add_task(async function test_IPPProxyManager_start() { let sandbox = sinon.createSandbox(); setupStubs(sandbox); - IPProtectionService.init(); - - await waitForEvent( + let readyEventPromise = waitForEvent( IPProtectionService, "IPProtectionService:StateChanged", () => IPProtectionService.state === IPProtectionStates.READY ); + IPProtectionService.init(); + + await readyEventPromise; + Assert.ok( !IPPProxyManager.activatedAt, "IP Protection service should not be active initially" diff --git a/browser/components/preferences/tests/browser_privacy_allowListPreference_reload.js b/browser/components/preferences/tests/browser_privacy_allowListPreference_reload.js index 7b3bf14f75289..9395befc8bb8a 100644 --- a/browser/components/preferences/tests/browser_privacy_allowListPreference_reload.js +++ b/browser/components/preferences/tests/browser_privacy_allowListPreference_reload.js @@ -41,7 +41,14 @@ async function setup() { add_task( async function test_reload_does_not_show_after_baseline_checkbox_dialog_cancel() { await SpecialPowers.pushPrefEnv({ - set: [[CB_CATEGORY_PREF, "strict"]], + set: [ + [CB_CATEGORY_PREF, "strict"], + // Prevent migration from running and changing baseline to false + [ + "privacy.trackingprotection.allow_list.hasMigratedCategoryPrefs", + true, + ], + ], }); Assert.ok( Services.prefs.getBoolPref(BASELINE_PREF), diff --git a/browser/components/tabbrowser/content/split-view-footer.js b/browser/components/tabbrowser/content/split-view-footer.js index 8a415f7369cd8..96a6df397fd1b 100644 --- a/browser/components/tabbrowser/content/split-view-footer.js +++ b/browser/components/tabbrowser/content/split-view-footer.js @@ -217,9 +217,11 @@ #resetTab() { if (this.#tab) { this.#tab.removeEventListener("TabAttrModified", this); - this.#tab.linkedBrowser?.removeProgressListener( - this.#browserProgressListener - ); + if (this.#tab.linkedBrowser?.webProgress) { + this.#tab.linkedBrowser.removeProgressListener( + this.#browserProgressListener + ); + } } this.#tab = null; } diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js index 7af4588ca8f17..a8c22577366a0 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -3446,6 +3446,7 @@ panels.push(tab.linkedPanel); } } + this.setIsSplitViewActive(true, tabs); this.tabpanels.splitViewPanels = panels; } @@ -5924,15 +5925,22 @@ } let unloadSelectedTab = false; let allTabsUnloaded = false; - if (tabs.some(tab => tab.selected)) { + if (tabs.some(tab => tab.selected || tab.splitview?.hasActiveTab)) { // Unloading the currently selected tab. // Need to select a different one before unloading. // Avoid selecting any tab we're unloading now or // any tab that is already unloaded. unloadSelectedTab = true; - const tabsToExclude = tabs.concat( + let tabsToExclude = tabs.concat( this.tabContainer.allTabs.filter(tab => !tab.linkedPanel) ); + for (const tab of tabs) { + if (tab.splitview) { + tabsToExclude.push( + ...tab.splitview.tabs.filter(splitViewTab => splitViewTab != tab) + ); + } + } let newTab = this._findTabToBlurTo(this.selectedTab, tabsToExclude); if (newTab) { this.selectedTab = newTab; diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_about_opentabs.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_about_opentabs.js index 2e6afc6777476..7f3d6979a95f3 100644 --- a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_about_opentabs.js +++ b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_about_opentabs.js @@ -58,13 +58,14 @@ const withTabMenu = async function (tab, callback) { const moveTabToNewSplitViewItem = document.getElementById( "context_moveTabToSplitView" ); + const unloadTabItem = document.getElementById("context_unloadTab"); const unsplitTabItem = document.getElementById("context_separateSplitView"); let contextMenuHidden = BrowserTestUtils.waitForPopupEvent( tabContextMenu, "hidden" ); - await callback(moveTabToNewSplitViewItem, unsplitTabItem); + await callback(moveTabToNewSplitViewItem, unloadTabItem, unsplitTabItem); tabContextMenu.hidePopup(); info("Hide popup"); return await contextMenuHidden; @@ -245,7 +246,7 @@ add_task(async function test_contextMenuMoveTabsToNewSplitView() { await withTabMenu( tabToClick, - async (moveTabToNewSplitViewItem, unsplitTabItem) => { + async (moveTabToNewSplitViewItem, unloadTabItem, unsplitTabItem) => { await BrowserTestUtils.waitForMutationCondition( unsplitTabItem, { attributes: true }, @@ -410,3 +411,79 @@ add_task(async function test_containerIndicators() { BrowserTestUtils.removeTab(gBrowser.tabs.at(-1)); } }); + +add_task(async function test_splitview_with_unloaded_tab() { + const tab1 = await addTab(); + await addTab(); + + // Click the first tab in our test split view to make sure the default tab at the + // start of the tab strip is deselected + EventUtils.synthesizeMouseAtCenter(tab1, {}); + + // Test adding split view with one tab and new tab + + let tabToClick = tab1; + let tabContainer = gBrowser.tabContainer; + let splitViewCreated = BrowserTestUtils.waitForEvent( + tabContainer, + "SplitViewCreated" + ); + await withTabMenu( + tabToClick, + async (moveTabToNewSplitViewItem, unloadTabItem) => { + await BrowserTestUtils.waitForMutationCondition( + unloadTabItem, + { attributes: true }, + () => !unloadTabItem.hidden && !unloadTabItem.disabled, + "unloadTabItem is visible and not disabled" + ); + Assert.ok( + !unloadTabItem.hidden && !unloadTabItem.disabled, + "unloadTabItem is visible and not disabled" + ); + + info("Click menu option to unload tab"); + unloadTabItem.click(); + await BrowserTestUtils.waitForMutationCondition( + tab1, + { attributes: true }, + () => tab1.hasAttribute("discarded"), + "tab1 has been unloaded" + ); + info("Tab has been unloaded"); + } + ); + + await withTabMenu(tabToClick, async moveTabToNewSplitViewItem => { + await BrowserTestUtils.waitForMutationCondition( + moveTabToNewSplitViewItem, + { attributes: true }, + () => + !moveTabToNewSplitViewItem.hidden && + !moveTabToNewSplitViewItem.disabled, + "moveTabToNewSplitViewItem is visible and not disabled" + ); + Assert.ok( + !moveTabToNewSplitViewItem.hidden && !moveTabToNewSplitViewItem.disabled, + "moveTabToNewSplitViewItem is visible and not disabled" + ); + + info("Click menu option to add new split view"); + moveTabToNewSplitViewItem.click(); + await splitViewCreated; + }); + + let splitview = tab1.splitview; + + Assert.equal(tab1.splitview, splitview, `tab1 is in split view`); + + Assert.ok( + !tab1.hasAttribute("discarded"), + "tab1 is no longer unloaded once added to split view" + ); + + splitview.unsplitTabs(); + while (gBrowser.tabs.length > 1) { + BrowserTestUtils.removeTab(gBrowser.tabs.at(-1)); + } +}); diff --git a/browser/extensions/newtab/content-src/styles/activity-stream.scss b/browser/extensions/newtab/content-src/styles/activity-stream.scss index 1694ee9f84667..c77ef033aae2d 100644 --- a/browser/extensions/newtab/content-src/styles/activity-stream.scss +++ b/browser/extensions/newtab/content-src/styles/activity-stream.scss @@ -134,6 +134,10 @@ input { } } +panel-item::part(button) { + padding-inline-start: var(--space-medium); +} + // These styles are needed for -webkit-line-clamp to work correctly, so reuse // this class name while separately setting a clamp value via CSS or JS. .clamp { diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css index f79cfaa3de384..ba6e06651fde6 100644 --- a/browser/extensions/newtab/css/activity-stream.css +++ b/browser/extensions/newtab/css/activity-stream.css @@ -425,6 +425,10 @@ input[type=text], input[type=search] { border-radius: var(--border-radius-small); } +panel-item::part(button) { + padding-inline-start: var(--space-medium); +} + .clamp { -webkit-box-orient: vertical; display: -webkit-box; diff --git a/browser/extensions/webcompat/experiment-apis/appConstants.js b/browser/extensions/webcompat/experiment-apis/appConstants.js index 277890fca8f3f..0c20caf6ab9a4 100644 --- a/browser/extensions/webcompat/experiment-apis/appConstants.js +++ b/browser/extensions/webcompat/experiment-apis/appConstants.js @@ -6,26 +6,6 @@ /* global AppConstants, ExtensionAPI, XPCOMUtils */ -const lazy = {}; - -if (AppConstants.ENABLE_WEBDRIVER) { - XPCOMUtils.defineLazyServiceGetter( - lazy, - "Marionette", - "@mozilla.org/remote/marionette;1", - Ci.nsIMarionette - ); - XPCOMUtils.defineLazyServiceGetter( - lazy, - "RemoteAgent", - "@mozilla.org/remote/agent;1", - Ci.nsIRemoteAgent - ); -} else { - lazy.Marionette = { running: false }; - lazy.RemoteAgent = { running: false }; -} - this.appConstants = class extends ExtensionAPI { getAPI() { return { @@ -68,13 +48,6 @@ this.appConstants = class extends ExtensionAPI { } return "unknown"; }, - isInAutomation: () => { - return ( - Cu.isInAutomation || - lazy.Marionette.running || - lazy.RemoteAgent.running - ); - }, }, }; } diff --git a/browser/extensions/webcompat/experiment-apis/appConstants.json b/browser/extensions/webcompat/experiment-apis/appConstants.json index e48807e751e15..4ccf13bed6d75 100644 --- a/browser/extensions/webcompat/experiment-apis/appConstants.json +++ b/browser/extensions/webcompat/experiment-apis/appConstants.json @@ -52,16 +52,6 @@ "type": "string", "description": "The release branch (dev_edition, early_beta_or_earlier, nightly, release_or_beta, or unknown)." } - }, - { - "name": "isInAutomation", - "type": "function", - "description": "", - "parameters": [], - "returns": { - "type": "boolean", - "description": "Whether Firefox is running in automation." - } } ] } diff --git a/browser/extensions/webcompat/lib/intervention_helpers.js b/browser/extensions/webcompat/lib/intervention_helpers.js index 6f90e3a619e79..19020bbb7c02c 100644 --- a/browser/extensions/webcompat/lib/intervention_helpers.js +++ b/browser/extensions/webcompat/lib/intervention_helpers.js @@ -552,111 +552,20 @@ var InterventionHelpers = { stillNeeded ); } catch (e) { - for (const script of scriptsToReg) { - try { - await browser.scripting.registerContentScripts(scriptsToReg); - } catch (e2) { - console.error( - `Error while registering ${typeStr} content script`, - script, - e2 - ); - } - } - debugLog( - `Registered ${typeStr} content scripts after error registering just non-active ones`, - scriptsToReg, - e - ); - } - }, - - async ensureOnlyTheseContentScripts(contentScriptsToRegister, type) { - if (type != "webcompat intervention" && type != "SmartBlock shim") { - throw new Error( - '`type` must be "webcompat intervention" or "SmartBlock shim"' - ); - } - - // Check which content scripts are already registered persistently. - // (we may need to disable ones we no longer need, and also register - // any new ones which are not persisted yet). - const desiredContentScriptIds = new Set( - contentScriptsToRegister.map(s => s.id) - ); - const activeContentScripts = - await browser.scripting.getRegisteredContentScripts(); - - const interventionContentScripts = activeContentScripts.filter(s => - s.id.includes(type) - ); - - const oldContentScriptsToUnregister = interventionContentScripts.filter( - ({ id }) => !desiredContentScriptIds.has(id) - ); - - if (oldContentScriptsToUnregister.length) { - debugLog( - `Unregistering no-longer-needed ${type} content scripts`, - oldContentScriptsToUnregister - ); - try { - await browser.scripting.unregisterContentScripts({ - ids: oldContentScriptsToUnregister.map(s => s.id), - }); - } catch (_) { - for (const script of oldContentScriptsToUnregister) { - try { - await browser.scripting.unregisterContentScripts({ - ids: [script.id], - }); - } catch (e) { - console.error("Error unregistering content script", script, e); - } - } - } - } - - const interventionContentScriptIds = new Set( - interventionContentScripts.map(s => s.id) - ); - const newContentScriptsToRegister = contentScriptsToRegister.filter( - ({ id }) => !interventionContentScriptIds.has(id) - ); - if (newContentScriptsToRegister.length) { - debugLog( - `Registering new ${type} content scripts`, - newContentScriptsToRegister - ); try { - await browser.scripting.registerContentScripts( - newContentScriptsToRegister + await browser.scripting.registerContentScripts(scriptsToReg); + debugLog( + `Registered all ${typeStr} content scripts after error registering just non-active ones`, + scriptsToReg, + e + ); + } catch (e2) { + console.error( + `Error while registering ${typeStr} content scripts:`, + e2, + scriptsToReg ); - } catch (_) { - for (const script of newContentScriptsToRegister) { - try { - await browser.scripting.registerContentScripts([script]); - } catch (e) { - console.error("Error registering content script", script, e); - } - } } } - - const alreadyRegisteredContentScripts = contentScriptsToRegister.filter( - ({ id }) => interventionContentScriptIds.has(id) - ); - if (alreadyRegisteredContentScripts.length) { - debugLog( - `Already have registered ${type} content scripts`, - alreadyRegisteredContentScripts - ); - } - - return { - alreadyRegisteredContentScripts, - newContentScriptsToRegister, - oldContentScriptsToUnregister, - }; }, }; diff --git a/browser/extensions/webcompat/lib/interventions.js b/browser/extensions/webcompat/lib/interventions.js index 342cabe9fe324..6100d9936d7c1 100644 --- a/browser/extensions/webcompat/lib/interventions.js +++ b/browser/extensions/webcompat/lib/interventions.js @@ -159,14 +159,7 @@ class Interventions { constructor(availableInterventions, customFunctions) { this.#customFunctions = customFunctions; this.#originalInterventions = availableInterventions; - let interventions = availableInterventions; - if (browser.appConstants.isInAutomation()) { - const override = browser.aboutConfigPrefs.getPref("test_interventions"); - if (override) { - interventions = JSON.parse(override); - } - } - this.#onSourceJSONChanged(interventions); + this.#onSourceJSONChanged(availableInterventions); } #onSourceJSONChanged(availableInterventions) { @@ -198,9 +191,7 @@ class Interventions { this.#stopListenersForTogglingIndividualInterventions(); await this.#disableInterventionsInternal(); this.#onSourceJSONChanged(newInterventions); - await this.#enableInterventionsInternal({ - alsoClearObsoleteContentScripts: true, - }); + await this.#enableInterventionsInternal(); await this.#signalInterventionChangesToAboutCompat( this.#availableInterventions ); @@ -239,7 +230,7 @@ class Interventions { }); }, ENABLE_INTERVENTIONS_PREF); - this.#checkInterventionPref(true).then(doneBootingUp); + this.#checkInterventionPref().then(doneBootingUp); } async updateInterventions(_data) { @@ -257,22 +248,20 @@ class Interventions { this.#availableInterventions.push(intervention); } } - await this.#enableInterventionsInternal({ whichInterventions: data }); + await this.#enableInterventionsInternal(data); await this.#signalInterventionChangesToAboutCompat(data ?? false); return data; }); } - #checkInterventionPref(alsoClearObsoleteContentScripts = false) { + #checkInterventionPref() { const value = browser.aboutConfigPrefs.getPref( ENABLE_INTERVENTIONS_PREF, true ); this.#interventionsEnabledByPref = value; if (value) { - return this.#enableInterventionsInternal({ - alsoClearObsoleteContentScripts, - }); + return this.#enableInterventionsInternal(); } return this.#disableInterventionsInternal(); } @@ -292,7 +281,7 @@ class Interventions { async enableInterventions(ids, force = false) { await this.#postStartupAtomicOperation(async () => { const whichInterventions = this.getInterventionsByIds(ids); - await this.#enableInterventionsInternal({ force, whichInterventions }); + await this.#enableInterventionsInternal(whichInterventions, force); await this.#signalInterventionChangesToAboutCompat( whichInterventions ?? false ); @@ -438,9 +427,7 @@ class Interventions { if (prefValue === true) { await this.#disableInterventionsInternal([config]); } else { - await this.#enableInterventionsInternal({ - whichInterventions: [config], - }); + await this.#enableInterventionsInternal([config]); } return this.#signalInterventionChangesToAboutCompat([config]); }); @@ -488,13 +475,10 @@ class Interventions { this.#individualDisablingPrefListeners = new Map(); } - #enableInterventionsInternal(options) { - const { - alsoClearObsoleteContentScripts = false, - force = false, - whichInterventions = this.#availableInterventions, - } = options ?? {}; - + #enableInterventionsInternal( + whichInterventions = this.#availableInterventions, + force = false + ) { const enabledUAoverrides = []; const enabledRequestBlocks = []; const enabledCustomFuncs = []; @@ -660,25 +644,10 @@ class Interventions { this.#uaOverridesListener.restartListener(); } - return Promise.resolve().then(async () => { - // If we're still booting up, we need to clean out any persisted content - // scripts for which the intervention has been removed, before we register - // the ones we have chosen to activate above. - if (alsoClearObsoleteContentScripts) { - const info = await InterventionHelpers.ensureOnlyTheseContentScripts( - contentScriptsToRegister, - "webcompat intervention" - ); - if (browser.appConstants.isInAutomation()) { - this._lastEnabledInfo = info; - } - } else { - await InterventionHelpers.registerContentScripts( - contentScriptsToRegister, - "webcompat" - ); - } - + return InterventionHelpers.registerContentScripts( + contentScriptsToRegister, + "webcompat" + ).then(() => { if (enabledUAoverrides.length) { debugLog( "Enabled", @@ -796,20 +765,10 @@ class Interventions { ids: contentScripts.map(s => s.id), }) )?.map(script => script.id); - try { - await browser.scripting.unregisterContentScripts({ ids }); - } catch (_) { - for (const id of ids) { - try { - await browser.scripting.unregisterContentScripts({ ids: [id] }); - } catch (e) { - console.error( - `Error while unregistering intervention content script`, - id, - e - ); - } - } + for (const id of ids) { + try { + await browser.scripting.unregisterContentScripts({ ids: [id] }); + } catch (_) {} } } @@ -817,7 +776,7 @@ class Interventions { const registration = { id: `webcompat intervention for ${label}: ${JSON.stringify(intervention.content_scripts)}`, matches, - persistAcrossSessions: true, + persistAcrossSessions: false, }; let { all_frames, css, js, run_at } = intervention.content_scripts; diff --git a/browser/extensions/webcompat/lib/shims.js b/browser/extensions/webcompat/lib/shims.js index db4af8294fb53..587caa476d535 100644 --- a/browser/extensions/webcompat/lib/shims.js +++ b/browser/extensions/webcompat/lib/shims.js @@ -477,16 +477,8 @@ class Shims { return; } - let shims = availableShims; - if (browser.appConstants.isInAutomation()) { - const override = browser.aboutConfigPrefs.getPref("test_shims"); - if (override) { - shims = JSON.parse(override); - } - } - this._readyPromise = new Promise(done => (this._resolveReady = done)); - this._registerShims(shims); + this._registerShims(availableShims); onMessageFromTab(this._onMessageFromShim.bind(this)); @@ -643,7 +635,7 @@ class Shims { } // Batch-register the content scripts during startup to improve IPC performance. - this._registerContentScriptsForShims(this.shims.values(), true); + this._registerContentScriptsForShims(); // Register onBeforeRequest listener which handles storage access requests // on matching redirects. @@ -1307,13 +1299,10 @@ class Shims { return undefined; } - async _registerContentScriptsForShims( - shims, - alsoClearObsoleteContentScripts - ) { + async _registerContentScriptsForShims(shims) { const contentScriptsToRegister = []; - for (const shim of shims) { + for (const shim of shims ?? this.shims.values()) { if ( shim.disabledReason || !shim.contentScripts.length || @@ -1333,7 +1322,7 @@ class Shims { Object.assign( { id, - persistAcrossSessions: true, + persistAcrossSessions: false, }, options ) @@ -1341,29 +1330,12 @@ class Shims { } } - // If we're still booting up, we need to clean out any persisted content - // scripts for which the intervention has been removed, before we register - // the ones we have chosen to activate above. - if (alsoClearObsoleteContentScripts) { - const info = await InterventionHelpers.ensureOnlyTheseContentScripts( - contentScriptsToRegister, - "SmartBlock shim" - ); - if (browser.appConstants.isInAutomation()) { - this._lastEnabledInfo = info; - } - } else { + if (contentScriptsToRegister.length) { await InterventionHelpers.registerContentScripts( contentScriptsToRegister, "SmartBlock" ); } - - if (alsoClearObsoleteContentScripts) { - // If we're still booting up, we need to clean out any persisted content - // scripts for which the intervention has been removed, before we register - // the ones we have chosen to activate above. - } } async _unregisterContentScriptsForShims(shims) { @@ -1372,16 +1344,10 @@ class Shims { ids.push(...shim._contentScriptRegistrations); shim._contentScriptRegistrations = []; } - try { - await browser.scripting.unregisterContentScripts({ ids }); - } catch (_) { - for (const id of ids) { - try { - await browser.scripting.unregisterContentScripts({ ids: [id] }); - } catch (e) { - console.error(`Error while unregistering shim content script`, id, e); - } - } + for (const id of ids) { + try { + await browser.scripting.unregisterContentScripts({ ids: [id] }); + } catch (_) {} } } } diff --git a/browser/extensions/webcompat/manifest.json b/browser/extensions/webcompat/manifest.json index ccdf06fa50845..b512669bb1090 100644 --- a/browser/extensions/webcompat/manifest.json +++ b/browser/extensions/webcompat/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Web Compatibility Interventions", "description": "Urgent post-release fixes for web compatibility.", - "version": "149.5.0", + "version": "149.4.0", "browser_specific_settings": { "gecko": { "id": "webcompat@mozilla.org", diff --git a/browser/extensions/webcompat/moz.build b/browser/extensions/webcompat/moz.build index fe9ba908c8220..19689b83a42c1 100644 --- a/browser/extensions/webcompat/moz.build +++ b/browser/extensions/webcompat/moz.build @@ -17,7 +17,5 @@ BROWSER_CHROME_MANIFESTS += [ "tests/browser/browser_uuid_migration.toml", ] -MARIONETTE_MANIFESTS += ["tests/marionette/manifest.toml"] - with Files("**"): BUG_COMPONENT = ("Web Compatibility", "Tooling & Investigations") diff --git a/browser/extensions/webcompat/tests/browser/head.js b/browser/extensions/webcompat/tests/browser/head.js index 27e2bc7b9861a..bd5e59b6347dc 100644 --- a/browser/extensions/webcompat/tests/browser/head.js +++ b/browser/extensions/webcompat/tests/browser/head.js @@ -57,6 +57,8 @@ const WebCompatExtension = new (class WebCompatExtension { async resetInterventionsAndShimsToDefaults() { return this.#run(async function () { await content.wrappedJSObject._downgradeForTesting(); + await content.wrappedJSObject.interventions.resetToDefaultInterventions(); + await content.wrappedJSObject.shims._resetToDefaultShims(); }).catch(_ => {}); } diff --git a/browser/extensions/webcompat/tests/marionette/manifest.toml b/browser/extensions/webcompat/tests/marionette/manifest.toml deleted file mode 100644 index f77bb772f13e3..0000000000000 --- a/browser/extensions/webcompat/tests/marionette/manifest.toml +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -run-if = [ - "buildapp == 'browser'", -] - -["test_persistent_contentscripts.py"] diff --git a/browser/extensions/webcompat/tests/marionette/test_persistent_contentscripts.py b/browser/extensions/webcompat/tests/marionette/test_persistent_contentscripts.py deleted file mode 100644 index 283d53f20c870..0000000000000 --- a/browser/extensions/webcompat/tests/marionette/test_persistent_contentscripts.py +++ /dev/null @@ -1,277 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import json - -from marionette_harness import MarionetteTestCase - - -class ContentScriptsAreCorrectlyPersistedTest(MarionetteTestCase): - def setUp(self): - super().setUp() - self.marionette.enforce_gecko_prefs({ - "extensions.background.idle.timeout": 300000, - }) - - def tearDown(self): - try: - # Make sure subsequent tests get a clean profile - self.marionette.restart(in_app=False, clean=True) - finally: - super().tearDown() - - def testContentScriptsAreCorrectlyPersisted(self): - # We check that as the addon starts up, the expected content scripts are all - # started up as well, and in the expected way (either being registered, or - # being already registered as they are marked as persistentAcrossSessions. - - # We also check that if the list of interventions/shims changes (for instance - # in a new version of the addon), any no-longer-needed content scripts which - # are not in its list of interventions or shims are properly unregsitered, - # so they do not persist. - - # First see what happens on first start-up. All content scripts ought to be - # registered by the compat addon, rather than already being there. - lastUpdateInfo, interventionContentScriptPath, shimContentScriptPath = ( - self.wait_until_webcompat_addon_is_ready().values() - ) - self.assertEqual( - lastUpdateInfo["alreadyRegisteredContentScripts"], - [], - "no content scripts were already registered on first startup", - ) - self.assertEqual( - lastUpdateInfo["oldContentScriptsToUnregister"], - [], - "no content scripts were unregistered on first startup", - ) - defaultContentScriptCount = len(lastUpdateInfo["newContentScriptsToRegister"]) - self.assertTrue( - defaultContentScriptCount > 0, - "the addon registerd some content scripts on first startup", - ) - self.assertTrue( - len(interventionContentScriptPath) > 0, - "sanity check: we have an intervention content script to test with", - ) - self.assertTrue( - len(shimContentScriptPath) > 0, - "sanity check: we have a shim content script to test with", - ) - - # Restart Firefox and confirm that the content scripts were all registered - # already before the addon started up (because they are persistent) - self.marionette.restart(in_app=True) - lastUpdateInfo, _, _ = self.wait_until_webcompat_addon_is_ready().values() - self.assertEqual( - len(lastUpdateInfo["alreadyRegisteredContentScripts"]), - defaultContentScriptCount, - "all of the content scripts were already registered on second boot", - ) - self.assertEqual( - lastUpdateInfo["oldContentScriptsToUnregister"], - [], - "no content scripts were unregistered on second boot", - ) - self.assertEqual( - lastUpdateInfo["newContentScriptsToRegister"], - [], - "no new content scripts were registered on second boot", - ) - - # First we prepare a few intervention and shim configurations with content scripts - # to use for the remaining tests. (Note that we need to grab one of the content scripts - # actually in the addon right now, hence the sanity checks above). - bug1 = self.buildInterventionConfig("bug1", interventionContentScriptPath) - bug2 = self.buildInterventionConfig("bug2", interventionContentScriptPath) - bug3 = self.buildInterventionConfig("bug3", interventionContentScriptPath) - - def getInterventionScriptId(bug): - return f"""webcompat intervention for {bug}: {{"js":["{interventionContentScriptPath}"]}}""" - - expectedBug1ScriptId = getInterventionScriptId("bug1") - expectedBug2ScriptId = getInterventionScriptId("bug2") - expectedBug3ScriptId = getInterventionScriptId("bug3") - - shim1 = self.buildShimConfig("bug1", shimContentScriptPath) - shim2 = self.buildShimConfig("bug2", shimContentScriptPath) - shim3 = self.buildShimConfig("bug3", shimContentScriptPath) - - def getShimScriptId(bug): - return f"""SmartBlock shim for {bug}: {{"js":["/shims/{shimContentScriptPath}"],"matches":["*://example.com/*"],"runAt":"document_start"}}""" - - expectedShim1ScriptId = getShimScriptId("bug1") - expectedShim2ScriptId = getShimScriptId("bug2") - expectedShim3ScriptId = getShimScriptId("bug3") - - # Now we simulate a few addon updates to see if things work as expected. - # first across addon-restarts, and then with our RemoteSettings update client. - def runTests(whichMethod): - # Now we simulate a few addon updates to see what happens. First we simulate - # an update with no interventions or shims, to see if they're all unregistered. - lastUpdateInfo, _, _ = self.rebootWithInterventionsAndShims({}, []).values() - self.verifyBootupContentScripts(lastUpdateInfo, [], []) - - # Now restart with one intervention and one shim, and confirm that both - # content scripts were newly registered. - lastUpdateInfo, _, _ = whichMethod({"bug1": bug1}, [shim1]).values() - self.verifyBootupContentScripts( - lastUpdateInfo, [expectedBug1ScriptId, expectedShim1ScriptId], [] - ) - - # Now restart with one different intervention and shim, and - # confirm that bug1 was unregistered (since it's no longer in the new - # update's config), and that the new bug2's was registered. - lastUpdateInfo, _, _ = whichMethod({"bug2": bug2}, [shim2]).values() - self.verifyBootupContentScripts( - lastUpdateInfo, - [expectedBug2ScriptId, expectedShim2ScriptId], - [expectedBug1ScriptId, expectedShim1ScriptId], - ) - - # Now restart with both an already-existing intervention and shim (to see - # if it is already started before the addon does), and a new intervention - # and shim, to confirm that it's also registered. - lastUpdateInfo, _, _ = whichMethod( - {"bug2": bug2, "bug3": bug3}, [shim2, shim3] - ).values() - self.verifyBootupContentScripts( - lastUpdateInfo, - [ - expectedBug2ScriptId, - expectedBug3ScriptId, - expectedShim2ScriptId, - expectedShim3ScriptId, - ], - [], - ) - - # First run the tests simulating an addon-update. - runTests(self.rebootWithInterventionsAndShims) - self.marionette.clear_pref("extensions.webcompat.test_interventions") - self.marionette.clear_pref("extensions.webcompat.test_shims") - - # Not re-run the same tests with RemoteSettings client updates. - self.nextRemoteSettingsUpdateVersion = 9000 - runTests(self.simulateRemoteSettingsUpdate) - - def rebootWithInterventionsAndShims(self, interventions, shims): - self.marionette.set_pref( - "extensions.webcompat.test_interventions", json.dumps(interventions) - ) - self.marionette.set_pref("extensions.webcompat.test_shims", json.dumps(shims)) - self.marionette.restart(in_app=True) - return self.wait_until_webcompat_addon_is_ready() - - def simulateRemoteSettingsUpdate(self, interventions, shims): - version = self.nextRemoteSettingsUpdateVersion - self.nextRemoteSettingsUpdateVersion += 1 - with self.marionette.using_context("chrome"): - self.marionette.execute_async_script( - """ - const [version, interventions, shims, done] = arguments; - const { RemoteSettings } = ChromeUtils.importESModule( - "resource://services-settings/remote-settings.sys.mjs" - ); - const client = RemoteSettings("webcompat-interventions"); - const update = { - id: 1, // RemoteSettings record id", - last_modified: 1368273600000, - version: `9999.9999.9999.${version}`, - interventions, - shims, - }; - client.emit("sync", { data: { current: [update] } }).then(done); - """, - script_args=(version, interventions, shims), - ) - return self.wait_until_webcompat_addon_is_ready() - - def wait_until_webcompat_addon_is_ready(self): - self.marionette.navigate("about:compat") - return self.marionette.execute_async_script( - """ - const bg = window.wrappedJSObject.browser.extension.getBackgroundPage(); - Promise.allSettled([ - bg.interventions.allSettled(), - bg.shims.ready(), - ]).then(() => { - const lastUpdateInfo = {}; - for (const type of ["interventions", "shims"]) { - for (const [key, value] of Object.entries( - bg[type]._lastEnabledInfo - )) { - lastUpdateInfo[key] = [ - lastUpdateInfo[key] ?? [], - value.map(script => script.id), - ].flat(); - } - } - - const interventionContentScriptPath = bg.interventions?.getAvailableInterventions() - .find(i => i.interventions.find(v => v.content_scripts.js)) - ?.interventions.find(v => v.content_scripts.js).content_scripts.js[0]; - - const shimContentScriptPath = bg.shims?.shims.values().find(s => s?.contentScripts.length)?.contentScripts[0].js[0].replace("/shims/", ""); - - return { - lastUpdateInfo, - interventionContentScriptPath, - shimContentScriptPath, - }; - }).then(arguments[0]); - """ - ) - - def verifyBootupContentScripts( - self, infoOnBoot, expectAddedOrPresent, expectNotAddedOrPresent - ): - registeredContentScripts = set( - infoOnBoot["alreadyRegisteredContentScripts"] - + infoOnBoot["newContentScriptsToRegister"] - ) - - self.assertEqual( - len(registeredContentScripts), - len(expectAddedOrPresent), - "only expected content scripts are registered", - ) - - for wantedScript in expectAddedOrPresent: - self.assertTrue( - wantedScript in registeredContentScripts, - f"content script is active: {wantedScript}", - ) - - notRegisteredContentScripts = set(infoOnBoot["oldContentScriptsToUnregister"]) - for unwantedScript in expectNotAddedOrPresent: - self.assertTrue( - (unwantedScript in notRegisteredContentScripts) - or not (unwantedScript in registeredContentScripts), - f"content script is NOT active: {unwantedScript}", - ) - - def buildInterventionConfig(self, id, jsContentScriptPath): - return { - "id": id, - "label": id, - "bugs": {"issue1": {"matches": ["*://example.com/*"]}}, - "interventions": [ - {"platforms": ["all"], "content_scripts": {"js": [jsContentScriptPath]}} - ], - } - - def buildShimConfig(self, id, jsContentScriptPath): - return { - "id": id, - "platform": "all", - "name": id, - "contentScripts": [ - { - "js": jsContentScriptPath, - "matches": ["*://example.com/*"], - "runAt": "document_start", - } - ], - } diff --git a/browser/locales/l10n-changesets.json b/browser/locales/l10n-changesets.json index 83c42adc8d30a..1a5654375014f 100644 --- a/browser/locales/l10n-changesets.json +++ b/browser/locales/l10n-changesets.json @@ -15,7 +15,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "af": { "pin": false, @@ -33,7 +33,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "an": { "pin": false, @@ -51,7 +51,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ar": { "pin": false, @@ -69,7 +69,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ast": { "pin": false, @@ -87,7 +87,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "az": { "pin": false, @@ -105,7 +105,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "be": { "pin": false, @@ -123,7 +123,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bg": { "pin": false, @@ -141,7 +141,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bn": { "pin": false, @@ -159,7 +159,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bo": { "pin": false, @@ -177,7 +177,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "br": { "pin": false, @@ -195,7 +195,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "brx": { "pin": false, @@ -213,7 +213,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bs": { "pin": false, @@ -231,7 +231,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ca": { "pin": false, @@ -249,7 +249,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ca-valencia": { "pin": false, @@ -267,7 +267,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cak": { "pin": false, @@ -285,7 +285,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ckb": { "pin": false, @@ -303,7 +303,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cs": { "pin": false, @@ -321,7 +321,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cy": { "pin": false, @@ -339,7 +339,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "da": { "pin": false, @@ -357,7 +357,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "de": { "pin": false, @@ -375,7 +375,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "dsb": { "pin": false, @@ -393,7 +393,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "el": { "pin": false, @@ -411,7 +411,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "en-CA": { "pin": false, @@ -429,7 +429,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "en-GB": { "pin": false, @@ -447,7 +447,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "eo": { "pin": false, @@ -465,7 +465,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-AR": { "pin": false, @@ -483,7 +483,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-CL": { "pin": false, @@ -501,7 +501,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-ES": { "pin": false, @@ -519,7 +519,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-MX": { "pin": false, @@ -537,7 +537,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "et": { "pin": false, @@ -555,7 +555,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "eu": { "pin": false, @@ -573,7 +573,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fa": { "pin": false, @@ -591,7 +591,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ff": { "pin": false, @@ -609,7 +609,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fi": { "pin": false, @@ -627,7 +627,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fr": { "pin": false, @@ -645,7 +645,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fur": { "pin": false, @@ -663,7 +663,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fy-NL": { "pin": false, @@ -681,7 +681,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ga-IE": { "pin": false, @@ -699,7 +699,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gd": { "pin": false, @@ -717,7 +717,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gl": { "pin": false, @@ -735,7 +735,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gn": { "pin": false, @@ -753,7 +753,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gu-IN": { "pin": false, @@ -771,7 +771,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "he": { "pin": false, @@ -789,7 +789,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hi-IN": { "pin": false, @@ -807,7 +807,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hr": { "pin": false, @@ -825,7 +825,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hsb": { "pin": false, @@ -843,7 +843,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hu": { "pin": false, @@ -861,7 +861,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hy-AM": { "pin": false, @@ -879,7 +879,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hye": { "pin": false, @@ -897,7 +897,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ia": { "pin": false, @@ -915,7 +915,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "id": { "pin": false, @@ -933,7 +933,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "is": { "pin": false, @@ -951,7 +951,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "it": { "pin": false, @@ -969,7 +969,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ja": { "pin": false, @@ -985,7 +985,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ja-JP-mac": { "pin": false, @@ -993,7 +993,7 @@ "macosx64", "macosx64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ka": { "pin": false, @@ -1011,7 +1011,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kab": { "pin": false, @@ -1029,7 +1029,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kk": { "pin": false, @@ -1047,7 +1047,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "km": { "pin": false, @@ -1065,7 +1065,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kn": { "pin": false, @@ -1083,7 +1083,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ko": { "pin": false, @@ -1101,7 +1101,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lij": { "pin": false, @@ -1119,7 +1119,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lo": { "pin": false, @@ -1137,7 +1137,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lt": { "pin": false, @@ -1155,7 +1155,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ltg": { "pin": false, @@ -1173,7 +1173,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lv": { "pin": false, @@ -1191,7 +1191,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "meh": { "pin": false, @@ -1209,7 +1209,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "mk": { "pin": false, @@ -1227,7 +1227,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ml": { "pin": false, @@ -1245,7 +1245,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "mr": { "pin": false, @@ -1263,7 +1263,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ms": { "pin": false, @@ -1281,7 +1281,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "my": { "pin": false, @@ -1299,7 +1299,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nb-NO": { "pin": false, @@ -1317,7 +1317,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ne-NP": { "pin": false, @@ -1335,7 +1335,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nl": { "pin": false, @@ -1353,7 +1353,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nn-NO": { "pin": false, @@ -1371,7 +1371,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "oc": { "pin": false, @@ -1389,7 +1389,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pa-IN": { "pin": false, @@ -1407,7 +1407,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pl": { "pin": false, @@ -1425,7 +1425,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pt-BR": { "pin": false, @@ -1443,7 +1443,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pt-PT": { "pin": false, @@ -1461,7 +1461,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "rm": { "pin": false, @@ -1479,7 +1479,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ro": { "pin": false, @@ -1497,7 +1497,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ru": { "pin": false, @@ -1515,7 +1515,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sat": { "pin": false, @@ -1533,7 +1533,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sc": { "pin": false, @@ -1551,7 +1551,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "scn": { "pin": false, @@ -1569,7 +1569,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sco": { "pin": false, @@ -1587,7 +1587,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "si": { "pin": false, @@ -1605,7 +1605,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sk": { "pin": false, @@ -1623,7 +1623,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "skr": { "pin": false, @@ -1641,7 +1641,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sl": { "pin": false, @@ -1659,7 +1659,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "son": { "pin": false, @@ -1677,7 +1677,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sq": { "pin": false, @@ -1695,7 +1695,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sr": { "pin": false, @@ -1713,7 +1713,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sv-SE": { "pin": false, @@ -1731,7 +1731,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "szl": { "pin": false, @@ -1749,7 +1749,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ta": { "pin": false, @@ -1767,7 +1767,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "te": { "pin": false, @@ -1785,7 +1785,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "tg": { "pin": false, @@ -1803,7 +1803,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "th": { "pin": false, @@ -1821,7 +1821,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "tl": { "pin": false, @@ -1839,7 +1839,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "tr": { "pin": false, @@ -1857,7 +1857,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "trs": { "pin": false, @@ -1875,7 +1875,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "uk": { "pin": false, @@ -1893,7 +1893,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ur": { "pin": false, @@ -1911,7 +1911,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "uz": { "pin": false, @@ -1929,7 +1929,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "vi": { "pin": false, @@ -1947,7 +1947,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "wo": { "pin": false, @@ -1965,7 +1965,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "xh": { "pin": false, @@ -1983,7 +1983,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "zh-CN": { "pin": false, @@ -2001,7 +2001,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "zh-TW": { "pin": false, @@ -2019,6 +2019,6 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" } } \ No newline at end of file diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/browser-shared.css index 058233a7646cd..f454c39e7583d 100644 --- a/browser/themes/shared/browser-shared.css +++ b/browser/themes/shared/browser-shared.css @@ -1447,7 +1447,7 @@ popupnotificationcontent { } } -/* AI window immersive view (hidden address bar and buttons) */ +/* AI window immersive view (hidden nav bar and buttons) */ :root[ai-window][aiwindow-immersive-view] { #nav-bar:not(.browser-titlebar) { height: 0; @@ -1455,12 +1455,28 @@ popupnotificationcontent { visibility: hidden; } + #urlbar-container, + #aiwindow-ask-button, + #nav-bar-customization-target > *:not(#sidebar-button), + #nav-bar-overflow-button, + #PanelUI-button { + visibility: hidden; + } + /* immersive view styles exclusive for first run */ &[aiwindow-first-run] { - .toolbar-items { + #TabsToolbar > .toolbar-items { opacity: 0.2; pointer-events: none; } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.verticalTabs") { + #sidebar-main { + opacity: 0.2; + pointer-events: none; + } + } } } diff --git a/browser/themes/shared/smartbar.css b/browser/themes/shared/smartbar.css index a2f5aa50c2b40..f08d4f08ccec5 100644 --- a/browser/themes/shared/smartbar.css +++ b/browser/themes/shared/smartbar.css @@ -7,9 +7,10 @@ :root { --smartbar-border-style-gradient: var(--border-width) solid; --smartbar-border-color-gradient: linear-gradient(172deg, #321bfd -18%, #cf30e2 53%, #ff9900 89%, #f5c451 109%); + --smartbar-border-color-gradient-dark: linear-gradient(270deg, var(--color-orange-40) 0%, var(--color-violet-50) 105.15%); --smartbar-max-width: calc(var(--size-item-xlarge) * 12); - --smartbar-shadow-color: color-mix(in srgb, var(--color-violet-40) 30%, transparent); - --smartbar-shadow: 0 2px 14px var(--smartbar-shadow-color); + --smartbar-shadow: 0 2px 14px color-mix(in srgb, var(--color-violet-40) 30%, transparent); + --smartbar-background-color: light-dark(white, var(--color-violet-110)); } .smartbar { @@ -26,11 +27,26 @@ .smartbar > .urlbar-background { background-color: white; background-clip: padding-box, border-box; - background-image: linear-gradient(white, white), var(--smartbar-border-color-gradient); + background-image: linear-gradient(var(--smartbar-background-color), var(--smartbar-background-color)), var(--smartbar-border-color-gradient); background-origin: border-box; border-radius: var(--border-radius-large); border: var(--smartbar-border-style-gradient) transparent; + @media (prefers-color-scheme: dark) { + background-image: linear-gradient(var(--smartbar-background-color), var(--smartbar-background-color)), var(--smartbar-border-color-gradient-dark); + + /* creating a gradient box shadow effect for dark mode */ + &::before { + background-image: var(--smartbar-border-color-gradient-dark); + border-radius: inherit; + content: ""; + filter: blur(30px) opacity(0.7); + inset: 0; + position: absolute; + z-index: -1; + } + } + .smartbar[open] > & { /* stylelint-disable-next-line stylelint-plugin-mozilla/use-design-tokens */ box-shadow: var(--smartbar-shadow); diff --git a/build/rust/siphasher/Cargo.toml b/build/rust/siphasher/Cargo.toml deleted file mode 100644 index 7834ef04ac96f..0000000000000 --- a/build/rust/siphasher/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "siphasher" -version = "0.3.999" -edition = "2018" -license = "MPL-2.0" - -[lib] -path = "lib.rs" - -[dependencies.siphasher] -version = "1" diff --git a/build/rust/siphasher/lib.rs b/build/rust/siphasher/lib.rs deleted file mode 100644 index b4bdec7b54143..0000000000000 --- a/build/rust/siphasher/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -pub use siphasher::*; diff --git a/devtools/client/responsive/test/browser/browser.toml b/devtools/client/responsive/test/browser/browser.toml index d557f64f42f2d..4f009f0fc7a5a 100644 --- a/devtools/client/responsive/test/browser/browser.toml +++ b/devtools/client/responsive/test/browser/browser.toml @@ -192,9 +192,7 @@ skip-if = [ ["browser_touch_all_events_long_tap.js"] skip-if = [ "a11y_checks", - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && asan", # Bug 1980013 - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && debug", # Bug 1980013 - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && tsan", # Bug 1980013 + "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11'", # Bug 2012028, Frequently failing on all linux variants "os == 'mac' && os_version == '14.70' && arch == 'x86_64' && debug", # Bug 1980013 "os == 'mac' && os_version == '14.70' && arch == 'x86_64' && verify", # can be too slow in chaos mode to trigger dblclick events "os == 'win'", # Bug 1977557 and Bug 1977558 diff --git a/devtools/client/shared/test/browser_spectrum.js b/devtools/client/shared/test/browser_spectrum.js index 6832b99c84b37..926eb586006b5 100644 --- a/devtools/client/shared/test/browser_spectrum.js +++ b/devtools/client/shared/test/browser_spectrum.js @@ -65,17 +65,9 @@ function extractRgbaOverlayString(linearGradientStr) { return linearGradientStr.substring(start + 1, end + 1); } -function testColorPreviewDisplay( - spectrum, - expectedRgbCssString, - expectedBorderColor -) { +function testColorPreviewDisplay(spectrum, expectedRgbCssString) { const { colorPreview } = spectrum; const colorPreviewStyle = window.getComputedStyle(colorPreview); - expectedBorderColor = - expectedBorderColor === "transparent" - ? "rgba(0, 0, 0, 0)" - : expectedBorderColor; spectrum.updateUI(); @@ -97,7 +89,7 @@ function testColorPreviewDisplay( const borderColorTop = colorPreviewStyle.getPropertyValue("border-top-color"); is( borderColorTop, - expectedBorderColor, + "color(srgb 0.5 0.5 0.5 / 0.5)", "Color preview border color is correct." ); } @@ -322,19 +314,19 @@ async function testChangingColorShouldUpdateColorPreview(container) { const s = await createSpectrum(container, [0, 0, 1, 1]); info("Test that color preview is black."); - testColorPreviewDisplay(s, "rgb(0, 0, 1)", "transparent"); + testColorPreviewDisplay(s, "rgb(0, 0, 1)"); info("Test that color preview is blue."); s.rgb = [0, 0, 255, 1]; - testColorPreviewDisplay(s, "rgb(0, 0, 255)", "transparent"); + testColorPreviewDisplay(s, "rgb(0, 0, 255)"); info("Test that color preview is red."); s.rgb = [255, 0, 0, 1]; - testColorPreviewDisplay(s, "rgb(255, 0, 0)", "transparent"); + testColorPreviewDisplay(s, "rgb(255, 0, 0)"); info("Test that color preview is white and also has a light grey border."); s.rgb = cssColors.white; - testColorPreviewDisplay(s, "rgb(255, 255, 255)", "rgb(204, 204, 204)"); + testColorPreviewDisplay(s, "rgb(255, 255, 255)"); s.destroy(); } diff --git a/docs/rust-components/api/js/viaduct.md b/docs/rust-components/api/js/viaduct.md index 4ebca60151ea0..3d64195c370a6 100644 --- a/docs/rust-components/api/js/viaduct.md +++ b/docs/rust-components/api/js/viaduct.md @@ -63,3 +63,5 @@ ``` ```{js:autofunction} RustViaduct.sys.initBackend ``` +```{js:autofunction} RustViaduct.sys.setGlobalDefaultUserAgent +``` diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 3e3bf3bd2b49b..0d00a3dccb96d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -443,23 +443,17 @@ nsresult nsDocShell::InitWindow(nsIWidget* aParentWidget, int32_t aX, mozilla::dom::WindowGlobalChild* aWindowActor) { SetParentWidget(aParentWidget); SetPositionAndSize(aX, aY, aWidth, aHeight, 0); - if (!Initialize(aOpenWindowInfo, aWindowActor)) { - // Since bug 543435, Initialize can fail and propagating this would - // cause callers to crash when they didn't previously (bug 2003244). - NS_WARNING("Failed to initialize docshell"); - } - - return NS_OK; + return Initialize(aOpenWindowInfo, aWindowActor); } -bool nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo, - mozilla::dom::WindowGlobalChild* aWindowActor) { +nsresult nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo, + mozilla::dom::WindowGlobalChild* aWindowActor) { if (mInitialized) { // We've already been initialized. MOZ_ASSERT(!aOpenWindowInfo, "Tried to reinitialize with override principal"); MOZ_ASSERT(!aWindowActor, "Tried to reinitialize with a window actor"); - return true; + return NS_OK; } MOZ_ASSERT(aOpenWindowInfo, @@ -468,15 +462,14 @@ bool nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo, NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); - NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); + NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_NOT_INITIALIZED); mInitialized = true; mDisableMetaRefreshWhenInactive = Preferences::GetBool("browser.meta_refresh_when_inactive.disabled", mDisableMetaRefreshWhenInactive); - bool succeeded = - NS_SUCCEEDED(CreateInitialDocumentViewer(aOpenWindowInfo, aWindowActor)); + nsresult rv = CreateInitialDocumentViewer(aOpenWindowInfo, aWindowActor); if (nsCOMPtr serv = services::GetObserverService()) { const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE @@ -484,7 +477,7 @@ bool nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo, serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr); } - return succeeded; + return rv; } /* static */ diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 7e828206261ca..7ca79a65b91dc 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -190,8 +190,8 @@ class nsDocShell final : public nsDocLoader, mozilla::dom::BrowsingContext* aBrowsingContext, uint64_t aContentWindowID = 0); - bool Initialize(nsIOpenWindowInfo* aOpenWindowInfo, - mozilla::dom::WindowGlobalChild* aWindowActor); + nsresult Initialize(nsIOpenWindowInfo* aOpenWindowInfo, + mozilla::dom::WindowGlobalChild* aWindowActor); nsresult InitWindow(nsIWidget* aParentWidget, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 175985e9f3ebb..0cba28c9f2ee2 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -2301,7 +2301,7 @@ nsresult nsFrameLoader::MaybeCreateDocShell() { doc->GetPolicyContainer(); openWindowInfo->mCoepToInheritForAboutBlank = doc->GetEmbedderPolicy(); openWindowInfo->mBaseUriToInheritForAboutBlank = mOwnerContent->GetBaseURI(); - if (!docShell->Initialize(openWindowInfo, nullptr)) { + if (NS_FAILED(docShell->Initialize(openWindowInfo, nullptr))) { // Do not call Destroy() here. See bug 472312. NS_WARNING("Something wrong when creating the docshell for a frameloader!"); return NS_ERROR_FAILURE; diff --git a/dom/chrome-webidl/UniFFI.webidl b/dom/chrome-webidl/UniFFI.webidl index 0cd3d0194ba1e..6f3c3080db206 100644 --- a/dom/chrome-webidl/UniFFI.webidl +++ b/dom/chrome-webidl/UniFFI.webidl @@ -124,4 +124,17 @@ namespace UniFFIScaffolding { // This is called at shutdown to clear out the reference to the JS function. [Throws] undefined deregisterCallbackHandler(UniFFICallbackInterfaceId interfaceId); + + // Create a new handle for a callback interface object + // + // This is called by the JS code before lowering a callback interface object. The returned handle + // is used as a key for the JS callback interface map and also returned to Rust. + unsigned long long callbackHandleCreate(); + + // Decrease the reference count for a callback interface handle + // + // The JS code calls this before lifting a callback interface object. It returns the new + // reference count. If `0`, then the map entry for the JS object is removed. + unsigned long callbackHandleRelease(unsigned long long handle); + }; diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 6c3aa4619eb81..709627e4bb60d 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -468,8 +468,9 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent, mPuppetWidget->InfallibleCreate(nullptr, LayoutDeviceIntRect(), widget::InitData()); - mWebBrowser = nsWebBrowser::Create(this, mPuppetWidget, mBrowsingContext, - aInitialWindowChild, aOpenWindowInfo); + MOZ_TRY(nsWebBrowser::Create(this, mPuppetWidget, mBrowsingContext, + aInitialWindowChild, aOpenWindowInfo, + getter_AddRefs(mWebBrowser))); if (!mWebBrowser) { // At least the JS recursion depth check can cause an early return // here. dom/base/crashtests/1419902.html diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 11639e7ec9785..6a02bfa1d07bd 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1890,6 +1890,13 @@ mozilla::ipc::IPCResult ContentChild::RecvConstructBrowser( ? IPC_FAIL(this, "discarded initial top BrowsingContext") : IPC_FAIL(this, "missing initial top BrowsingContext"); } + if (!browsingContext->AncestorsAreCurrent()) { + MOZ_ASSERT(!aIsTopLevel, "Top level discard was already checked"); + MOZ_LOG( + BrowsingContext::GetLog(), LogLevel::Warning, + ("Discarded or inactive ancestor of initial frame BrowsingContext")); + return IPC_OK(); + } if (xpc::IsInAutomation() && StaticPrefs:: @@ -1964,15 +1971,11 @@ mozilla::ipc::IPCResult ContentChild::RecvConstructBrowser( openWindowInfo->mPolicyContainerToInheritForAboutBlank = new PolicyContainer(); - { - // Block the script runner that notifies about the creation of the script - // global for the initial about:blank. - nsAutoScriptBlocker blockScripts; - - if (NS_WARN_IF(NS_FAILED(browserChild->Init( - /* aOpener */ nullptr, windowChild, openWindowInfo)))) { - return IPC_FAIL(browserChild, "BrowserChild::Init failed"); - } + nsresult rv = browserChild->Init( + /* aOpener */ nullptr, windowChild, openWindowInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + IPC_FAIL_UNSAFE_PRINTF(browserChild, "BrowserChild::Init failed (rv=%s)", + mozilla::GetStaticErrorName(rv)); } nsCOMPtr os = services::GetObserverService(); diff --git a/dom/notification/NotificationParent.cpp b/dom/notification/NotificationParent.cpp index ab33e10a2fdaa..d3e0079db8a35 100644 --- a/dom/notification/NotificationParent.cpp +++ b/dom/notification/NotificationParent.cpp @@ -11,9 +11,12 @@ #include "mozilla/AlertNotification.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/dom/ServiceWorkerManager.h" +#include "mozilla/glean/DomNotificationMetrics.h" #include "mozilla/ipc/Endpoint.h" #include "nsComponentManagerUtils.h" #include "nsIServiceWorkerManager.h" +#include "nsIURIClassifier.h" +#include "nsNetCID.h" #include "nsThreadUtils.h" namespace mozilla::dom::notification { @@ -148,6 +151,40 @@ class NotificationObserver final : public nsIObserver { NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver) +using SafeBrowsingPromise = MozPromise; + +class SafeBrowsingClassificationCallback final + : public nsIURIClassifierCallback { + public: + NS_DECL_ISUPPORTS + + SafeBrowsingClassificationCallback() = default; + + already_AddRefed Promise() { + return mPromiseHolder.Ensure(__func__); + } + + NS_IMETHOD OnClassifyComplete(nsresult aErrorCode, const nsACString& aList, + const nsACString& aProvider, + const nsACString& aFullHash) override { + if (NS_FAILED(aErrorCode)) { + mPromiseHolder.Reject(aErrorCode, __func__); + } else { + mPromiseHolder.Resolve(true, __func__); + } + return NS_OK; + } + + private: + ~SafeBrowsingClassificationCallback() { + mPromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__); + } + + MozPromiseHolder mPromiseHolder; +}; + +NS_IMPL_ISUPPORTS(SafeBrowsingClassificationCallback, nsIURIClassifierCallback) + nsresult NotificationParent::HandleAlertTopic(AlertTopic aTopic) { if (aTopic == AlertTopic::Click) { return FireClickEvent(); @@ -238,18 +275,82 @@ mozilla::ipc::IPCResult NotificationParent::RecvShow(Maybe&& aIcon, return IPC_OK(); } - // Step 4.2: Run the fetch steps for notification. (Already happened in the - // child) - // - // Step 4.3: Run the show steps for notification. - nsresult rv = Show(std::move(aIcon)); - // It's possible that we synchronously received a notification while in Show, - // so mResolver may now be empty. - if (NS_FAILED(rv) && mResolver) { - mResolver.take().value()(CopyableErrorResult(rv)); + auto showNotification = [self = RefPtr(this)](Maybe&& aIcon) { + // Step 4.2: Run the fetch steps for notification. (Already happened in the + // child) + // + // Step 4.3: Run the show steps for notification. + nsresult rv = self->Show(std::move(aIcon)); + // It's possible that we synchronously received a notification while in + // Show, so mResolver may now be empty. + if (NS_FAILED(rv) && self->mResolver) { + self->mResolver.take().value()(CopyableErrorResult(rv)); + } + // If not failed, the resolver will be called asynchronously by + // NotificationObserver. + }; + + // Check Safe Browsing blocklist if the feature is enabled (bug 1986300). + if (StaticPrefs::dom_webnotifications_block_if_on_safebrowsing()) { + nsresult rv = NS_OK; + nsCOMPtr uriClassifier = + do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv) || !uriClassifier) { + NS_WARNING("URI classifier unavailable for notification check"); + } else { + RefPtr callback = + new SafeBrowsingClassificationCallback(); + RefPtr promise = callback->Promise(); + + bool willClassify = false; + rv = uriClassifier->Classify(mArgs.mPrincipal, callback, &willClassify); + + if (NS_SUCCEEDED(rv) && willClassify) { + glean::web_notification::show_safe_browsing_block.AddToDenominator(1); + + mShowPending = true; + promise->Then( + GetMainThreadSerialEventTarget(), __func__, + [self = RefPtr(this), showNotification, + icon = std::move(aIcon)](bool) mutable { + self->mShowPending = false; + + // Always show first to register with the alert system, even if + // close was requested while pending. This ensures platforms like + // Android can properly trigger onCloseNotification callbacks. + showNotification(std::move(icon)); + + // Handle close() called while SafeBrowsing check was in progress. + if (self->mClosePending) { + self->mClosePending = false; + self->Unregister(); + self->Close(); + } + }, + [self = RefPtr(this)](nsresult) { + // SafeBrowsing classification determined the notification is + // unsafe, reject the show request and revoke permission. + self->mShowPending = false; + self->mClosePending = false; + + glean::web_notification::show_safe_browsing_block.AddToNumerator( + 1); + RemovePermission(self->mArgs.mPrincipal); + + CopyableErrorResult rv; + rv.ThrowTypeError("Permission to show Notification denied."); + self->mResolver.take().value()(rv); + + self->mDangling = true; + }); + + return IPC_OK(); + } + } } - // If not failed, the resolver will be called asynchronously by - // NotificationObserver + + showNotification(std::move(aIcon)); return IPC_OK(); } @@ -317,6 +418,15 @@ nsresult NotificationParent::Show(Maybe&& aIcon) { } mozilla::ipc::IPCResult NotificationParent::RecvClose() { + // If SafeBrowsing check is in progress, defer the close until it completes. + // We need to call Show() first to register with the alert system before + // Unregister() can properly trigger close callbacks (e.g., + // onCloseNotification on Android). + if (mShowPending) { + mClosePending = true; + return IPC_OK(); + } + Unregister(); Close(); return IPC_OK(); diff --git a/dom/notification/NotificationParent.h b/dom/notification/NotificationParent.h index c6559a0b065ca..255f65b37c0e1 100644 --- a/dom/notification/NotificationParent.h +++ b/dom/notification/NotificationParent.h @@ -72,6 +72,12 @@ class NotificationParent final : public PNotificationParent, // either because it's closed or denied permission. We don't have to call // CloseAlert if this is the case. bool mDangling = false; + + // State tracking for async SafeBrowsing checks (bug 1986300). + // When a SafeBrowsing classification is in progress, we track whether a + // close was requested before the check completes. + bool mShowPending = false; + bool mClosePending = false; }; } // namespace mozilla::dom::notification diff --git a/dom/notification/metrics.yaml b/dom/notification/metrics.yaml index 6a8b06e051854..c3dc34d6129c5 100644 --- a/dom/notification/metrics.yaml +++ b/dom/notification/metrics.yaml @@ -67,3 +67,18 @@ web_notification: notification_emails: - krosylight@mozilla.com expires: never + show_safe_browsing_block: + type: rate + description: > + The rate of notification show attempts that are blocked because the + origin is on the Safe Browsing blocklist (phishing, malware, etc.). + The numerator is the number of blocked attempts, and the denominator + is the total number of show attempts that passed the initial + permission check. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1986300 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1986300 + notification_emails: + - emz@mozilla.com + expires: 155 diff --git a/dom/notification/moz.build b/dom/notification/moz.build index 01571c082adee..c44918a8e446f 100644 --- a/dom/notification/moz.build +++ b/dom/notification/moz.build @@ -55,6 +55,8 @@ LOCAL_INCLUDES += [ "/dom/ipc", # NotificationHandler::RespondOnClick, but see bug 1966910. "/js/xpconnect/src", + # Safe Browsing classification for notifications (bug 1986300). + "/netwerk/url-classifier", ] BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"] diff --git a/dom/notification/test/browser/browser.toml b/dom/notification/test/browser/browser.toml index 42a38a428e502..f775f79d96a92 100644 --- a/dom/notification/test/browser/browser.toml +++ b/dom/notification/test/browser/browser.toml @@ -10,6 +10,12 @@ support-files = [ ["browser_permission_dismiss.js"] support-files = ["notification.html"] +["browser_safebrowsing_block.js"] +support-files = [ + "file_safebrowsing_test.html", + "file_safebrowsing_test.serviceworker.js", +] + ["browser_userContextId_openWindow.js"] support-files = [ "empty.html", diff --git a/dom/notification/test/browser/browser_safebrowsing_block.js b/dom/notification/test/browser/browser_safebrowsing_block.js new file mode 100644 index 0000000000000..1d3506d5353db --- /dev/null +++ b/dom/notification/test/browser/browser_safebrowsing_block.js @@ -0,0 +1,249 @@ +"use strict"; + +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +const { UrlClassifierTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/UrlClassifierTestUtils.sys.mjs" +); + +const PHISH_TABLE = "moztest-phish-simple"; +const DYNAMIC_PHISH_TABLE = "mochitest-phish-simple"; +const PHISH_URL = "https://www.itisatrap.org/firefox/its-a-trap.html"; +const NORMAL_URL = + "https://example.com/browser/dom/notification/test/browser/file_safebrowsing_test.html"; +const PERMISSION_NAME = "desktop-notification"; + +async function getTelemetryRate() { + await Services.fog.testFlushAllChildren(); + return ( + Glean.webNotification.showSafeBrowsingBlock.testGetValue() ?? { + numerator: 0, + denominator: 0, + } + ); +} + +function waitForDBInit() { + let principal = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(PHISH_URL), + {} + ); + let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService( + Ci.nsIUrlClassifierDBService + ); + + return BrowserTestUtils.waitForCondition( + () => + new Promise(resolve => { + dbService.lookup(principal, PHISH_TABLE, value => { + resolve(value === PHISH_TABLE); + }); + }), + "DB lookup confirmed phishing URL is in table" + ); +} + +function addHostToPhishingTable(host) { + let entry = host + "/"; + let updateData = `n:1000\ni:${DYNAMIC_PHISH_TABLE}\nad:1\na:1:32:${entry.length}\n${entry}\n`; + + return UrlClassifierTestUtils.useTestDatabase({ + pref: "urlclassifier.phishTable", + name: DYNAMIC_PHISH_TABLE, + update: updateData, + }); +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["urlclassifier.phishTable", PHISH_TABLE], + ["browser.safebrowsing.phishing.enabled", true], + ["dom.webnotifications.block_if_on_safebrowsing", true], + ], + }); + + SafeBrowsing.init(); + await waitForDBInit(); +}); + +// Test 1: Normal site registers SW, sends notification - should succeed. +// This is a control test ensuring notifications work for non-phishing sites. +// Also verifies telemetry: denominator increments, numerator does not. +add_task(async function test_sw_notification_allowed_for_normal_site() { + Services.fog.testResetFOG(); + + let normalPrincipal = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI("https://example.com"), + {} + ); + + PermissionTestUtils.add( + normalPrincipal, + PERMISSION_NAME, + Services.perms.ALLOW_ACTION + ); + + // Step 1 & 2: Visit page and register service worker + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: NORMAL_URL, + }, + async browser => { + let result = await SpecialPowers.spawn(browser, [], async () => { + await content.navigator.serviceWorker.register( + "file_safebrowsing_test.serviceworker.js" + ); + const reg = await content.navigator.serviceWorker.ready; + + return new Promise(resolve => { + content.navigator.serviceWorker.onmessage = event => { + resolve(event.data); + }; + reg.active.postMessage("show-notification"); + }); + }); + + ok(result.success, "Service worker notification should succeed"); + } + ); + + // Verify permission is still intact + is( + PermissionTestUtils.testPermission(normalPrincipal, PERMISSION_NAME), + Services.perms.ALLOW_ACTION, + "Permission should still be granted for normal site" + ); + + // Verify telemetry: denominator should be 1, numerator should be 0 + let rate = await getTelemetryRate(); + is(rate.denominator, 1, "Denominator should be 1 (one notification checked)"); + is(rate.numerator, 0, "Numerator should be 0 (no notifications blocked)"); + + // Cleanup + await SpecialPowers.removeAllServiceWorkerData(); + PermissionTestUtils.remove(normalPrincipal, PERMISSION_NAME); +}); + +// Test 2: Site registers SW when clean, then becomes phishing, then tries to +// send notification via SW - should be blocked and permission revoked. +// This is the key scenario we're protecting against. +// Also verifies telemetry: both numerator and denominator increment. +add_task( + async function test_sw_notification_blocked_when_site_becomes_phishing() { + Services.fog.testResetFOG(); + + let testOrigin = "https://example.com"; + let testPrincipal = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(testOrigin), + {} + ); + + // Grant notification permission (simulating user granted permission when + // site was clean) + PermissionTestUtils.add( + testPrincipal, + PERMISSION_NAME, + Services.perms.ALLOW_ACTION + ); + + // Step 1 & 2: Visit clean page and register service worker + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: NORMAL_URL, + }, + async browser => { + await SpecialPowers.spawn(browser, [], async () => { + await content.navigator.serviceWorker.register( + "file_safebrowsing_test.serviceworker.js" + ); + await content.navigator.serviceWorker.ready; + }); + } + ); + // Step 3: Tab is now closed (withNewTab closes it) + + // Step 4: Site becomes phishing - add example.com to phishing table + info("Adding example.com to phishing table"); + await addHostToPhishingTable("example.com"); + + // Verify the URL is now in the phishing table + let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService( + Ci.nsIUrlClassifierDBService + ); + let lookupResult = await new Promise(resolve => { + dbService.lookup(testPrincipal, DYNAMIC_PHISH_TABLE, value => { + resolve(value); + }); + }); + is( + lookupResult, + DYNAMIC_PHISH_TABLE, + "example.com should now be in phishing table" + ); + + // Verify permission is still there before we try the notification + is( + PermissionTestUtils.testPermission(testPrincipal, PERMISSION_NAME), + Services.perms.ALLOW_ACTION, + "Permission should still be granted before notification attempt" + ); + + // Step 5: SW tries to send notification (no tabs open). + // We need to trigger the SW to send a notification. We can do this by + // opening a new tab to the same origin and messaging the SW. + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: NORMAL_URL, + }, + async browser => { + let result = await SpecialPowers.spawn(browser, [], async () => { + const reg = await content.navigator.serviceWorker.ready; + + return new Promise(resolve => { + content.navigator.serviceWorker.onmessage = event => { + resolve(event.data); + }; + reg.active.postMessage("show-notification"); + }); + }); + + ok( + !result.success, + "Service worker notification should fail for phishing site" + ); + is( + result.error, + "Permission to show Notification denied.", + "Error message should be generic permission denial" + ); + } + ); + + // Verify permission was revoked + is( + PermissionTestUtils.testPermission(testPrincipal, PERMISSION_NAME), + Services.perms.UNKNOWN_ACTION, + "Permission should be revoked after Safe Browsing block" + ); + + // Verify telemetry: both numerator and denominator should be 1 + let rate = await getTelemetryRate(); + is( + rate.denominator, + 1, + "Denominator should be 1 (one notification checked)" + ); + is(rate.numerator, 1, "Numerator should be 1 (one notification blocked)"); + + // Cleanup + await SpecialPowers.removeAllServiceWorkerData(); + PermissionTestUtils.remove(testPrincipal, PERMISSION_NAME); + Services.prefs.setCharPref("urlclassifier.phishTable", PHISH_TABLE); + } +); diff --git a/dom/notification/test/browser/file_safebrowsing_test.html b/dom/notification/test/browser/file_safebrowsing_test.html new file mode 100644 index 0000000000000..dcea5d4cb973a --- /dev/null +++ b/dom/notification/test/browser/file_safebrowsing_test.html @@ -0,0 +1,10 @@ + + + + + Safe Browsing Notification Test + + +

Safe Browsing Notification Test Page

+ + diff --git a/dom/notification/test/browser/file_safebrowsing_test.serviceworker.js b/dom/notification/test/browser/file_safebrowsing_test.serviceworker.js new file mode 100644 index 0000000000000..7e19bc7c3a15e --- /dev/null +++ b/dom/notification/test/browser/file_safebrowsing_test.serviceworker.js @@ -0,0 +1,9 @@ +// Service worker that shows a notification when receiving a message +self.addEventListener("message", async event => { + try { + await self.registration.showNotification("Test notification from SW"); + event.source.postMessage({ success: true }); + } catch (e) { + event.source.postMessage({ success: false, error: e.message }); + } +}); diff --git a/dom/webauthn/WebAuthnResult.h b/dom/webauthn/WebAuthnResult.h index c7b54ad936ce0..1f67398bf99f4 100644 --- a/dom/webauthn/WebAuthnResult.h +++ b/dom/webauthn/WebAuthnResult.h @@ -86,6 +86,30 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult { mCredPropsRk = Some(java::sdk::Boolean::Ref::From(aResponse->CredProps()) ->BooleanValue()); } + if (aResponse->PrfEnabled()) { + mPrfSupported = + Some(java::sdk::Boolean::Ref::From(aResponse->PrfEnabled()) + ->BooleanValue()); + } + if (aResponse->PrfFirst() && aResponse->PrfFirst()->Length() > 0) { + mPrfFirst.emplace(); + mPrfFirst->AppendElements( + reinterpret_cast( + aResponse->PrfFirst()->GetElements().Elements()), + aResponse->PrfFirst()->Length()); + } + if (aResponse->PrfSecond() && aResponse->PrfSecond()->Length() > 0) { + mPrfSecond.emplace(); + mPrfSecond->AppendElements( + reinterpret_cast( + aResponse->PrfSecond()->GetElements().Elements()), + aResponse->PrfSecond()->Length()); + } + if (aResponse->LargeBlobSupported()) { + mLargeBlobSupported = + Some(java::sdk::Boolean::Ref::From(aResponse->LargeBlobSupported()) + ->BooleanValue()); + } } #endif @@ -275,6 +299,33 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult { aResponse->UserHandle()->Length()); mAuthenticatorAttachment = Some(aResponse->AuthenticatorAttachment()->ToString()); + if (aResponse->PrfFirst() && aResponse->PrfFirst()->Length() > 0) { + mPrfFirst.emplace(); + mPrfFirst->AppendElements( + reinterpret_cast( + aResponse->PrfFirst()->GetElements().Elements()), + aResponse->PrfFirst()->Length()); + } + if (aResponse->PrfSecond() && aResponse->PrfSecond()->Length() > 0) { + mPrfSecond.emplace(); + mPrfSecond->AppendElements( + reinterpret_cast( + aResponse->PrfSecond()->GetElements().Elements()), + aResponse->PrfSecond()->Length()); + } + if (aResponse->LargeBlobBlob() && + aResponse->LargeBlobBlob()->Length() > 0) { + mLargeBlobValue.emplace(); + mLargeBlobValue->AppendElements( + reinterpret_cast( + aResponse->LargeBlobBlob()->GetElements().Elements()), + aResponse->LargeBlobBlob()->Length()); + } + if (aResponse->LargeBlobWritten()) { + mLargeBlobWritten = + Some(java::sdk::Boolean::Ref::From(aResponse->LargeBlobWritten()) + ->BooleanValue()); + } } #endif diff --git a/gfx/wr/Cargo.lock b/gfx/wr/Cargo.lock index 91ccc4ac129bf..bdef5c87f3b78 100644 --- a/gfx/wr/Cargo.lock +++ b/gfx/wr/Cargo.lock @@ -125,9 +125,9 @@ dependencies = [ [[package]] name = "askama" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ "askama_derive", "itoa", @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ "askama_parser", "basic-toml", @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "askama_parser" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" dependencies = [ "memchr", "serde", @@ -1232,9 +1232,9 @@ dependencies = [ [[package]] name = "glean" -version = "66.3.0" +version = "67.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cff88cbb3ec5ecd45a8ba642772a5fb88cc61350b6960d72a7452de138d9e2" +checksum = "8b43d851e3966fafee2933c2891e6b7f7f346866bab7835cb0410700336f424b" dependencies = [ "crossbeam-channel", "glean-core", @@ -1247,9 +1247,9 @@ dependencies = [ [[package]] name = "glean-core" -version = "66.3.0" +version = "67.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34409d1d4bc10da8ddcb584a0a1825d8ad6a5a268bf711320da95c615c30245" +checksum = "b8146e9829a5fe15343fd3c42d8d41e478b02f68b8359346273cc56d7f7feff8" dependencies = [ "android_logger", "bincode", @@ -1722,6 +1722,7 @@ checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.4", + "serde", ] [[package]] @@ -2479,9 +2480,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", "rand 0.8.5", @@ -2489,9 +2490,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] @@ -2558,12 +2559,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "precomputed-hash" version = "0.1.1" @@ -2577,7 +2572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror 1.0.38", - "toml", + "toml 0.5.9", ] [[package]] @@ -2629,18 +2624,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", "rand_core 0.6.4", ] @@ -2664,9 +2647,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.6", -] [[package]] name = "raw-window-handle" @@ -2964,6 +2944,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + [[package]] name = "servo-fontconfig" version = "0.5.1" @@ -3014,9 +3003,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "siphasher" -version = "0.3.10" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" @@ -3101,12 +3090,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot 0.12.1", "phf_shared", "precomputed-hash", @@ -3115,9 +3103,9 @@ dependencies = [ [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ "phf_generator", "phf_shared", @@ -3324,6 +3312,45 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201" +dependencies = [ + "indexmap 2.10.0", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "topological-sort" version = "0.1.0" @@ -3396,9 +3423,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "uniffi" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6d968cb62160c11f2573e6be724ef8b1b18a277aededd17033f8a912d73e2b4" +checksum = "b8c6dec3fc6645f71a16a3fa9ff57991028153bd194ca97f4b55e610c73ce66a" dependencies = [ "anyhow", "uniffi_build", @@ -3409,9 +3436,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b39ef1acbe1467d5d210f274fae344cb6f8766339330cb4c9688752899bf6b" +checksum = "4ed0150801958d4825da56a41c71f000a457ac3a4613fa9647df78ac4b6b6881" dependencies = [ "anyhow", "askama", @@ -3425,7 +3452,7 @@ dependencies = [ "serde", "tempfile", "textwrap 0.16.1", - "toml", + "toml 0.9.6", "uniffi_internal_macros", "uniffi_meta", "uniffi_pipeline", @@ -3434,9 +3461,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6683e6b665423cddeacd89a3f97312cf400b2fb245a26f197adaf65c45d505b2" +checksum = "b78fd9271a4c2e85bd2c266c5a9ede1fac676eb39fd77f636c27eaf67426fd5f" dependencies = [ "anyhow", "camino", @@ -3445,9 +3472,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d990b553d6b9a7ee9c3ae71134674739913d52350b56152b0e613595bb5a6f" +checksum = "b0ef62e69762fbb9386dcb6c87cd3dd05d525fa8a3a579a290892e60ddbda47e" dependencies = [ "anyhow", "bytes", @@ -3457,9 +3484,9 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f4f224becf14885c10e6e400b95cc4d1985738140cb194ccc2044563f8a56b" +checksum = "98f51ebca0d9a4b2aa6c644d5ede45c56f73906b96403c08a1985e75ccb64a01" dependencies = [ "anyhow", "indexmap 2.10.0", @@ -3470,9 +3497,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b481d385af334871d70904e6a5f129be7cd38c18fcf8dd8fd1f646b426a56d58" +checksum = "db9d12529f1223d014fd501e5f29ca0884d15d6ed5ddddd9f506e55350327dc3" dependencies = [ "camino", "fs-err", @@ -3481,15 +3508,15 @@ dependencies = [ "quote", "serde", "syn 2.0.100", - "toml", + "toml 0.9.6", "uniffi_meta", ] [[package]] name = "uniffi_meta" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f817868a3b171bb7bf259e882138d104deafde65684689b4694c846d322491" +checksum = "9df6d413db2827c68588f8149d30d49b71d540d46539e435b23a7f7dbd4d4f86" dependencies = [ "anyhow", "siphasher", @@ -3499,9 +3526,9 @@ dependencies = [ [[package]] name = "uniffi_pipeline" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b147e133ad7824e32426b90bc41fda584363563f2ba747f590eca1fd6fd14e6" +checksum = "a806dddc8208f22efd7e95a5cdf88ed43d0f3271e8f63b47e757a8bbdb43b63a" dependencies = [ "anyhow", "heck", @@ -3512,9 +3539,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.29.4" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caed654fb73da5abbc7a7e9c741532284532ba4762d6fe5071372df22a41730a" +checksum = "0d1a7339539bf6f6fa3e9b534dece13f778bda2d54b1a6d4e40b4d6090ac26e7" dependencies = [ "anyhow", "textwrap 0.16.1", diff --git a/gfx/wr/Cargo.toml b/gfx/wr/Cargo.toml index 79efc5fa19e38..9bec3a9cab5c9 100644 --- a/gfx/wr/Cargo.toml +++ b/gfx/wr/Cargo.toml @@ -10,7 +10,7 @@ resolver = "2" [workspace.dependencies] gleam = "0.15.1" -glean = "=66.3.0" +glean = "=67.0.0" [profile.release] debug = true diff --git a/gfx/wr/servo-tidy.toml b/gfx/wr/servo-tidy.toml index 721bcb3418cb3..b50a67e21d254 100644 --- a/gfx/wr/servo-tidy.toml +++ b/gfx/wr/servo-tidy.toml @@ -62,6 +62,9 @@ packages = [ "miniz_oxide", "mio", "socket2", + # 0.9 required by glean/uniffi + # 0.5 by proc-macro-crate + "toml", "windows-link", ] diff --git a/gfx/wr/webrender/src/quad.rs b/gfx/wr/webrender/src/quad.rs index a571033e71ea5..190e58c1064cf 100644 --- a/gfx/wr/webrender/src/quad.rs +++ b/gfx/wr/webrender/src/quad.rs @@ -410,19 +410,15 @@ fn prepare_quad_impl( } let surface = &mut frame_state.surfaces[pic_context.surface_index.0]; - let clipped_local_rect = clip_chain.pic_coverage_rect.intersection_unchecked(&surface.clipping_rect); - - let mut clipped_raster_rect = clip_chain.pic_coverage_rect.cast_unit(); - if surface.raster_spatial_node_index != surface.surface_spatial_node_index { - let pic_to_raster = SpaceMapper::new_with_target( - surface.raster_spatial_node_index, - surface.surface_spatial_node_index, - RasterRect::max_rect(), - ctx.spatial_tree, - ); + let clipped_pic_rect = clip_chain.pic_coverage_rect.intersection_unchecked(&surface.clipping_rect); - clipped_raster_rect = pic_to_raster.map(&clipped_local_rect).unwrap(); - } + let pic_to_raster = SpaceMapper::new_with_target( + surface.raster_spatial_node_index, + surface.surface_spatial_node_index, + RasterRect::max_rect(), + ctx.spatial_tree, + ); + let Some(clipped_raster_rect) = pic_to_raster.map(&clipped_pic_rect) else { return; }; // TODO: we are making the assumption that raster space and world space have the same // scale. I think that it is the case, but it's not super clean. @@ -1494,7 +1490,7 @@ pub fn prepare_clip_task( QuadFlags::empty() }; - + rg_builder.push_sub_task( sub_tasks, SubTask::RectangleClip(RectangleClipSubTask { diff --git a/gfx/wr/wrench/reftests/clip/reftest.list b/gfx/wr/wrench/reftests/clip/reftest.list index 3410425635381..4dbcce97bccdd 100644 --- a/gfx/wr/wrench/reftests/clip/reftest.list +++ b/gfx/wr/wrench/reftests/clip/reftest.list @@ -1,7 +1,7 @@ platform(linux,mac) == border-with-rounded-clip.yaml border-with-rounded-clip.png fuzzy-if(platform(swgl),1,4) == clip-mode.yaml clip-mode.png fuzzy-if(platform(swgl),1,80) == clip-ellipse.yaml clip-ellipse.png -platform(linux,mac) == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png +fuzzy(1,1000) platform(linux,mac) == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png == clip-3d-transform.yaml clip-3d-transform-ref.yaml fuzzy(1,4) == clip-corner-overlap.yaml clip-corner-overlap-ref.yaml == custom-clip-chain-node-ancestors.yaml custom-clip-chain-node-ancestors-ref.yaml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eceb6f03f9cc6..5ec0346b540d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,7 +52,7 @@ work = "2.11.0" accompanist = "0.37.3" firebase-messaging = "25.0.1" installreferrer = "2.2" -ksp = "2.3.4" +ksp = "2.3.5" material = "1.13.0" osslicenses-plugin = "0.10.10" play-review = "2.0.2" @@ -78,7 +78,7 @@ ktlint = "1.8.0" spotless = "8.2.0" # Mozilla -glean = "66.3.0" +glean = "67.0.0" # Testing libraries androidx-test = "1.7.0" diff --git a/image/rust/jxl/Cargo.toml b/image/rust/jxl/Cargo.toml index 530ea70c5d492..9c6d082480717 100644 --- a/image/rust/jxl/Cargo.toml +++ b/image/rust/jxl/Cargo.toml @@ -10,5 +10,5 @@ edition = "2021" license = "MPL-2.0" [dependencies] -jxl = "0.3" +jxl = { version = "0.3", features = ["all-simd"] } log = "0.4" diff --git a/image/test/gtest/TestDecoders.cpp b/image/test/gtest/TestDecoders.cpp index 4b1f6cf2b6ccd..da6ffa6dd7967 100644 --- a/image/test/gtest/TestDecoders.cpp +++ b/image/test/gtest/TestDecoders.cpp @@ -747,6 +747,7 @@ IMAGE_GTEST_DECODER_BASE_F(BMP) IMAGE_GTEST_DECODER_BASE_F(ICO) IMAGE_GTEST_DECODER_BASE_F(Icon) IMAGE_GTEST_DECODER_BASE_F(WebP) +IMAGE_GTEST_DECODER_BASE_F(AVIF) #ifdef MOZ_JXL IMAGE_GTEST_DECODER_BASE_F(JXL) #endif @@ -771,10 +772,6 @@ TEST_F(ImageDecoders, WebPTransparentNoAlphaHeaderSingleChunk) { CheckDecoderSingleChunk(TransparentNoAlphaHeaderWebPTestCase()); } -TEST_F(ImageDecoders, AVIFSingleChunk) { - CheckDecoderSingleChunk(GreenAVIFTestCase()); -} - TEST_F(ImageDecoders, AVIFSingleChunkNonzeroReserved) { CheckDecoderSingleChunk(NonzeroReservedAVIFTestCase()); } @@ -926,22 +923,10 @@ TEST_F(ImageDecoders, AVIFStackCheck) { CheckDecoderSingleChunk(StackCheckAVIFTestCase(), /* aUseDecodePool */ true); } -TEST_F(ImageDecoders, AVIFDelayedChunk) { - CheckDecoderDelayedChunk(GreenAVIFTestCase()); -} - -TEST_F(ImageDecoders, AVIFMultiChunk) { - CheckDecoderMultiChunk(GreenAVIFTestCase()); -} - TEST_F(ImageDecoders, AVIFLargeMultiChunk) { CheckDecoderMultiChunk(LargeAVIFTestCase(), /* aChunkSize */ 64); } -TEST_F(ImageDecoders, AVIFDownscaleDuringDecode) { - CheckDownscaleDuringDecode(DownscaledAVIFTestCase()); -} - #ifdef MOZ_JXL TEST_F(ImageDecoders, JXLLargeMultiChunk) { CheckDecoderMultiChunk(LargeJXLTestCase(), /* aChunkSize */ 64); diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index dc836f8bd43cc..19f1ed50fe291 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -714,142 +714,9 @@ skip script test262/language/expressions/class/decorator/syntax/class-valid/deco skip script test262/staging/explicit-resource-management/call-dispose-methods.js skip script test262/staging/explicit-resource-management/await-using-in-switch-case-block.js -# https://github.com/tc39/test262/pull/4623 -skip script test262/built-ins/ThrowTypeError/distinct-cross-realm.js - -# https://github.com/tc39/test262/issues/4639 -skip script test262/intl402/Intl/supportedValuesOf/calendars-required-by-intl-era-monthcode.js - -# https://github.com/tc39/test262/pull/4649 -skip-if(!xulRuntime.shell) script test262/built-ins/Math/sumPrecise/throws-on-non-number.js - -# Not yet updated for Unicode 17. +# https://github.com/tc39/test262/pull/4898 skip script test262/staging/sm/String/string-upper-lower-mapping.js -# Not yet updated for Unicode 17. -# https://github.com/tc39/test262/pull/4572 -# https://github.com/tc39/test262/pull/4573 -skip script test262/built-ins/RegExp/property-escapes/generated/Cased.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Tangut.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Inherited.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Inherited.js -skip script test262/built-ins/RegExp/property-escapes/generated/Extender.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Latin.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_Casefolded.js -skip script test262/built-ins/RegExp/property-escapes/generated/Alphabetic.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Syriac.js -skip script test262/built-ins/RegExp/property-escapes/generated/XID_Start.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Arabic.js -skip script test262/built-ins/RegExp/property-escapes/generated/Unified_Ideograph.js -skip script test262/built-ins/RegExp/property-escapes/generated/XID_Continue.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/Math.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Han.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Cased_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Math_Symbol.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Sharada.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_Casemapped.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Tirhuta.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Tifinagh.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_Titlecased.js -skip script test262/built-ins/RegExp/property-escapes/generated/Lowercase.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Newa.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Tangut.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Nonspacing_Mark.js -skip script test262/built-ins/RegExp/property-escapes/generated/Ideographic.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_Lowercased.js -skip script test262/built-ins/RegExp/property-escapes/generated/Emoji.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Kannada.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Modifier_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Number.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Other.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Nandinagari.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Latin.js -skip script test262/built-ins/RegExp/property-escapes/generated/Grapheme_Extend.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Uppercase_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Kannada.js -skip script test262/built-ins/RegExp/property-escapes/generated/Case_Ignorable.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Spacing_Mark.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_Uppercased.js -skip script test262/built-ins/RegExp/property-escapes/generated/Uppercase.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Other_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Telugu.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Han.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Telugu.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Common.js -skip script test262/built-ins/RegExp/property-escapes/generated/Extended_Pictographic.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Decimal_Number.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Mark.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Symbol.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Punctuation.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Common.js -skip script test262/built-ins/RegExp/property-escapes/generated/ID_Continue.js -skip script test262/built-ins/RegExp/property-escapes/generated/ID_Start.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Unassigned.js -skip script test262/built-ins/RegExp/property-escapes/generated/Grapheme_Base.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Currency_Symbol.js -skip script test262/built-ins/RegExp/property-escapes/generated/Assigned.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Other_Symbol.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_Extensions_-_Sharada.js -skip script test262/built-ins/RegExp/property-escapes/generated/Emoji_Presentation.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Lowercase_Letter.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Letter_Number.js -skip script test262/built-ins/RegExp/property-escapes/generated/Changes_When_NFKC_Casefolded.js -skip script test262/built-ins/RegExp/property-escapes/generated/Diacritic.js -skip script test262/built-ins/RegExp/property-escapes/generated/General_Category_-_Other_Punctuation.js -skip script test262/built-ins/RegExp/property-escapes/generated/Script_-_Arabic.js - -# https://github.com/tc39/test262/pull/4659 -skip script test262/staging/Intl402/Temporal/old/indian-calendar.js - -# "Intl era and monthCode" proposal should clarify if "islamic" and "islamic-rgsa" are still allowed -# for Intl.DisplayNames. -skip script test262/intl402/Intl/supportedValuesOf/calendars-accepted-by-DisplayNames.js - -# https://github.com/tc39/test262/pull/4721 -skip script test262/staging/Intl402/Temporal/old/non-iso-calendars-ethioaa.js -skip script test262/staging/Intl402/Temporal/old/non-iso-calendars-ethiopic.js -skip script test262/staging/Intl402/Temporal/old/non-iso-calendars-indian.js -skip script test262/staging/Intl402/Temporal/old/non-iso-calendars-coptic.js - -# https://github.com/tc39/proposal-intl-era-monthcode/issues/96 -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-chinese.js - -# https://github.com/tc39/test262/pull/4676 -skip script test262/intl402/DateTimeFormat/prototype/formatToParts/era.js - -# https://github.com/tc39/test262/pull/4778 -skip script test262/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js - -# https://github.com/tc39/test262/pull/4775 -skip script test262/intl402/DateTimeFormat/prototype/formatToParts/chinese-calendar-dates.js -skip script test262/intl402/DateTimeFormat/prototype/formatToParts/dangi-calendar-dates.js -skip script test262/intl402/DateTimeFormat/prototype/formatRangeToParts/chinese-calendar-dates.js -skip script test262/intl402/DateTimeFormat/prototype/formatRangeToParts/dangi-calendar-dates.js - ############################################## # Enable Iterator Helpers tests in the shell # @@ -1037,3 +904,93 @@ skip script test262/built-ins/Temporal/ZonedDateTime/from/argument-string-limits skip script test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-limits.js skip script test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-limits.js skip script test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-limits.js + +# https://github.com/tc39/proposal-temporal/pull/3253 +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js +skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js + +# https://github.com/tc39/proposal-intl-era-monthcode/pull/102 +skip script test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js +skip script test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js +skip script test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js +skip script test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js + +# https://github.com/tc39/proposal-intl-era-monthcode/issues/96 +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-hebrew.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-chinese.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-dangi.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-hebrew.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-chinese.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-dangi.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-chinese.js + +# https://github.com/tc39/proposal-intl-era-monthcode/pull/101 +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js +skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-japanese.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-roc.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-tbla.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethioaa.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-civil.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-gregory.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-coptic.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethiopic.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-persian.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-indian.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-buddhist.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-japanese.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-roc.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-tbla.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethioaa.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-civil.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-coptic.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-gregory.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethiopic.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-dangi.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-persian.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-indian.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-hebrew.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-chinese.js +skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-buddhist.js diff --git a/js/src/tests/test262/GIT-INFO b/js/src/tests/test262/GIT-INFO index 7607486ff76f8..619b8056c49fd 100644 --- a/js/src/tests/test262/GIT-INFO +++ b/js/src/tests/test262/GIT-INFO @@ -1,9 +1,13 @@ -commit 4869a68f71f9ea4c1b50ac7b1283e1fc81525581 -Author: Frank Yung-Fong Tang -Date: Wed Dec 10 08:37:46 2025 -0800 +commit 677ee2634952e762ca57ffcb8fffee91692c7517 +Author: André Bargull +Date: Thu Jan 29 14:19:46 2026 +0100 - Add test for ECMA402 PR 1015 to verify (new Intl.Segmenter()).segment("123")[Symbol.iterator].name (#4635) + Allow Intl.DisplayNames to support calendars not returned by Intl.supportedValuesOf - * Add test for ECMA402 PR 1015 + Change the assertion so that if a calendar is not supported, then it + must be a calendar not returned by `Intl.supportedValuesOf("calendar")`. - https://github.com/tc39/ecma402/pull/1015 + This allows for example implementations to provide a display name for + `"islamic"`. + + See . diff --git a/js/src/tests/test262/built-ins/Array/fromAsync/shell.js b/js/src/tests/test262/built-ins/Array/fromAsync/shell.js index 9dd780b6d8292..bae26d4551b03 100644 --- a/js/src/tests/test262/built-ins/Array/fromAsync/shell.js +++ b/js/src/tests/test262/built-ins/Array/fromAsync/shell.js @@ -370,6 +370,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/built-ins/Array/prototype/fill/coerced-indexes.js b/js/src/tests/test262/built-ins/Array/prototype/fill/coerced-indexes.js index 3cf5954171f88..cf027e44d4161 100644 --- a/js/src/tests/test262/built-ins/Array/prototype/fill/coerced-indexes.js +++ b/js/src/tests/test262/built-ins/Array/prototype/fill/coerced-indexes.js @@ -5,16 +5,15 @@ esid: sec-array.prototype.fill description: > Fills elements from coerced to Integer `start` and `end` values info: | - 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] ) - - ... - 7. Let relativeStart be ToInteger(start). - 8. ReturnIfAbrupt(relativeStart). - 9. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be - min(relativeStart, len). - 10. If end is undefined, let relativeEnd be len; else let relativeEnd be - ToInteger(end). - ... + Array.prototype.fill ( _value_ [ , _start_ [ , _end_ ] ] ) + + 3. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). + 4. If _relativeStart_ = -∞, let _k_ be 0. + 5. Else if _relativeStart_ < 0, let _k_ be max(_len_ + _relativeStart_, 0). + + 7. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). + 8. If _relativeEnd_ = -∞, let _final_ be 0. + includes: [compareArray.js] ---*/ @@ -74,4 +73,12 @@ assert.compareArray([0, 0].fill(1, 0, 1.5), [1, 0], '[0, 0].fill(1, 0, 1.5) must return [1, 0]' ); +assert.compareArray([0, 0].fill(1, Number.NEGATIVE_INFINITY, 1), [1, 0], + '[0, 0].fill(1, Number.NEGATIVE_INFINITY, 1) must return [1, 0]' +); + +assert.compareArray([0, 0].fill(1, 0, Number.NEGATIVE_INFINITY), [0, 0], + '[0, 0].fill(1, 0, Number.NEGATIVE_INFINITY) must return [0, 0]' +); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js b/js/src/tests/test262/built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js index fb86d87595e1f..ae39a115d7555 100644 --- a/js/src/tests/test262/built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js +++ b/js/src/tests/test262/built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js @@ -42,4 +42,9 @@ actual = a.flat(depthNum); expected = [1, 2] assert.compareArray(actual, expected, 'The value of actual is expected to equal the value of expected'); +// undefined depthNum uses the default value of 1 +actual = a.flat(undefined); +expected = [1, 2]; +assert.compareArray(actual, expected, 'a.flat(undefined) uses default depth of 1'); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Iterator/from/shell.js b/js/src/tests/test262/built-ins/Iterator/from/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/built-ins/Iterator/from/shell.js +++ b/js/src/tests/test262/built-ins/Iterator/from/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/built-ins/NativeErrors/cause_property_native_error.js b/js/src/tests/test262/built-ins/NativeErrors/cause_property_native_error.js index bbc5812e4ebdc..1be98f688a4b6 100644 --- a/js/src/tests/test262/built-ins/NativeErrors/cause_property_native_error.js +++ b/js/src/tests/test262/built-ins/NativeErrors/cause_property_native_error.js @@ -42,6 +42,7 @@ for (var i = 0; i < nativeErrors.length; ++i) { verifyProperty(new nativeError(message), "cause", undefined); verifyProperty(new nativeError(message, { cause: undefined }), "cause", { value: undefined }); + verifyProperty(new nativeError(message, {}), "cause", undefined); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-bigint.js b/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-bigint.js new file mode 100644 index 0000000000000..b9dab3fb8932a --- /dev/null +++ b/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-bigint.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Hyunjoon Kim. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-string.fromcharcode +description: > + String.fromCharCode propagates abrupt completion from ToNumber when argument is BigInt via ToUint16. +info: | + String.fromCharCode ( ..._codeUnits_ ) + + 2. For each element _next_ of _codeUnits_, do + a. Let _nextCU_ be the code unit whose numeric value is ℝ(? ToUint16(_next_)). + + ToUint16 ( _argument_ ) + + 1. Let _number_ be ? ToNumber(_argument_). + + ToNumber ( _argument_ ) + + 2. If _argument_ is either a Symbol or a BigInt, throw a *TypeError* exception. +features: [BigInt] +---*/ + +assert.throws(TypeError, function () { + String.fromCharCode(0n); +}, "ToNumber throws when argument is BigInt, and String.fromCharCode must propagate it."); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-valueof.js b/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-valueof.js new file mode 100644 index 0000000000000..8daeec617178b --- /dev/null +++ b/js/src/tests/test262/built-ins/String/fromCharCode/touint16-tonumber-throws-valueof.js @@ -0,0 +1,29 @@ +// Copyright (C) 2026 Hyunjoon Kim. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-string.fromcharcode +description: > + String.fromCharCode propagates abrupt completion from ToNumber via ToUint16. (ToPrimitive/valueOf throws) +info: | + String.fromCharCode ( ..._codeUnits_ ) + + 2. For each element _next_ of _codeUnits_, do + a. Let _nextCU_ be the code unit whose numeric value is ℝ(? ToUint16(_next_)). + + ToUint16 ( _argument_ ) + + 1. Let _number_ be ? ToNumber(_argument_). + + ToNumber ( _argument_ ) + + 8. Let _primValue_ be ? ToPrimitive(_argument_, ~number~). +---*/ + +assert.throws(Test262Error, function () { + String.fromCharCode({ + valueOf: function () { throw new Test262Error(); } + }); +}, "ToNumber throws when its argument's ToPrimitive step calls a throwing valueOf, and String.fromCharCode must propagate it."); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/String/prototype/lastIndexOf/not-a-substring.js b/js/src/tests/test262/built-ins/String/prototype/lastIndexOf/not-a-substring.js new file mode 100644 index 0000000000000..abfb6524d17ce --- /dev/null +++ b/js/src/tests/test262/built-ins/String/prototype/lastIndexOf/not-a-substring.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Garham Lee. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-string.prototype.lastindexof +description: > + String.prototype.lastIndexOf must be able to return -1 +info: | + String.prototype.lastIndexOf ( _searchString_ [ , _position_ ] ) + + 12. Let _result_ be StringLastIndexOf(_S_, _searchStr_, _start_). + + StringLastIndexOf (_string_, _searchValue_, _fromIndex_) + + 4. For each integer _i_ such that 0 ≤ _i_ ≤ _fromIndex_, in descending order, do + a. Let _candidate_ be the substring of _string_ from _i_ to _i_ + _searchLen_. + b. If _candidate_ is _searchValue_, return _i_. + 5. Return ~not-found~. +---*/ + +assert.sameValue("abc".lastIndexOf("d"), -1, "String.prototype.lastIndexOf returns -1 when searchString is shorter than this and searchString is not a substring of this."); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-propertybag-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..3f9a488ae207b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-propertybag-calendar-invalid-iso-string.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance_1 = new Temporal.Duration(1); +const instance_2 = new Temporal.Duration(2); + +const invalidStrings = [ + ["", "empty string"], + ["notacal", "Unknown calendar"], +]; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar: cal }; + assert.throws( + RangeError, + () => Temporal.Duration.compare(instance_1, instance_2, { relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..5c00e39f45322 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance_1 = new Temporal.Duration(1); +const instance_2 = new Temporal.Duration(2); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.Duration.compare(instance_1, instance_2, { relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..55f54e3142067 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-no-fractional-minutes-hours.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Fractional minutes or hours in time string in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance1 = new Temporal.Duration(1, 0, 0, 0, 24) +const instance2 = new Temporal.Duration(1, 0, 0, 0, 24) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.Duration.compare(instance1, instance2, { relativeTo: arg }), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js index 3dfa6b72a38a3..317d79a22e1bd 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js @@ -19,6 +19,7 @@ const badOffsets = [ null, // must be a string true, // must be a string 1000n, // must be a string + "+00:0000", // separator must be consistent for hours/minutes and minutes/seconds ]; badOffsets.forEach((offset) => { const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone: "UTC" }; diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js index f48784d3b5712..c0eda962f2dfb 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js @@ -8,16 +8,23 @@ description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string features: [Temporal] ---*/ +const duration1 = new Temporal.Duration(0, 0, 0, 31); +const duration2 = new Temporal.Duration(0, 1); + [ '2000-01-01[UTC]', '2000-01-01T00:00[UTC]', '2000-01-01T00:00+00:00[UTC]', '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', ].forEach((relativeTo) => { - const duration1 = new Temporal.Duration(0, 0, 0, 31); - const duration2 = new Temporal.Duration(0, 1); const result = Temporal.Duration.compare(duration1, duration2, { relativeTo }); assert.sameValue(result, 0); }); +[ + '2025-01-01T00:00:00+00:0000[UTC]' +].forEach((relativeTo) => { + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), "separators in offset are inconsistent"); +}); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js index 3d53993e4e392..e9b87fd135cfd 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js @@ -8,11 +8,35 @@ description: Appropriate error thrown if primitive input cannot convert to a val features: [Temporal] ---*/ -assert.throws(TypeError, () => Temporal.Duration.from(undefined), "undefined"); -assert.throws(TypeError, () => Temporal.Duration.from(null), "null"); -assert.throws(TypeError, () => Temporal.Duration.from(true), "boolean"); -assert.throws(TypeError, () => Temporal.Duration.from(Symbol()), "Symbol"); -assert.throws(TypeError, () => Temporal.Duration.from(5), "number"); -assert.throws(TypeError, () => Temporal.Duration.from(5n), "bigint"); +assert.throws(TypeError, () => Temporal.Duration.from(), "no argument"); + +const primitiveTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.Duration, "Temporal.Duration, object"], + [Temporal.Duration.prototype, "Temporal.Duration.prototype, object"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + TypeError, + () => Temporal.Duration.from(arg), + `${description} does not convert to a valid ISO string` + ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + TypeError, + () => Temporal.Duration.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } +} reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-propertybag.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-propertybag.js new file mode 100644 index 0000000000000..80a750270fe47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-propertybag.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Basic propertybag arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertDuration(Temporal.Duration.from({ + years: 0, + months: 0, + weeks: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 0 +}), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + +TemporalHelpers.assertDuration(Temporal.Duration.from({ + years: 1, + months: 2, + weeks: 3, + days: 4, + hours: 5, + minutes: 6, + seconds: 7, + milliseconds: 8, + microseconds: 9, + nanoseconds: 10 +}), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +TemporalHelpers.assertDuration(Temporal.Duration.from({ + years: -1, + months: -2, + weeks: -3, + days: -4, + hours: -5, + minutes: -6, + seconds: -7, + milliseconds: -8, + microseconds: -9, + nanoseconds: -10 +}), -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js index 65b257b3551d4..bcbdb292fca74 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js @@ -17,6 +17,15 @@ const resultNegHours = Temporal.Duration.from("-PT1.03125H"); TemporalHelpers.assertDuration(resultNegHours, 0, 0, 0, 0, -1, -1, -52, -500, 0, 0, "negative fractional hours rounded with correct rounding mode"); +const resultPosMinutes = Temporal.Duration.from('PT3.125M'); +TemporalHelpers.assertDuration(resultPosMinutes, 0, 0, 0, 0, 0, 3, 7, 500, 0, 0, + "positive fractional minutes rounded with correct rounding mode"); + +const resultNegMinutes = Temporal.Duration.from('-PT3,025M'); +TemporalHelpers.assertDuration(resultNegMinutes, 0, 0, 0, 0, 0, -3, -1, -500, 0, 0, + "negative fractional minutes rounded with correct rounding mode"); + + // The following input should not round, but may fail if an implementation does // floating point arithmetic too early: @@ -28,4 +37,12 @@ const resultNegSeconds = Temporal.Duration.from("-PT46H66M71.50040904S"); TemporalHelpers.assertDuration(resultNegSeconds, 0, 0, 0, 0, -46, -66, -71, -500, -409, -40, "negative fractional seconds not rounded"); +const resultPosSecondsWithDate = Temporal.Duration.from('P11Y22M33W44D' + 'T55H66M77.987654321S'); +TemporalHelpers.assertDuration(resultPosSecondsWithDate, 11, 22, 33, 44, 55, 66, 77, 987, 654, 321, + "positive fractional seconds in datetime string not rounded"); + +const resultNegSecondsWithDate = Temporal.Duration.from('-P11Y22M33W44D' + 'T55H66M77.987654321S'); +TemporalHelpers.assertDuration(resultNegSecondsWithDate, -11, -22, -33, -44, -55, -66, -77, -987, -654, -321, + "negative fractional seconds in datetime string not rounded"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js index 963c72f4af264..f86c5dad61723 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js @@ -25,7 +25,30 @@ const tests = [ "+PT", "P1Y1M1W1DT1H1M1.01Sjunk", "P-1Y1M", - "P1Y-1M" + "P1Y-1M", + "P2H", + "P2.5M", + "P2,5M", + "P2S", + "PT2.H3M", + "PT2,H3M", + "PT2.H3S", + "PT2,H3S", + "PT2.H0.5M", + "PT2,H0,5M", + "PT2.H0.5S", + "PT2,H0,5S", + "PT2H3.2M3S", + "PT2H3,2M3S", + "PT2H3.2M0.3S", + "PT2H3,2M0,3S", + "PT.1H", + "PT,1H", + "PT.1M", + "PT,1M", + "PT.1S", + "PT,1S", + "", ]; for (const input of tests) { diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js index 84437a1b04610..5eab2620663be 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/out-of-range.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// Copyright (C) 2026 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -34,7 +34,7 @@ assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 15011998757 assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -150119987579017), "minutes < min"); assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -150119987579016, -60), "seconds balance into minutes < min"); -// 2^53 = 9007199254740992 +// 2^53 = 9007199254740992 = max safe integer + 1 assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740992), "seconds > max"); assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 1000), "ms balance into seconds > max"); assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199254740991, 999, 1000), "µs balance into seconds > max"); @@ -44,4 +44,16 @@ assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199 assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -999, -1000), "µs balance into seconds < min"); assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199254740991, -999, -999, -1000), "ns balance into seconds < min"); +// max safe integer - floor(max safe integer / 1000) +1 = 8998192055486252 +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 8998192055486252, 9007199254740991 , 0, 0), "max ms balance into s > max"); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -8998192055486252, -9007199254740991, 0, 0), "min ms balance into s < min"); + +// max safe integer - floor(max safe integer / 1000000) +1 = 9007190247541738 +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007190247541738, 0, 9007199254740991, 0), "max µs balance into s > max"); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007190247541738, 0, -9007199254740991, 0), "min µs balance into s < min"); + +// max safe integer - floor(max safe integer / 1000000000) +1 = 9007199245733793 +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 9007199245733793, 0, 0, 9007199254740991), "max ns balance into s > max"); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -9007199245733793, 0, 0, -9007199254740991), "min ns balance into s < min"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-propertybag-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..0e010d71d1bee --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-propertybag-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Brage Hogstad, University of Bergen. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const invalidStrings = [ + ["", "empty string"], + ["notacal", "Unknown calendar"], +]; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar: cal }; + assert.throws( + RangeError, + () => instance.round({ largestUnit: "months", relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..fd05684e044d4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.round({ largestUnit: "months", relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..062f53e5f4ebf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fractional minutes or hours in time string in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.round({ largestUnit: "months", relativeTo: arg }), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-invalid-iso-string.js deleted file mode 100644 index 2b45293a0bc56..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-invalid-iso-string.js +++ /dev/null @@ -1,27 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2025 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.duration.prototype.round -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError -features: [Temporal] ----*/ - -const instance = new Temporal.Duration(1, 0, 0, 0, 24); - -const invalidStrings = [ - ["", "empty string"] -]; - -for (const [calendar, description] of invalidStrings) { - const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; - assert.throws( - RangeError, - () => instance.round({ largestUnit: "years", relativeTo }), - `${description} is not a valid calendar ID` - ); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js index 6203beb0432f4..af89cb7a3d413 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js @@ -19,6 +19,7 @@ const badOffsets = [ null, // must be a string true, // must be a string 1000n, // must be a string + "+00:0000", // separator must be consistent for hours/minutes and minutes/seconds ]; badOffsets.forEach((offset) => { const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js index 82adc4c7955ba..2d76c79a2eeb4 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js @@ -38,4 +38,7 @@ assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeT relativeTo = "2019-11-01T00:00+04:15[UTC]"; assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); +relativeTo = "2025-01-01T00:00:00+00:0000"; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time with offset like 00:0000 shouldn't parse"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js index 3a7a45f6393f5..b46567f988f56 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/max-value.js @@ -1,14 +1,98 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// Copyright (C) 2026 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- esid: sec-temporal.duration.prototype.tojson -description: Balancing the maximum nanoseconds and seconds does not go out of range +description: Various maximum value combinations do not go out of range features: [Temporal] ---*/ -const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, /* s = */ Number.MAX_SAFE_INTEGER, 0, 0, /* ns = */ 999_999_999); -assert.sameValue(d.toJSON(), "PT9007199254740991.999999999S", "max value ns and s does not go out of range"); +const maxYMW = Math.pow(2, 32) - 1; // Maximum years, months, weeks +const maxDays = Math.floor(Number.MAX_SAFE_INTEGER / 86400); +const maxHours = Math.floor(Number.MAX_SAFE_INTEGER / 3600); +const maxMinutes = Math.floor(Number.MAX_SAFE_INTEGER / 60); +const maxSecs = Number.MAX_SAFE_INTEGER; // = 9007199254740991, also max ms, μs, ns + +assert.sameValue(new Temporal.Duration(maxYMW).toJSON(), + 'P' + maxYMW + 'Y', "maximum years"); +assert.sameValue(new Temporal.Duration(-maxYMW).toJSON(), + '-P' + maxYMW + 'Y', "minimum years"); +assert.sameValue(new Temporal.Duration(0, maxYMW).toJSON(), + 'P' + maxYMW + 'M', "maximum months"); +assert.sameValue(new Temporal.Duration(0, -maxYMW).toJSON(), + '-P' + maxYMW + 'M', "minimum months"); +assert.sameValue(new Temporal.Duration(0, 0, maxYMW).toJSON(), + 'P' + maxYMW + 'W', "maximum weeks"); +assert.sameValue(new Temporal.Duration(0, 0, -maxYMW).toJSON(), + '-P' + maxYMW + 'W', "minimum weeks"); +assert.sameValue(new Temporal.Duration(0, 0, 0, maxDays).toJSON(), + 'P' + maxDays + 'D', "maximum days"); +assert.sameValue(new Temporal.Duration(0, 0, 0, -maxDays).toJSON(), + '-P' + maxDays + 'D', "minimum days"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, maxHours).toJSON(), + 'PT' + maxHours + 'H', "maximum hours"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -maxHours).toJSON(), + '-PT' + maxHours + 'H', "minimum hours"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, maxMinutes).toJSON(), + 'PT' + maxMinutes + 'M', "maximum minutes"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, -maxMinutes).toJSON(), + '-PT' + maxMinutes + 'M', "minimum minutes"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, maxSecs).toJSON(), + 'PT' + maxSecs + 'S', "maximum seconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -maxSecs).toJSON(), + '-PT' + maxSecs + 'S', "minimum seconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, maxSecs).toJSON(), + 'PT' + Math.floor(maxSecs / 1000) + '.' + maxSecs % 1000 + 'S', "maximum milliseconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -maxSecs).toJSON(), + '-PT' + Math.floor(maxSecs / 1000) + '.' + maxSecs % 1000 + 'S', "minimum milliseconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, maxSecs).toJSON(), + 'PT' + Math.floor(maxSecs / 1000000) + '.' + maxSecs % 1000000 + 'S', "maximum microseconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -maxSecs).toJSON(), + '-PT' + Math.floor(maxSecs / 1000000) + '.' + maxSecs % 1000000 + 'S', "minimum microseconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, maxSecs).toJSON(), + 'PT' + Math.floor(maxSecs / 1000000000) + '.' + maxSecs % 1000000000 + 'S', "maximum nanoseconds"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -maxSecs).toJSON(), + '-PT' + Math.floor(maxSecs / 1000000000) + '.' + maxSecs % 1000000000 + 'S', "minimum nanoseconds"); + +// Combinations with maximum values without balancing. +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, maxSecs, 999).toJSON(), + 'PT' + maxSecs + '.999S', "max value ms and s does not go out of range"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -maxSecs, -999).toJSON(), + '-PT' + maxSecs + '.999S', "min value ms and s does not go out of range"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, maxSecs, 999, 999).toJSON(), + 'PT' + maxSecs + '.999999S', "max value ms, μs and s does not go out of range"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -maxSecs, -999, -999).toJSON(), + '-PT' + maxSecs + '.999999S', "min value ms, μs and s does not go out of range"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, maxSecs, 999, 999, 999).toJSON(), + 'PT' + maxSecs + '.999999999S', "max value ms, μs, ns and s does not go out of range"); +assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -maxSecs, -999, -999, -999).toJSON(), + '-PT' + maxSecs + '.999999999S', "min value ms, μs, ns and s does not go out of range"); + +// Combinations with maximum values with balancing. +const balanceMaxSecondsMilliseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, maxSecs - Math.floor(maxSecs / 1000), maxSecs, 0, 0); +assert.sameValue(balanceMaxSecondsMilliseconds.toJSON(), + 'PT' + maxSecs + '.' + maxSecs % 1000 + 'S', "balancing max ms to max s"); +const balanceMinSecondsMilliseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, -(maxSecs - Math.floor(maxSecs / 1000)), -maxSecs, 0, 0); +assert.sameValue(balanceMinSecondsMilliseconds.toJSON(), + '-PT' + maxSecs + '.' + maxSecs % 1000 + 'S', "balancing min ms to max s"); +const balanceMaxSecondsMicroseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, maxSecs - Math.floor(maxSecs / 1000000), 0, maxSecs, 0); +assert.sameValue(balanceMaxSecondsMicroseconds.toJSON(), + 'PT' + maxSecs + '.' + maxSecs % 1000000 + 'S', "balancing max μs to max s"); +const balanceMinSecondsMicroseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, -(maxSecs - Math.floor(maxSecs / 1000000)), 0, -maxSecs, 0); +assert.sameValue(balanceMinSecondsMicroseconds.toJSON(), + '-PT' + maxSecs + '.' + maxSecs % 1000000 + 'S', "balancing min μs to min s"); +const balanceMaxSecondsNanoseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, maxSecs - Math.floor(maxSecs / 1000000000), 0, 0, maxSecs); +assert.sameValue(balanceMaxSecondsNanoseconds.toJSON(), + 'PT' + maxSecs + '.' + maxSecs % 1000000000 + 'S', "balancing max ns to max s"); +const balanceMinSecondsNanoseconds = new Temporal.Duration( + 0, 0, 0, 0, 0, 0, -(maxSecs - Math.floor(maxSecs / 1000000000)), 0, 0, -maxSecs); +assert.sameValue(balanceMinSecondsNanoseconds.toJSON(), + '-PT' + maxSecs + '.' + maxSecs % 1000000000 + 'S', "balancing min ns to min s"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js index 8ce9ad27114f1..5a6d38bf90d7a 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-5.js @@ -58,4 +58,31 @@ const seconds = 8692288669465520; ); } +{ + const d = new Temporal.Duration(0, 0, 5, 5); + + const result = d.total({ unit: "months", relativeTo: "1972-01-31" }) + +/* +Expected months checked using Decimals in Python: + +>>> from decimal import * +>>> getcontext().prec = 18 +>>> dest_epoch_ns = Decimal(69120000000000000) +>>> start_epoch_ns = Decimal(68169600000000000) +>>> end_epoch_ns = 70848000000000000 +>>> progress = ((dest_epoch_ns - start_epoch_ns) / (end_epoch_ns - start_epoch_ns)) +>>> progress +Decimal('0.354838709677419355') +>>> Decimal(1) + progress +Decimal('1.35483870967741936') + +The result should be truncated. +*/ + const expectedMonths = 1.3548387096774193; + + assert.sameValue(result, expectedMonths, + "NudgeToCalendarUnit should implement floating-point calculation correctly for largestUnit months"); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-propertybag-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..318926026172f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-propertybag-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Brage Hogstad, University of Bergen. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const invalidStrings = [ + ["", "empty string"], + ["notacal", "Unknown calendar"], +]; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar: cal }; + assert.throws( + RangeError, + () => instance.total({ unit: "months", relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..a5d38f4319d20 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Invalid ISO string as calendar in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.total({ unit: "months", relativeTo: arg }), + `${description} is not a valid calendar ID` + ); +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js index 20a8dca9eb186..947859240b9b0 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js @@ -34,7 +34,7 @@ assert.sameValue( "maximum date is a valid ISO string for PlainDateTime relativeTo" ); -relativeTo = "+275760-09-12T23:59:60+00:00[UTC]"; +relativeTo = "+275760-09-12T00:00:00+00:00[UTC]"; const result4 = instance.total({ unit: "days", relativeTo }); assert.sameValue( result4, @@ -42,6 +42,10 @@ assert.sameValue( "maximum date is a valid ISO string for ZonedDateTime relativeTo" ); +relativeTo = "+275760-09-12T00:00:01+00:00[UTC]"; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), + `${relativeTo} is out of range as a relativeTo argument for total`); + relativeTo = { year: -271821, month: 4, day: 19 }; const result5 = instance.total({ unit: "days", relativeTo }); assert.sameValue( @@ -66,7 +70,7 @@ assert.sameValue( "minimum date is valid in a property bag for ZonedDateTime relativeTo" ); -relativeTo = { year: 275760, month: 9, day: 12, hour: 23, minute: 59, second: 60, timeZone: "UTC" }; +relativeTo = { year: 275760, month: 9, day: 12, hour: 0, minute: 0, second: 0, timeZone: "UTC" }; const result8 = instance.total({ unit: "days", relativeTo }); assert.sameValue( result8, diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..b2ad973deea60 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Fractional minutes or hours in time string in relativeTo option should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.total({ unit: "months", relativeTo: arg }), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-invalid-iso-string.js deleted file mode 100644 index 5ef9a7433a1c3..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-invalid-iso-string.js +++ /dev/null @@ -1,26 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2025 Brage Hogstad, University of Bergen. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.duration.prototype.total -description: Various invalid ISO string values for relativeTo.calendar -features: [Temporal] ----*/ - -const instance = new Temporal.Duration(1, 0, 0, 0, 24); - -const invalidStrings = [ - ["", "empty string"], -]; - -for (const [calendar, description] of invalidStrings) { - const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; - assert.throws( - RangeError, - () => instance.total({ unit: "days", relativeTo }), - `${description} is not a valid calendar ID` - ); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js index 58c82cfa6b313..7108edfd12fd5 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js @@ -19,6 +19,7 @@ const badOffsets = [ null, // must be a string true, // must be a string 1000n, // must be a string + "+00:0000", // separator must be consistent for hours/minutes and minutes/seconds ]; badOffsets.forEach((offset) => { const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js index b2e0cd8a56cca..a5b5f5f24850b 100644 --- a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js @@ -37,4 +37,7 @@ assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "d relativeTo = "2019-11-01T00:00+04:15[UTC]"; assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); +relativeTo = "2025-01-01T00:00:00+00:0000"; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time with offset like 00:0000 shouldn't parse"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/basic.js index 9b52398cec3bd..649961ceaa9a2 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/basic.js @@ -11,11 +11,26 @@ features: [Temporal] const bigIntInstant = new Temporal.Instant(217175010123456789n); assert(bigIntInstant instanceof Temporal.Instant, "BigInt instanceof"); assert.sameValue(bigIntInstant.epochMilliseconds, 217175010123, "BigInt epochMilliseconds"); +assert.sameValue(bigIntInstant.epochNanoseconds, 217175010123456789n, "BigInt epochNanoseconds"); const stringInstant = new Temporal.Instant("217175010123456789"); assert(stringInstant instanceof Temporal.Instant, "String instanceof"); assert.sameValue(stringInstant.epochMilliseconds, 217175010123, "String epochMilliseconds"); +assert.sameValue(stringInstant.epochNanoseconds, 217175010123456789n, "String epochNanoseconds"); + +const negativeBigIntInstant = new Temporal.Instant(-217175010123456789n); +assert(negativeBigIntInstant instanceof Temporal.Instant, "negative BigInt instanceof"); +assert.sameValue(negativeBigIntInstant.epochMilliseconds, -217175010124, "negagive BigInt epochMilliseconds"); +assert.sameValue(negativeBigIntInstant.epochNanoseconds, -217175010123456789n, "negative BigInt epochNanoseconds"); + +const negativeStringInstant = new Temporal.Instant("-217175010123456789"); +assert(negativeStringInstant instanceof Temporal.Instant, "negative String instanceof"); +assert.sameValue(negativeStringInstant.epochMilliseconds, -217175010124, "negative string epochMilliseconds"); +assert.sameValue(negativeStringInstant.epochNanoseconds, -217175010123456789n, "negative string epochNanoseconds"); assert.throws(SyntaxError, () => new Temporal.Instant("abc123"), "invalid BigInt syntax"); +assert.sameValue(new Temporal.Instant(true).epochNanoseconds, 1n, "true as argument is 1n"); +assert.sameValue(new Temporal.Instant(false).epochNanoseconds, 0n, "false as argument is 0n"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Instant/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..319fb75aa6fef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123[CET]", "Fractional minutes"], + ["2025-04-03T12.5[CET]", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.Instant.compare(arg, new Temporal.Instant(0n)), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.Instant.compare(new Temporal.Instant(0n), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js index 92af4d6f632a2..411be0ed498db 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-object-tostring.js @@ -11,6 +11,9 @@ features: [Temporal] const arg = {}; assert.throws(RangeError, () => Temporal.Instant.from(arg), "[object Object] is not a valid ISO string"); +const temporalInstant = Temporal.Instant; +assert.throws(RangeError, () => Temporal.Instant.from(temporalInstant), "Temporal.Instant object is not a valid ISO string"); + arg.toString = function() { return "1970-01-01T00:00Z"; }; diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js index a7e601ee8d64d..c4940b8836f8d 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-string-invalid.js @@ -57,6 +57,9 @@ const invalidStrings = [ // valid, but outside the supported range: "-999999-01-01T00:00Z", "+999999-01-01T00:00Z", + // "00:0000" is invalid (the hour/minute and minute/second separator + // or lack thereof needs to match). + "2025-01-01T00:00:00+00:0000", ]; for (const arg of invalidStrings) { assert.throws( diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js index eadb4b3963d44..cdf5341732f52 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/argument-wrong-type.js @@ -10,35 +10,33 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.Instant.from(), "no argument"); + const primitiveTests = [ [undefined, 'undefined'], [null, 'null'], [true, 'boolean'], - ['', 'empty string'], [1, "number that doesn't convert to a valid ISO string"], [19761118, 'number that would convert to a valid ISO string in other contexts'], [1n, 'bigint'], - [{}, 'plain object'], - [Temporal.Instant, 'Temporal.Instant, object'] + [Symbol(), 'symbol'], + [Temporal.Instant.prototype, 'Temporal.Instant.prototype (fails brand check)'], ]; for (const [arg, description] of primitiveTests) { assert.throws( - typeof arg === 'string' || (typeof arg === 'object' && arg !== null) || typeof arg === 'function' - ? RangeError - : TypeError, + TypeError, () => Temporal.Instant.from(arg), `${description} does not convert to a valid ISO string` ); -} - -const typeErrorTests = [ - [Symbol(), 'symbol'], - [Temporal.Instant.prototype, 'Temporal.Instant.prototype, object'] // fails brand check in toString() -]; -for (const [arg, description] of typeErrorTests) { - assert.throws(TypeError, () => Temporal.Instant.from(arg), `${description} does not convert to a string`); + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + TypeError, + () => Temporal.Instant.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Instant/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..674635a728ad8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123[CET]", "Fractional minutes"], + ["2025-04-03T12.5[CET]", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.Instant.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/limits.js b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/limits.js index 16e10be5c97d3..e14c13a7c999f 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/limits.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/fromEpochNanoseconds/limits.js @@ -12,6 +12,7 @@ info: | ... 2. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception. ... +includes: [temporalHelpers.js] features: [Temporal] ---*/ @@ -19,5 +20,9 @@ var limit = 8640000000000000000000n; assert.throws(RangeError, () => Temporal.Instant.fromEpochNanoseconds(-limit - 1n)); assert.throws(RangeError, () => Temporal.Instant.fromEpochNanoseconds(limit + 1n)); +TemporalHelpers.assertInstantsEqual(Temporal.Instant.fromEpochNanoseconds(-limit), + Temporal.Instant.from("-271821-04-20T00:00:00Z")); +TemporalHelpers.assertInstantsEqual(Temporal.Instant.fromEpochNanoseconds(limit), + Temporal.Instant.from("+275760-09-13T00:00:00Z")); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/basic.js new file mode 100644 index 0000000000000..c2544a976e13b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/basic.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.equals +description: Basic functionality for Temporal.Instant.prototype.equals. +features: [Temporal] +---*/ + +let inst1 = new Temporal.Instant(1234567890123456789n); +let inst2 = new Temporal.Instant(1234567890123456000n); +let inst3 = new Temporal.Instant(1234567890123456000n); +assert(!inst1.equals(inst2)); +assert(inst2.equals(inst3)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..4dac017cf7251 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Instant(0n) + +const invalidStrings = [ + ["2025-04-03T05:07.123[CET]", "Fractional minutes"], + ["2025-04-03T12.5[CET]", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-increments.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-increments.js index 0651b95e4dcc5..6fd8f04f4c996 100644 --- a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-increments.js +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/round/rounding-increments.js @@ -47,4 +47,22 @@ TemporalHelpers.assertInstantsEqual(inst.round({ roundingIncrement: 10 }), Temporal.Instant.from("1976-11-18T14:23:30.12345679Z")); +const unitsAndIncrements = { + "hour": [1, 2, 4, 6, 8, 12, 24], + "minute": [1, 5, 10, 20, 30, 40, 80, 120, 720, 1440], + "second": [1, 5, 10, 20, 25, 30, 50, 100, 400, 86400], + "millisecond": [1, 5, 10, 20, 25, 30, 50, 100, 86_400_000], + "microsecond": [1, 5, 10, 20, 25, 30, 50, 100], + "nanosecond": [1, 5, 10, 20, 25, 30, 50, 100] +}; + +// Just check that each combination of unit and increment doesn't throw +Object.entries(unitsAndIncrements).forEach(([unit, increments]) => { + increments.forEach((increment) => { + const result = inst.round({ smallestUnit: unit, roundingMode: "ceil", roundingIncrement: increment }); + assert.sameValue(result instanceof Temporal.Instant, true, `${unit} ${increment}`); + }) +}); + + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..9f9c954a785bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/since/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.since +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Instant(0n) + +const invalidStrings = [ + ["2025-04-03T05:07.123[CET]", "Fractional minutes"], + ["2025-04-03T12.5[CET]", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/fromEpochMilliseconds.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/fromEpochMilliseconds.js new file mode 100644 index 0000000000000..5bdd0ab742df1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toJSON/fromEpochMilliseconds.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.tojson +description: Various test cases using fromEpochMilliseconds. +features: [Temporal] +---*/ + +assert.sameValue(Temporal.Instant.fromEpochMilliseconds(0).toJSON(), '1970-01-01T00:00:00Z'); +let days_in_ms = 24 * 60 * 60 * 1000; +assert.sameValue(Temporal.Instant.fromEpochMilliseconds(365 * days_in_ms - 1).toJSON(), '1970-12-31T23:59:59.999Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds(365 * days_in_ms).toJSON(), '1971-01-01T00:00:00Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds(2 * 365 * days_in_ms - 1).toJSON(), '1971-12-31T23:59:59.999Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds(2 * 365 * days_in_ms).toJSON(), '1972-01-01T00:00:00Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds((2 * 365 + 58) * days_in_ms).toJSON(), '1972-02-28T00:00:00Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds((2 * 365 + 59) * days_in_ms).toJSON(), '1972-02-29T00:00:00Z'); +assert.sameValue(Temporal.Instant.fromEpochMilliseconds((15 * 365 + 4) * days_in_ms).toJSON(), '1985-01-01T00:00:00Z'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/no-observable-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/no-observable-array-iteration.js new file mode 100644 index 0000000000000..5fe93f7fce9fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/no-observable-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.tozoneddatetimeiso +description: > + Calling GetPossibleEpochNanoseconds (from ToTemporalZonedDateTime > InterpretISODateTimeOffset) + causes no observable array iteration. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +let inst = new Temporal.Instant(0n); +let zdt = inst.toZonedDateTimeISO("UTC"); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..7073bae6db28c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Instant/prototype/until/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.until +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Instant(0n) + +const invalidStrings = [ + ["2025-04-03T05:07.123[CET]", "Fractional minutes"], + ["2025-04-03T12.5[CET]", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-invalid-iso-string.js index 3e15e4d430c60..6f9ccc65f8f51 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/calendar-invalid-iso-string.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.plaindate.constructor -description: Various invalid ISO string values for calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], ["1997-12-04[u-ca=iso8601]", "ISO string with calendar annotation"], + ["notacal", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-invalid-iso-string.js index 1b8291396475a..69b46a83d9041 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-invalid-iso-string.js @@ -10,10 +10,12 @@ features: [Temporal] const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; - -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..d11162a79abf3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.compare +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(1976, 11, 18)), + `${description} is not a valid calendar ID (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainDate.compare(new Temporal.PlainDate(1976, 11, 18), arg), + `${description} is not a valid calendar ID (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..9a6fe34715f07 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDate.compare(arg, new Temporal.PlainDate(2025, 4, 3)), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainDate.compare(new Temporal.PlainDate(2025, 4, 3), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-invalid-iso-string.js index e28aa7e2d42a9..588cc71ae4a3c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-invalid-iso-string.js @@ -4,16 +4,18 @@ /*--- esid: sec-temporal.plaindate.from -description: Various invalid ISO string values for calendar in a property bag +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; - -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.PlainDate.from(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js index 5c7e4c369b919..96de9225a6295 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js @@ -13,5 +13,7 @@ const calendar = "iso8601"; const plainDate = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18, calendar }); TemporalHelpers.assertPlainDate(plainDate, 1976, 11, "M11", 18); assert.sameValue(plainDate.calendarId, "iso8601", "calendar string is iso8601"); +const plainDateImplicitCalendar = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18 }); +assert.sameValue(plainDateImplicitCalendar.calendarId, "iso8601", "default calendar is iso8601"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..4a044012e2844 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDate.from(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js index d8cd182a203f4..d056caae55a0c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-string-invalid.js @@ -51,6 +51,9 @@ const invalidStrings = [ // valid, but outside the supported range: "-999999-01-01", "+999999-01-01", + // non-matching separators + "2020-0101", + "202001-01", ]; for (const arg of invalidStrings) { assert.throws( diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js index 8f7cf439c25e3..e9c19b9793028 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.PlainDate.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -25,6 +27,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.PlainDate.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.PlainDate.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -36,6 +46,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.PlainDate.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.PlainDate.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/month-code-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/month-code-wrong-type.js new file mode 100644 index 0000000000000..be46bbee93c02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/month-code-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: Month code must be a string +features: [Temporal] +---*/ + +const monthCodeValues = [ + 5, 5n, false, Symbol(), null, { toString: () => 5 } +]; + +const year = 2026; + +for (const monthCode of monthCodeValues) { + assert.throws(TypeError, () => Temporal.PlainDate.from({ + year, + monthCode, + day: 1 + }), typeof monthCode === 'symbol' ? + "Symbol should be rejected as month code" : + `month code ${monthCode} should be rejected`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/negative-month-or-day.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/negative-month-or-day.js new file mode 100644 index 0000000000000..ba23a55d75f82 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/negative-month-or-day.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: Months and days must be non-negative integers +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.PlainDate.from({ year: 2000, day: 1, month: -1 })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ year: 2000, month: 1, day: -1 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..e2f01238857dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDate.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-basic.js new file mode 100644 index 0000000000000..a0755e36630f4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-basic.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: Basic tests with options +features: [Temporal] +---*/ + +[ + { overflow: 'constrain' }, + { overflow: 'reject' } +].forEach(function (validOptions) { + let d = new Temporal.PlainDate(1, 2, 3); + let d2 = Temporal.PlainDate.from(d, validOptions); + assert.sameValue(d2.year, 1); + assert.sameValue(d2.month, 2); + assert.sameValue(d2.day, 3); + assert.sameValue(d2.calendarId, 'iso8601'); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js index 15b317f3ffce3..07db7e9ec2402 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-undefined.js @@ -16,4 +16,7 @@ assert.sameValue(explicit.month, 12, "default overflow is constrain"); const implicit = Temporal.PlainDate.from(fields); assert.sameValue(implicit.month, 12, "default overflow is constrain"); +const implicitEmpty = Temporal.PlainDate.from(fields, {}); +assert.sameValue(implicitEmpty.month, 12, "default overflow is constrain"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js index 724b25c5dd1db..98c72d99ec5bb 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/options-wrong-type.js @@ -15,6 +15,9 @@ const badOptions = [ Symbol(), 1, 2n, + Infinity, + NaN, + null, ]; for (const value of badOptions) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-reject.js new file mode 100644 index 0000000000000..2d06223cfcea0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/overflow-reject.js @@ -0,0 +1,76 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.plaindate.from +description: Out-of-range months/days are rejected with overflow 'reject' +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 1, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 2, + day: 29 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 3, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 4, + day: 31 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 5, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 6, + day: 31 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 7, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 8, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 9, + day: 31 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 10, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 11, + day: 31 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 12, + day: 32 +}, { overflow: 'reject' })); +assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 2021, + month: 13, + day: 5 +}, { overflow: 'reject' })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/roundtrip-from-property-bag.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/roundtrip-from-property-bag.js index 1f5d01c490a6c..88eb5632d129d 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/roundtrip-from-property-bag.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/roundtrip-from-property-bag.js @@ -11,23 +11,40 @@ includes: [temporalHelpers.js] features: [Temporal, Intl.Era-monthcode] ---*/ -const options = { overflow: "reject" }; +const options = [ + { overflow: "constrain" }, + { overflow: "reject" } +] -testRoundtrip(2000); -testRoundtrip(1); +const testData = [ + [2000, 1, "M01", 1], + [1, 1, "M01", 1], + [2021, 7, "M07", 15], + [2021, 7, "M07", 3], + [2021, 12, "M12", 31], + [2021, 7, "M07", 15], +]; -function testRoundtrip(year) { - const dateFromYearMonth = Temporal.PlainDate.from({ year, month: 1, day: 1 }, options); - TemporalHelpers.assertPlainDate( - dateFromYearMonth, - year, 1, "M01", 1, - `${dateFromYearMonth} - created from year and month`); +for (const [year, month, monthCode, day] of testData) { + testRoundtrip(year, month, monthCode, day); +} + +function testRoundtrip(year, month, monthCode, day) { + for (const opt of options) { + const dateFromYearMonth = Temporal.PlainDate.from({ year, month, day }, opt); + TemporalHelpers.assertPlainDate( + dateFromYearMonth, + year, month, monthCode, day, + `${dateFromYearMonth} - created from year and month`); + } - const dateFromYearMonthCode = Temporal.PlainDate.from({ year, monthCode: "M01", day: 1 }, options); - TemporalHelpers.assertPlainDate( - dateFromYearMonthCode, - year, 1, "M01", 1, - `${dateFromYearMonthCode} - created from year and month code`); + for (const opt of options) { + const dateFromYearMonthCode = Temporal.PlainDate.from({ year, monthCode, day }, opt); + TemporalHelpers.assertPlainDate( + dateFromYearMonthCode, + year, month, monthCode, day, + `${dateFromYearMonthCode} - created from year and month code`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-month-day-need-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-month-day-need-constrain.js index 987224095886e..6c8f0910b7bff 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-month-day-need-constrain.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-month-day-need-constrain.js @@ -16,74 +16,40 @@ features: [Temporal] includes: [temporalHelpers.js] ---*/ -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 1, day: 133}), - 2021, 1, "M01", 31, - "year/month/day with day need to be constrained in Jan"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 2, day: 133}), - 2021, 2, "M02", 28, - "year/month/day with day need to be constrained in Feb"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 3, day: 133}), - 2021, 3, "M03", 31, - "year/month/day with day need to be constrained in March"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 4, day: 133}), - 2021, 4, "M04", 30, - "year/month/day with day need to be constrained in April"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 5, day: 133}), - 2021, 5, "M05", 31, - "year/month/day with day need to be constrained in May"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 6, day: 133}), - 2021, 6, "M06", 30, - "year/month/day with day need to be constrained in Jun"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 7, day: 133}), - 2021, 7, "M07", 31, - "year/month/day with day need to be constrained in July"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 8, day: 133}), - 2021, 8, "M08", 31, - "year/month/day with day need to be constrained in Aug"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 9, day: 133}), - 2021, 9, "M09", 30, - "year/month/day with day need to be constrained in Sept."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 10, day: 133}), - 2021, 10, "M10", 31, - "year/month/day with day need to be constrained in Oct."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 11, day: 133}), - 2021, 11, "M11", 30, - "year/month/day with day need to be constrained in Nov."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 12, day: 133}), - 2021, 12, "M12", 31, - "year/month/day with day need to be constrained in Dec."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 13, day: 500}), - 2021, 12, "M12", 31, - "year/month/day with month and day need to be constrained"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, month: 999999, day: 500}), - 2021, 12, "M12", 31, - "year/month/day with month and day need to be constrained"); +const year = 2021; + +const testData = [ + [1, 133, "Jan", 1, "M01", 31], + [2, 133, "Feb", 2, "M02", 28], + [3, 133, "March", 3, "M03", 31], + [4, 133, "April", 4, "M04", 30], + [5, 133, "May", 5, "M05", 31], + [6, 133, "Jun", 6, "M06", 30], + [7, 133, "Jul", 7, "M07", 31], + [8, 133, "Aug", 8, "M08", 31], + [9, 133, "Sept", 9, "M09", 30], + [10, 133, "Oct", 10, "M10", 31], + [11, 133, "Nov", 11, "M11", 30], + [12, 133, "Dec", 12, "M12", 31], + [13, 500, "out-of-range month 13", 12, "M12", 31], + [999999, 500, "out-of-range month 999999", 12, "M12", 31], + [3, 9033, "out-of-range day 9033", 3, "M03", 31], + [4, 50, "out-of-range day 50", 4, "M04", 30], + [5, 77, "out-of-range day 77", 5, "M05", 31], + [6, 33, "out-of-range date 06-33", 6, "M06", 30], + [7, 33, "out-of-range day 07-33", 7, "M07", 31], + [8, 300, "out-of-range day 300", 8, "M08", 31], + [9, 400, "out-of-range date 09-400", 9, "M09", 30], + [10, 400, "out-of-range date 10-400", 10, "M10", 31], + [11, 400, "out-of-range date 11-400", 11, "M11", 30], + [12, 500, "out-of-range day 500", 12, "M12", 31], +]; + +for (const [month, day, descr, expectedMonth, expectedMonthCode, expectedDay] of testData) { + TemporalHelpers.assertPlainDate( + Temporal.PlainDate.from({year, month, day}), + year, expectedMonth, expectedMonthCode, expectedDay, + `year/month/day need to be constrained in ${descr}`); +} reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-monthCode-day-need-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-monthCode-day-need-constrain.js index 33b308535890f..8c6a02f0433f7 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-monthCode-day-need-constrain.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/from/with-year-monthCode-day-need-constrain.js @@ -16,64 +16,38 @@ features: [Temporal] includes: [temporalHelpers.js] ---*/ -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M01", day: 133}), - 2021, 1, "M01", 31, - "year/monthCode/day with day need to be constrained in Jan"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M02", day: 133}), - 2021, 2, "M02", 28, - "year/monthCode/day with day need to be constrained in Feb"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M03", day: 133}), - 2021, 3, "M03", 31, - "year/monthCode/day with day need to be constrained in March"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M04", day: 133}), - 2021, 4, "M04", 30, - "year/monthCode/day with day need to be constrained in April"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M05", day: 133}), - 2021, 5, "M05", 31, - "year/monthCode/day with day need to be constrained in May"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M06", day: 133}), - 2021, 6, "M06", 30, - "year/monthCode/day with day need to be constrained in Jun"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M07", day: 133}), - 2021, 7, "M07", 31, - "year/monthCode/day with day need to be constrained in July"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M08", day: 133}), - 2021, 8, "M08", 31, - "year/monthCode/day with day need to be constrained in Aug"); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M09", day: 133}), - 2021, 9, "M09", 30, - "year/monthCode/day with day need to be constrained in Sept."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M10", day: 133}), - 2021, 10, "M10", 31, - "year/monthCode/day with day need to be constrained in Oct."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M11", day: 133}), - 2021, 11, "M11", 30, - "year/monthCode/day with day need to be constrained in Nov."); - -TemporalHelpers.assertPlainDate( - Temporal.PlainDate.from({year: 2021, monthCode: "M12", day: 133}), - 2021, 12, "M12", 31, - "year/monthCode/day with day need to be constrained in Dec."); +const year = 2021; + +const testData = [ + ["M01", 133, "Jan", 1, "M01", 31], + ["M02", 133, "Feb", 2, "M02", 28], + ["M03", 133, "March", 3, "M03", 31], + ["M04", 133, "April", 4, "M04", 30], + ["M05", 133, "May", 5, "M05", 31], + ["M06", 133, "Jun", 6, "M06", 30], + ["M07", 133, "Jul", 7, "M07", 31], + ["M08", 133, "Aug", 8, "M08", 31], + ["M09", 133, "Sept", 9, "M09", 30], + ["M10", 133, "Oct", 10, "M10", 31], + ["M11", 133, "Nov", 11, "M11", 30], + ["M12", 133, "Dec", 12, "M12", 31], + ["M03", 9033, "out-of-range day 9033", 3, "M03", 31], + ["M04", 50, "out-of-range day 50", 4, "M04", 30], + ["M05", 77, "out-of-range day 77", 5, "M05", 31], + ["M06", 33, "out-of-range date 06-33", 6, "M06", 30], + ["M07", 33, "out-of-range day 07-33", 7, "M07", 31], + ["M08", 300, "out-of-range day 300", 8, "M08", 31], + ["M09", 400, "out-of-range date 09-400", 9, "M09", 30], + ["M10", 400, "out-of-range date 10-400", 10, "M10", 31], + ["M11", 400, "out-of-range date 11-400", 11, "M11", 30], + ["M12", 500, "out-of-range day 500", 12, "M12", 31], +]; + +for (const [monthCode, day, descr, expectedMonth, expectedMonthCode, expectedDay] of testData) { + TemporalHelpers.assertPlainDate( + Temporal.PlainDate.from({year, monthCode, day}), + year, expectedMonth, expectedMonthCode, expectedDay, + `year/month code/day need to be constrained in ${descr}`); +} reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js index 10fb6a5e249f9..4fe341aedd8ef 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/limits.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// Copyright 2021 the V8 project authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -9,6 +9,8 @@ includes: [temporalHelpers.js] features: [Temporal] ---*/ +// Year limits + assert.throws(RangeError, () => new Temporal.PlainDate(-271821, 4, 18), "min"); assert.throws(RangeError, () => new Temporal.PlainDate(275760, 9, 14), "max"); TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-271821, 4, 19), @@ -16,4 +18,37 @@ TemporalHelpers.assertPlainDate(new Temporal.PlainDate(-271821, 4, 19), TemporalHelpers.assertPlainDate(new Temporal.PlainDate(275760, 9, 13), 275760, 9, "M09", 13, "max"); +// Monthday limits + +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 1, 31), 2021, 1, 'M01', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 2, 28), 2021, 2, 'M02', 28); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 3, 31), 2021, 3, 'M03', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 4, 30), 2021, 4, 'M04', 30); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 5, 31), 2021, 5, 'M05', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 6, 30), 2021, 6, 'M06', 30); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 7, 31), 2021, 7, 'M07', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 8, 31), 2021, 8, 'M08', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 9, 30), 2021, 9, 'M09', 30); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 10, 31), 2021, 10, 'M10', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 11, 30), 2021, 11, 'M11', 30); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 12, 31), 2021, 12, 'M12', 31); +TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2004, 2, 29), 2004, 2, 'M02', 29); +assert.throws(RangeError, () => new Temporal.PlainDate(1900, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2001, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2002, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2003, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2100, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 1, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 2, 29)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 3, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 4, 31)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 5, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 6, 31)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 8, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 9, 31)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 10, 32)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 11, 31)); +assert.throws(RangeError, () => new Temporal.PlainDate(2021, 12, 32)); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js index 86a92f87ef92c..118d91d673099 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/missing-arguments.js @@ -22,4 +22,8 @@ const args = [ assert.throws(RangeError, () => new Temporal.PlainDate(...args)); assert.compareArray(actual, expected, "order of operations"); +assert.throws(RangeError, () => new Temporal.PlainDate(), "no arguments"); +assert.throws(RangeError, () => new Temporal.PlainDate(2021), "only year"); + + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..d5f724f7de9a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.add +description: Maximum allowed duration adding to minimum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const min = Temporal.PlainDate.from({ year: -271821, month: 4, day: 19 }); + +const maxCases = [ + ["P547581Y4M25DT23H59M59.999999999S", "string with max years"], + [{ years: 547581, months: 4, days: 25, nanoseconds: 86399999999999 }, "property bag with max years"], + ["P6570976M25DT23H59M59.999999999S", "string with max months"], + [{ months: 6570976, days: 25, nanoseconds: 86399999999999 }, "property bag with max months"], + ["P28571428W5DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: 28_571_428, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"], + ["P200000001DT23H59M59.999999999S", "string with max days"], + [{ days: 200_000_001, nanoseconds: 86399999999999 }, "property bag with max days"], + ["PT4800000047H59M59.999999999S", "string with max hours"], + [{ hours: 4_800_000_047, minutes: 59, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max hours"], + ["PT288000002879M59.999999999S", "string with max minutes"], + [{ minutes: 288_000_002_879, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max minutes"], + ["PT17280000172799.999999998S", "string with max seconds"], + [{ seconds: 17_280_000_172_799, nanoseconds: 999999998 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = min.add(arg); + TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`); +} + +const max = Temporal.PlainDate.from({ year: 275760, month: 9, day: 13 }); + +const minCases = [ + ["-P547581Y4M24DT23H59M59.999999999S", "string with max years"], + [{ years: -547581, months: -4, days: -24, nanoseconds: -86399999999999 }, "property bag with max years"], + ["-P6570976M24DT23H59M59.999999999S", "string with max months"], + [{ months: -6570976, days: -24, nanoseconds: -86399999999999 }, "property bag with max months"], + ["-P28571428W5DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: -28_571_428, days: -5, nanoseconds: -86399999999999 }, "property bag with max weeks"], + ["-P200000001DT23H59M59.999999999S", "string with max days"], + [{ days: -200_000_001, nanoseconds: -86399999999999 }, "property bag with max days"], + ["-PT4800000047H59M59.999999999S", "string with max hours"], + [{ hours: -4_800_000_047, minutes: -59, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max hours"], + ["-PT288000002879M59.999999999S", "string with max minutes"], + [{ minutes: -288_000_002_879, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max minutes"], + ["-PT17280000172799.999999998S", "string with max seconds"], + [{ seconds: -17_280_000_172_799, nanoseconds: -999999998 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = max.add(arg); + TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js index 0a0cea79ca7d8..d6530b6c054f4 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/argument-string.js @@ -14,5 +14,6 @@ const result = instance.add("P3D"); TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 5); TemporalHelpers.assertPlainDate(instance.add("P1M1W"), 2000, 6, "M06", 9, "calendar units"); +TemporalHelpers.assertPlainDate(instance.add("-P1M1D"), 2000, 4, "M04", 1, "calendar units"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js index 0d757e712924a..2ab49b56e57b3 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/add/basic.js @@ -29,4 +29,90 @@ TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('1976-12-08').add({ days TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-02-28').add({ months: -1 }), 2019, 1, "M01", 28); +let p1y = new Temporal.Duration(1); +let p4y = new Temporal.Duration(4); +let p5m = new Temporal.Duration(0, 5); +let p1y2m = new Temporal.Duration(1, 2); +let p1y4d = new Temporal.Duration(1, 0, 0, 4); +let p1y2m4d = new Temporal.Duration(1, 2, 0, 4); +let p10d = new Temporal.Duration(0, 0, 0, 10); +let p1w = new Temporal.Duration(0, 0, 1); +let p6w = new Temporal.Duration(0, 0, 6); +let p2w3d = new Temporal.Duration(0, 0, 2, 3); +let p1y2w = new Temporal.Duration(1, 0, 2); +let p2m3w = new Temporal.Duration(0, 2, 3); + +let testData = [ + [ '2020-02-29', p1y, '2021-02-28' ], + [ '2020-02-29', p4y, '2024-02-29' ], + [ '2021-07-16', p1y, '2022-07-16' ], + [ '2021-07-16', p5m, '2021-12-16' ], + [ '2021-08-16', p5m, '2022-01-16' ], + [ '2021-10-31', p5m, '2022-03-31' ], + [ '2021-09-30', p5m, '2022-02-28' ], + [ '2019-09-30', p5m, '2020-02-29' ], + [ '2019-10-01', p5m, '2020-03-01' ], + [ '2021-07-16', p1y2m, '2022-09-16' ], + [ '2021-11-30', p1y2m, '2023-01-30' ], + [ '2021-12-31', p1y2m, '2023-02-28' ], + [ '2022-12-31', p1y2m, '2024-02-29' ], + [ '2021-07-16', p1y4d, '2022-07-20' ], + [ '2021-02-27', p1y4d, '2022-03-03' ], + [ '2023-02-27', p1y4d, '2024-03-02' ], + [ '2021-12-30', p1y4d, '2023-01-03' ], + [ '2021-07-30', p1y4d, '2022-08-03' ], + [ '2021-06-30', p1y4d, '2022-07-04' ], + [ '2021-07-16', p1y2m4d, '2022-09-20' ], + [ '2021-02-27', p1y2m4d, '2022-05-01' ], + [ '2021-02-26', p1y2m4d, '2022-04-30' ], + [ '2023-02-26', p1y2m4d, '2024-04-30' ], + [ '2021-12-30', p1y2m4d, '2023-03-04' ], + [ '2021-07-30', p1y2m4d, '2022-10-04' ], + [ '2021-06-30', p1y2m4d, '2022-09-03' ], + [ '2021-07-16', p10d, '2021-07-26' ], + [ '2021-07-26', p10d, '2021-08-05' ], + [ '2021-12-26', p10d, '2022-01-05' ], + [ '2020-02-26', p10d, '2020-03-07' ], + [ '2021-02-26', p10d, '2021-03-08' ], + [ '2020-02-19', p10d, '2020-02-29' ], + [ '2021-02-19', p10d, '2021-03-01' ], + [ '2021-02-19', p1w, '2021-02-26' ], + [ '2021-02-27', p1w, '2021-03-06' ], + [ '2020-02-27', p1w, '2020-03-05' ], + [ '2021-12-24', p1w, '2021-12-31' ], + [ '2021-12-27', p1w, '2022-01-03' ], + [ '2021-01-27', p1w, '2021-02-03' ], + [ '2021-06-27', p1w, '2021-07-04' ], + [ '2021-07-27', p1w, '2021-08-03' ], + [ '2021-02-19', p6w, '2021-04-02' ], + [ '2021-02-27', p6w, '2021-04-10' ], + [ '2020-02-27', p6w, '2020-04-09' ], + [ '2021-12-24', p6w, '2022-02-04' ], + [ '2021-12-27', p6w, '2022-02-07' ], + [ '2021-01-27', p6w, '2021-03-10' ], + [ '2021-06-27', p6w, '2021-08-08' ], + [ '2021-07-27', p6w, '2021-09-07' ], + [ '2020-02-29', p2w3d, '2020-03-17' ], + [ '2020-02-28', p2w3d, '2020-03-16' ], + [ '2021-02-28', p2w3d, '2021-03-17' ], + [ '2020-12-28', p2w3d, '2021-01-14' ], + [ '2020-02-29', p1y2w, '2021-03-14' ], + [ '2020-02-28', p1y2w, '2021-03-14' ], + [ '2021-02-28', p1y2w, '2022-03-14' ], + [ '2020-12-28', p1y2w, '2022-01-11' ], + [ '2020-02-29', p2m3w, '2020-05-20' ], + [ '2020-02-28', p2m3w, '2020-05-19' ], + [ '2021-02-28', p2m3w, '2021-05-19' ], + [ '2020-12-28', p2m3w, '2021-03-21' ], + [ '2019-12-28', p2m3w, '2020-03-20' ], + [ '2019-10-28', p2m3w, '2020-01-18' ], + [ '2019-10-31', p2m3w, '2020-01-21' ], +]; + +for (let [dateString, duration, resultString] of testData) { + const date = Temporal.PlainDate.from(dateString); + const result = Temporal.PlainDate.from(resultString); + TemporalHelpers.assertPlainDatesEqual(date.add(duration), result); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/basic.js new file mode 100644 index 0000000000000..22f9cb717eeaf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/day/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindate.prototype.day +description: The "day" property of Temporal.PlainDate.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).day, 15); +assert.sameValue(Temporal.PlainDate.from('2019-03-18').day, 18); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js index a0b4fc99672dc..f3186285dcda5 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js @@ -13,4 +13,19 @@ for (let i = 1; i <= 7; ++i) { assert.sameValue(plainDate.dayOfYear, 319 + i, `${plainDate} should be on day ${319 + i}`); } +assert.sameValue((new Temporal.PlainDate(1970, 1, 1)).dayOfYear, 1); +assert.sameValue((new Temporal.PlainDate(2000, 1, 1)).dayOfYear, 1); +assert.sameValue((new Temporal.PlainDate(2021, 1, 15)).dayOfYear, 15); +assert.sameValue((new Temporal.PlainDate(2020, 2, 15)).dayOfYear, 46); +assert.sameValue((new Temporal.PlainDate(2000, 2, 15)).dayOfYear, 46); +assert.sameValue((new Temporal.PlainDate(2020, 3, 15)).dayOfYear, 75); +assert.sameValue((new Temporal.PlainDate(2000, 3, 15)).dayOfYear, 75); +assert.sameValue((new Temporal.PlainDate(2001, 3, 15)).dayOfYear, 74); +assert.sameValue((new Temporal.PlainDate(2000, 12, 31)).dayOfYear, 366); +assert.sameValue((new Temporal.PlainDate(2001, 12, 31)).dayOfYear, 365); +assert.sameValue(Temporal.PlainDate.from('2019-01-18').dayOfYear, 18); +assert.sameValue(Temporal.PlainDate.from('2020-02-18').dayOfYear, 49); +assert.sameValue(Temporal.PlainDate.from('2019-12-31').dayOfYear, 365); +assert.sameValue(Temporal.PlainDate.from('2000-12-31').dayOfYear, 366); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js index 022b6d218a9b0..25cf809cc0f6d 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInMonth/basic.js @@ -13,9 +13,37 @@ const tests = [ [new Temporal.PlainDate(1976, 11, 18), 30], [new Temporal.PlainDate(1976, 12, 18), 31], [new Temporal.PlainDate(1977, 2, 18), 28], + [new Temporal.PlainDate(2021, 1, 15), 31], + [new Temporal.PlainDate(2020, 2, 15), 29], + [new Temporal.PlainDate(2000, 2, 15), 29], + [new Temporal.PlainDate(2021, 2, 15), 28], + [new Temporal.PlainDate(2021, 3, 15), 31], + [new Temporal.PlainDate(2021, 4, 15), 30], + [new Temporal.PlainDate(2021, 5, 15), 31], + [new Temporal.PlainDate(2021, 6, 15), 30], + [new Temporal.PlainDate(2021, 7, 15), 31], + [new Temporal.PlainDate(2021, 8, 15), 31], + [new Temporal.PlainDate(2021, 9, 15), 30], + [new Temporal.PlainDate(2021, 10, 15), 31], + [new Temporal.PlainDate(2021, 11, 15), 30], + [new Temporal.PlainDate(2021, 12, 15), 31], + [Temporal.PlainDate.from('2019-01-18'), 31], + [Temporal.PlainDate.from('2020-02-18'), 29], + [Temporal.PlainDate.from('2019-02-18'), 28], + [Temporal.PlainDate.from('2019-03-18'), 31], + [Temporal.PlainDate.from('2019-04-18'), 30], + [Temporal.PlainDate.from('2019-05-18'), 31], + [Temporal.PlainDate.from('2019-06-18'), 30], + [Temporal.PlainDate.from('2019-07-18'), 31], + [Temporal.PlainDate.from('2019-08-18'), 31], + [Temporal.PlainDate.from('2019-09-18'), 30], + [Temporal.PlainDate.from('2019-10-18'), 31], + [Temporal.PlainDate.from('2019-11-18'), 30], + [Temporal.PlainDate.from('2019-12-18'), 31], ]; for (const [plainDate, expected] of tests) { assert.sameValue(plainDate.daysInMonth, expected, `${expected} days in the month of ${plainDate}`); } + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js index 719a2fd52777d..b5cb842d4f523 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/daysInYear/basic.js @@ -10,5 +10,24 @@ features: [Temporal] assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).daysInYear, 366, "leap year"); assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).daysInYear, 365, "non-leap year"); +assert.sameValue((new Temporal.PlainDate(1995, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(1996, 7, 15)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDate(1997, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(1998, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(1999, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(2000, 7, 15)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDate(2001, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(2002, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(2003, 7, 15)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDate(2004, 7, 15)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDate(2005, 7, 15)).daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2019-03-18').daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2020-03-18').daysInYear, 366); +assert.sameValue(Temporal.PlainDate.from('2021-03-18').daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2022-03-18').daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2023-03-18').daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2024-03-18').daysInYear, 366); +assert.sameValue(Temporal.PlainDate.from('2025-03-18').daysInYear, 365); +assert.sameValue(Temporal.PlainDate.from('2026-03-18').daysInYear, 365); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js index 3ba80f54ace1b..1e98dd2cc27a3 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDate(2000, 5, 2); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.equals(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..ad250de5133c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.equals +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..5f315294f7ec0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js index 8a7aabb4e8b11..1df7550203268 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/inLeapYear/basic.js @@ -12,5 +12,24 @@ assert.sameValue((new Temporal.PlainDate(1976, 11, 18)).inLeapYear, true, "leap year"); assert.sameValue((new Temporal.PlainDate(1977, 11, 18)).inLeapYear, false, "non-leap year"); +assert.sameValue((new Temporal.PlainDate(1995, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(1996, 7, 15)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDate(1997, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(1998, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(1999, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(2000, 7, 15)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDate(2001, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(2002, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(2003, 7, 15)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDate(2004, 7, 15)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDate(2005, 7, 15)).inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2019-03-18').inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2020-03-18').inLeapYear, true); +assert.sameValue(Temporal.PlainDate.from('2021-03-18').inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2022-03-18').inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2023-03-18').inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2024-03-18').inLeapYear, true); +assert.sameValue(Temporal.PlainDate.from('2025-03-18').inLeapYear, false); +assert.sameValue(Temporal.PlainDate.from('2026-03-18').inLeapYear, false); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/basic.js new file mode 100644 index 0000000000000..8b3e294ba6a5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/month/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindate.prototype.month +description: Month property for PlainDate +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).month, 7); +assert.sameValue(Temporal.PlainDate.from('2019-03-15').month, 3); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/basic.js new file mode 100644 index 0000000000000..e9ad1680660ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthCode/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.monthcode +description: monthCode property for PlainDate +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).monthCode, 'M07'); +assert.sameValue(Temporal.PlainDate.from('2019-03-15').monthCode, 'M03'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js index e87dbbc43bf50..cf3be7bc31a5c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js @@ -10,5 +10,8 @@ features: [Temporal] const plainDate = new Temporal.PlainDate(1976, 11, 18); assert.sameValue(plainDate.monthsInYear, 12); +assert.sameValue((new Temporal.PlainDate(1234, 7, 15)).monthsInYear, 12); +assert.sameValue(Temporal.PlainDate.from('2019-03-18').monthsInYear, 12); +assert.sameValue(Temporal.PlainDate.from('1234-03-18').monthsInYear, 12); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-invalid-iso-string.js index 182db0f8386b6..aea9696e60334 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDate(2000, 5, 2); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.since(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..b3e17b37e7761 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic-arithmetic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic-arithmetic.js new file mode 100644 index 0000000000000..890f5e81f9bef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/basic-arithmetic.js @@ -0,0 +1,471 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: > + Check various basic calculations not involving leap years or constraining +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Years + +const date00011225 = Temporal.PlainDate.from("0001-12-25"); +const date19600216 = Temporal.PlainDate.from("1960-02-16"); +const date19600330 = Temporal.PlainDate.from("1960-03-30"); +const date19690724 = new Temporal.PlainDate(1969, 7, 24); +const date19970616 = Temporal.PlainDate.from("1997-06-16"); +const date19970716 = Temporal.PlainDate.from("1997-07-16"); +const date19971216 = Temporal.PlainDate.from("1997-12-16"); +const date19971230 = Temporal.PlainDate.from("1997-12-30"); +const date20110716 = Temporal.PlainDate.from("2011-07-16"); +const date20190101 = Temporal.PlainDate.from("2019-01-01"); +const date20190201 = Temporal.PlainDate.from("2019-02-01"); +const date20190724 = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 }); +const date20191230 = Temporal.PlainDate.from("2019-12-30"); +const date20200201 = Temporal.PlainDate.from("2020-02-01"); +const date20200315 = Temporal.PlainDate.from("2020-03-15"); +const date20200316 = Temporal.PlainDate.from("2020-03-16"); +const date20200330 = Temporal.PlainDate.from("2020-03-30"); +const date20201216 = Temporal.PlainDate.from("2020-12-16"); +const date20201230 = Temporal.PlainDate.from("2020-12-30"); +const date20210105 = Temporal.PlainDate.from("2021-01-05"); +const date20210107 = Temporal.PlainDate.from("2021-01-07"); +const date20210116 = Temporal.PlainDate.from("2021-01-16"); +const date20210201 = Temporal.PlainDate.from("2021-02-01"); +const date20210205 = Temporal.PlainDate.from("2021-02-05"); +const date20210228 = new Temporal.PlainDate(2021, 2, 28); +const date20210305 = Temporal.PlainDate.from("2021-03-05"); +const date20210307 = Temporal.PlainDate.from("2021-03-07"); +const date20210315 = Temporal.PlainDate.from("2021-03-15"); +const date20210330 = Temporal.PlainDate.from("2021-03-30"); +const date20210615 = Temporal.PlainDate.from("2021-06-15"); +const date20210713 = Temporal.PlainDate.from("2021-07-13"); +const date20210715 = Temporal.PlainDate.from("2021-07-15"); +const date20210716 = Temporal.PlainDate.from("2021-07-16"); +const date20210717 = Temporal.PlainDate.from("2021-07-17"); +const date20210723 = Temporal.PlainDate.from("2021-07-23"); +const date20210813 = Temporal.PlainDate.from("2021-08-13"); +const date20210816 = Temporal.PlainDate.from("2021-08-16"); +const date20210817 = Temporal.PlainDate.from("2021-08-17"); +const date20210916 = Temporal.PlainDate.from("2021-09-16"); +const date20210921 = Temporal.PlainDate.from("2021-09-21"); +const date20211017 = Temporal.PlainDate.from("2021-10-17"); +const date20220228 = new Temporal.PlainDate(2022, 2, 28); +const date20220716 = Temporal.PlainDate.from("2022-07-16"); +const date20220719 = Temporal.PlainDate.from("2022-07-19"); +const date20220919 = Temporal.PlainDate.from("2022-09-19"); +const date20221017 = Temporal.PlainDate.from("2022-10-17"); +const date20310716 = Temporal.PlainDate.from("2031-07-16"); +const date20311216 = Temporal.PlainDate.from("2031-12-16"); + +const tests = [ + [ + date20210716, date20210716, "same day", + ["years", 0, 0, 0, 0], + ["months", 0, 0, 0, 0], + ["weeks", 0, 0, 0, 0], + ["days", 0, 0, 0, 0], + ], + [ + date20210716, date20210717, "one day", + ["years", 0, 0, 0, 1], + ["months", 0, 0, 0, 1], + ["weeks", 0, 0, 0, 1], + ["days", 0, 0, 0, 1], + ], + [ + date20210716, date20210723, "7 days", + ["years", 0, 0, 0, 7], + ["months", 0, 0, 0, 7], + ["weeks", 0, 0, 1, 0], + ], + [ + date20210716, date20210817, "32 days", + ["years", 0, 1, 0, 1], + ["months", 0, 1, 0, 1], + ["weeks", 0, 0, 4, 4], + ["days", 0, 0, 0, 32], + ], + [ + date20210716, date20210916, "62 days", + ["years", 0, 2, 0, 0], + ["months", 0, 2, 0, 0], + ["weeks", 0, 0, 8, 6], + ["days", 0, 0, 0, 62], + ], + [ + date20210716, date20210813, "4 weeks", + ["years", 0, 0, 0, 28], + ["months", 0, 0, 0, 28], + ["weeks", 0, 0, 4, 0], + ["days", 0, 0, 0, 28], + ], + [ + date20210716, date20210816, "1 month in same year", + ["years", 0, 1, 0, 0], + ["months", 0, 1, 0, 0], + ["weeks", 0, 0, 4, 3], + ], + [ + date20210713, date20210816, "1 month and 3 days", + ["years", 0, 1, 0, 3], + ["months", 0, 1, 0, 3], + ["weeks", 0, 0, 4, 6], + ], + [ + date20201216, date20210116, "1 month in different year", + ["years", 0, 1, 0, 0], + ["months", 0, 1, 0, 0], + ], + [ + date20210105, date20210205, "1 month in same year", + ["years", 0, 1, 0, 0], + ["months", 0, 1, 0, 0], + ], + [ + date20210107, date20210307, "2 months in same year", + ["years", 0, 2, 0, 0], + ["months", 0, 2, 0, 0], + ], + [ + date20210716, date20210921, "2 months and 5 days", + ["years", 0, 2, 0, 5], + ["months", 0, 2, 0, 5], + ], + [ + date20210716, date20210817, "1 month and 1 day in a month with 31 days", + ["years", 0, 1, 0, 1], + ["months", 0, 1, 0, 1], + ["days", 0, 0, 0, 32], + ], + [ + date20210716, date20210813, "28 days across a month which has 31 days", + ["years", 0, 0, 0, 28], + ["months", 0, 0, 0, 28], + ["weeks", 0, 0, 4, 0], + ], + [ + date20210716, date20210916, "2 months which both have 31 days", + ["years", 0, 2, 0, 0], + ["months", 0, 2, 0, 0], + ["weeks", 0, 0, 8, 6], + ["days", 0, 0, 0, 62], + ], + [ + date20210716, date20220716, "1 year", + ["years", 1, 0, 0, 0], + ["months", 0, 12, 0, 0], + ["weeks", 0, 0, 52, 1], + ["days", 0, 0, 0, 365], + ], + [ + date20210716, date20220719, "1 year, 3 days", + ["years", 1, 0, 0, 3], + ["months", 0, 12, 0, 3], + ["weeks", 0, 0, 52, 4], + ["days", 0, 0, 0, 368], + ], + [ + date20210716, date20220919, "1 year, 2 months, 3 days", + ["years", 1, 2, 0, 3], + ["months", 0, 14, 0, 3], + ], + [ + date20210716, date20221017, "1 year, 3 months, 1 day", + ["years", 1, 3, 0, 1], + ["months", 0, 15, 0, 1], + ], + [ + date20200201, date20210201, "start of February", + ["years", 1, 0, 0, 0], + ["months", 0, 12, 0, 0], + ], + [ + date20210228, date20220228, "end of February", + ["years", 1, 0, 0, 0], + ["months", 0, 12, 0, 0], + ], + [ + date20190101, date20190201, "length of January 2019", + ["days", 0, 0, 0, 31], + ], + [ + date20210716, date20310716, "10 years", + ["years", 10, 0, 0, 0], + ["months", 0, 120, 0, 0], + ["weeks", 0, 0, 521, 5], + ["days", 0, 0, 0, 3652], + ], + [ + date20210716, date20311216, "10 years and 5 months", + ["years", 10, 5, 0, 0], + ["months", 0, 125, 0, 0], + ], + [ + date20210716, date20220719, "1 year and 3 days", + ["years", 1, 0, 0, 3], + ], + [ + date20210716, date20220919, "1 year 2 months and 3 days", + ["years", 1, 2, 0, 3], + ], + [ + date20210716, date20311216, "10 years and 5 months", + ["years", 10, 5, 0, 0], + ], + [ + date19971216, date20110716, "13 years and 7 months", + ["years", 13, 7, 0, 0], + ], + [ + date19971216, date20210716, "23 years and 7 months", + ["years", 23, 7, 0, 0], + ], + [ + date19970716, date20210716, "24 years", + ["years", 24, 0, 0, 0], + ], + [ + date19970716, date20210715, "23 years, 11 months and 29 days", + ["years", 23, 11, 0, 29], + ], + [ + date19970616, date20210615, "23 years, 11 months and 30 days", + ["years", 23, 11, 0, 30], + ], + [ + date19600216, date20200315, "60 years, 28 days", + ["years", 60, 0, 0, 28], + ], + [ + date19600216, date20200316, "60 years, 1 month", + ["years", 60, 1, 0, 0], + ], + [ + date20210330, date20210716, "3 months and 16 days", + ["years", 0, 3, 0, 16], + ], + [ + date20200330, date20210716, "1 year, 3 months and 16 days", + ["years", 1, 3, 0, 16], + ], + [ + date19600216, date20210315, "61 years, 27 days", + ["years", 61, 0, 0, 27], + ], + [ + date19600330, date20210716, "61 years, 3 months and 16 days", + ["years", 61, 3, 0, 16], + ], + [ + date20191230, date20210716, "1 year, 6 months and 16 days", + ["years", 1, 6, 0, 16], + ], + [ + date20201230, date20210716, "6 months and 16 days", + ["years", 0, 6, 0, 16], + ], + [ + date19971230, date20210716, "23 years, 6 months and 16 days", + ["years", 23, 6, 0, 16], + ], + [ + date00011225, date20210716, "2019 years, 6 months and 21 days", + ["years", 2019, 6, 0, 21], + ], + [ + date20191230, date20210305, "1 year, 2 months and 5 days", + ["years", 1, 2, 0, 5], + ], + [ + date19690724, date20190724, "crossing epoch", + ["years", 50, 0, 0, 0], + ], + [ + date20210717, date20210716, "negative one day", + ["years", 0, 0, 0, -1], + ["months", 0, 0, 0, -1], + ["weeks", 0, 0, 0, -1], + ["days", 0, 0, 0, -1], + ], + [ + date20210723, date20210716, "negative 7 days", + ["years", 0, 0, 0, -7], + ["months", 0, 0, 0, -7], + ["weeks", 0, 0, -1, 0], + ], + [ + date20210816, date20210716, "negative 1 month in same year", + ["years", 0, -1, 0, 0], + ["months", 0, -1, 0, 0], + ["weeks", 0, 0, -4, -3], + ], + [ + date20210116, date20201216, "negative 1 month in different year", + ["years", 0, -1, 0, 0], + ["months", 0, -1, 0, 0], + ], + [ + date20210205, date20210105, "negative 1 month in same year", + ["years", 0, -1, 0, 0], + ["months", 0, -1, 0, 0], + ], + [ + date20210817, date20210716, "negative 1 month and 1 day in a month with 31 days", + ["years", 0, -1, 0, -1], + ["months", 0, -1, 0, -1], + ["days", 0, 0, 0, -32], + ], + [ + date20210816, date20210713, "negative 1 month and 3 days", + ["years", 0, -1, 0, -3], + ["months", 0, -1, 0, -3], + ["weeks", 0, 0, -4, -6], + ], + [ + date20210813, date20210716, "negative 28 days across a month which has 31 days", + ["years", 0, 0, 0, -28], + ["months", 0, 0, 0, -28], + ["weeks", 0, 0, -4, 0], + ], + [ + date20210916, date20210716, "negative 2 months which both have 31 days", + ["years", 0, -2, 0, 0], + ["months", 0, -2, 0, 0], + ["weeks", 0, 0, -8, -6], + ["days", 0, 0, 0, -62], + ], + [ + date20210921, date20210716, "negative 2 months and 5 days", + ["years", 0, -2, 0, -5], + ["months", 0, -2, 0, -5], + ], + [ + date20210817, date20210716, "negative 32 days", + ["years", 0, -1, 0, -1], + ["months", 0, -1, 0, -1], + ["weeks", 0, 0, -4, -4], + ["days", 0, 0, 0, -32], + ], + [ + date20210916, date20210716, "negative 62 days", + ["years", 0, -2, 0, 0], + ["months", 0, -2, 0, 0], + ["weeks", 0, 0, -8, -6], + ["days", 0, 0, 0, -62], + ], + [ + date20220716, date20210716, "negative 1 year", + ["years", -1, 0, 0, 0], + ["months", 0, -12, 0, 0], + ["weeks", 0, 0, -52, -1], + ["days", 0, 0, 0, -365], + ], + [ + date20310716, date20210716, "negative 10 years", + ["years", -10, 0, 0, 0], + ["months", 0, -120, 0, 0], + ["weeks", 0, 0, -521, -5], + ["days", 0, 0, 0, -3652], + ], + [ + date20220719, date20210716, "negative 1 year and 3 days", + ["years", -1, 0, 0, -3], + ], + [ + date20220919, date20210716, "negative 1 year 2 months and 3 days", + ["years", -1, -2, 0, -3], + ], + [ + date20221017, date20210716, "negative 1 year, 3 months, and 1 day", + ["years", -1, -3, 0, -1], + ["months", 0, -15, 0, -1], + ], + [ + date20311216, date20210716, "negative 10 years and 5 months", + ["years", -10, -5, 0, 0], + ], + [ + date20110716, date19971216, "negative 13 years and 7 months", + ["years", -13, -7, 0, 0], + ], + [ + date20210716, date19971216, "negative 23 years and 7 months", + ["years", -23, -7, 0, 0], + ], + [ + date20210716, date19970716, "negative 24 years", + ["years", -24, 0, 0, 0], + ], + [ + date20210715, date19970716, "negative 23 years, 11 months and 30 days", + ["years", -23, -11, 0, -30], + ], + [ + date20210615, date19970616, "negative 23 years, 11 months and 29 days", + ["years", -23, -11, 0, -29], + ], + [ + date20200315, date19600216, "negative 60 years, 28 days", + ["years", -60, 0, 0, -28], + ], + [ + date20200316, date19600216, "negative 60 years, 1 month", + ["years", -60, -1, 0, 0], + ], + [ + date20210716, date20210330, "negative 3 months and 17 days", + ["years", 0, -3, 0, -17], + ], + [ + date20210716, date20200330, "negative 1 year, 3 months and 17 days", + ["years", -1, -3, 0, -17], + ], + [ + date20210716, date19600330, "negative 61 years, 3 months and 17 days", + ["years", -61, -3, 0, -17], + ], + [ + date20210716, date20191230, "negative 1 year, 6 months and 17 days", + ["years", -1, -6, 0, -17], + ], + [ + date20210716, date20201230, "negative 6 months and 17 days", + ["years", 0, -6, 0, -17], + ], + [ + date20210716, date19971230, "negative 23 years, 6 months and 17 days", + ["years", -23, -6, 0, -17], + ], + [ + date20210716, date00011225, "negative 2019 years, 6 months and 22 days", + ["years", -2019, -6, 0, -22], + ], + [ + date20210305, date20191230, "negative 1 year, 2 months and 6 days", + ["years", -1, -2, 0, -6], + ], + [ + date20190724, date19690724, "crossing epoch", + ["years", -50, 0, 0, 0], + ], +]; + +function neg(x) { + return (x === 0) ? x : -x; +} + +for (const [one, two, descr, ...units] of tests) { + for (const [largestUnit, years, months, weeks, days] of units) { + TemporalHelpers.assertDuration( + one.since(two, { largestUnit }), + neg(years), neg(months), neg(weeks), neg(days), 0, 0, 0, 0, 0, 0, + descr + ); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..f7c29c1feb4ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/throws-with-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/throws-with-time-units.js new file mode 100644 index 0000000000000..e38760b26a795 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/since/throws-with-time-units.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Throws if largestUnit or smallestUnit is a time unit +features: [Temporal] +---*/ + +const from = new Temporal.PlainDate(2021, 7, 16); +const to = new Temporal.PlainDate(2021, 7, 17); + +const units = ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']; + +for (const largestUnit of units) { + for (const smallestUnit of units) { + assert.throws(RangeError, () => from.since(to, { largestUnit, smallestUnit }), + `Can't use ${largestUnit} and ${smallestUnit} as largestUnit and smallestUnit for PlainDate`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..a1f6910764e12 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.subtract +description: Maximum allowed duration subtracting from maximum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const max = Temporal.PlainDate.from({ year: 275760, month: 9, day: 13 }); + +const maxCases = [ + ["P547581Y4M24DT23H59M59.999999999S", "string with max years"], + [{ years: 547581, months: 4, days: 24, nanoseconds: 86399999999999 }, "property bag with max years"], + ["P6570976M24DT23H59M59.999999999S", "string with max months"], + [{ months: 6570976, days: 24, nanoseconds: 86399999999999 }, "property bag with max months"], + ["P28571428W5DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: 28_571_428, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"], + ["P200000001DT23H59M59.999999999S", "string with max days"], + [{ days: 200_000_001, nanoseconds: 86399999999999 }, "property bag with max days"], + ["PT4800000047H59M59.999999999S", "string with max hours"], + [{ hours: 4_800_000_047, minutes: 59, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max hours"], + ["PT288000002879M59.999999999S", "string with max minutes"], + [{ minutes: 288_000_002_879, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max minutes"], + ["PT17280000172799.999999998S", "string with max seconds"], + [{ seconds: 17_280_000_172_799, nanoseconds: 999999998 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = max.subtract(arg); + TemporalHelpers.assertPlainDate(result, -271821, 4, "M04", 19, `operation succeeds with ${descr}`); +} + +const min = Temporal.PlainDate.from({ year: -271821, month: 4, day: 19 }); + +const minCases = [ + ["-P547581Y4M25DT23H59M59.999999999S", "string with max years"], + [{ years: -547581, months: -4, days: -25, nanoseconds: -86399999999999 }, "property bag with max years"], + ["-P6570976M25DT23H59M59.999999999S", "string with max months"], + [{ months: -6570976, days: -25, nanoseconds: -86399999999999 }, "property bag with max months"], + ["-P28571428W5DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: -28_571_428, days: -5, nanoseconds: -86399999999999 }, "property bag with max weeks"], + ["-P200000001DT23H59M59.999999999S", "string with max days"], + [{ days: -200_000_001, nanoseconds: -86399999999999 }, "property bag with max days"], + ["-PT4800000047H59M59.999999999S", "string with max hours"], + [{ hours: -4_800_000_047, minutes: -59, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max hours"], + ["-PT288000002879M59.999999999S", "string with max minutes"], + [{ minutes: -288_000_002_879, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max minutes"], + ["-PT17280000172799.999999998S", "string with max seconds"], + [{ seconds: -17_280_000_172_799, nanoseconds: -999999998 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = min.subtract(arg); + TemporalHelpers.assertPlainDate(result, 275760, 9, "M09", 13, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js index 168af12c461a4..e3bb12933b078 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js @@ -10,7 +10,7 @@ features: [Temporal] ---*/ const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 }); -const result = instance.subtract("P3D"); -TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 29); +TemporalHelpers.assertPlainDate(instance.subtract("P3D"), 2000, 4, "M04", 29); +TemporalHelpers.assertPlainDate(instance.subtract("-P3D"), 2000, 5, "M05", 5); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js index a7a928366b0d6..e7cf44cd76128 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/subtract/basic.js @@ -29,4 +29,90 @@ TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-10-29').subtract({ TemporalHelpers.assertPlainDate(Temporal.PlainDate.from('2019-01-28').subtract({ months: -1 }), 2019, 2, "M02", 28); +let p1y = new Temporal.Duration(1); +let p4y = new Temporal.Duration(4); +let p5m = new Temporal.Duration(0, 5); +let p1y2m = new Temporal.Duration(1, 2); +let p1y4d = new Temporal.Duration(1, 0, 0, 4); +let p1y2m4d = new Temporal.Duration(1, 2, 0, 4); +let p10d = new Temporal.Duration(0, 0, 0, 10); +let p1w = new Temporal.Duration(0, 0, 1); +let p6w = new Temporal.Duration(0, 0, 6); +let p2w3d = new Temporal.Duration(0, 0, 2, 3); +let p1y2w = new Temporal.Duration(1, 0, 2); +let p2m3w = new Temporal.Duration(0, 2, 3); + +let testData = [ + [ '2020-02-29', p1y, '2021-02-28' ], + [ '2020-02-29', p4y, '2024-02-29' ], + [ '2021-07-16', p1y, '2022-07-16' ], + [ '2021-07-16', p5m, '2021-12-16' ], + [ '2021-08-16', p5m, '2022-01-16' ], + [ '2021-10-31', p5m, '2022-03-31' ], + [ '2021-09-30', p5m, '2022-02-28' ], + [ '2019-09-30', p5m, '2020-02-29' ], + [ '2019-10-01', p5m, '2020-03-01' ], + [ '2021-07-16', p1y2m, '2022-09-16' ], + [ '2021-11-30', p1y2m, '2023-01-30' ], + [ '2021-12-31', p1y2m, '2023-02-28' ], + [ '2022-12-31', p1y2m, '2024-02-29' ], + [ '2021-07-16', p1y4d, '2022-07-20' ], + [ '2021-02-27', p1y4d, '2022-03-03' ], + [ '2023-02-27', p1y4d, '2024-03-02' ], + [ '2021-12-30', p1y4d, '2023-01-03' ], + [ '2021-07-30', p1y4d, '2022-08-03' ], + [ '2021-06-30', p1y4d, '2022-07-04' ], + [ '2021-07-16', p1y2m4d, '2022-09-20' ], + [ '2021-02-27', p1y2m4d, '2022-05-01' ], + [ '2021-02-26', p1y2m4d, '2022-04-30' ], + [ '2023-02-26', p1y2m4d, '2024-04-30' ], + [ '2021-12-30', p1y2m4d, '2023-03-04' ], + [ '2021-07-30', p1y2m4d, '2022-10-04' ], + [ '2021-06-30', p1y2m4d, '2022-09-03' ], + [ '2021-07-16', p10d, '2021-07-26' ], + [ '2021-07-26', p10d, '2021-08-05' ], + [ '2021-12-26', p10d, '2022-01-05' ], + [ '2020-02-26', p10d, '2020-03-07' ], + [ '2021-02-26', p10d, '2021-03-08' ], + [ '2020-02-19', p10d, '2020-02-29' ], + [ '2021-02-19', p10d, '2021-03-01' ], + [ '2021-02-19', p1w, '2021-02-26' ], + [ '2021-02-27', p1w, '2021-03-06' ], + [ '2020-02-27', p1w, '2020-03-05' ], + [ '2021-12-24', p1w, '2021-12-31' ], + [ '2021-12-27', p1w, '2022-01-03' ], + [ '2021-01-27', p1w, '2021-02-03' ], + [ '2021-06-27', p1w, '2021-07-04' ], + [ '2021-07-27', p1w, '2021-08-03' ], + [ '2021-02-19', p6w, '2021-04-02' ], + [ '2021-02-27', p6w, '2021-04-10' ], + [ '2020-02-27', p6w, '2020-04-09' ], + [ '2021-12-24', p6w, '2022-02-04' ], + [ '2021-12-27', p6w, '2022-02-07' ], + [ '2021-01-27', p6w, '2021-03-10' ], + [ '2021-06-27', p6w, '2021-08-08' ], + [ '2021-07-27', p6w, '2021-09-07' ], + [ '2020-02-29', p2w3d, '2020-03-17' ], + [ '2020-02-28', p2w3d, '2020-03-16' ], + [ '2021-02-28', p2w3d, '2021-03-17' ], + [ '2020-12-28', p2w3d, '2021-01-14' ], + [ '2020-02-29', p1y2w, '2021-03-14' ], + [ '2020-02-28', p1y2w, '2021-03-14' ], + [ '2021-02-28', p1y2w, '2022-03-14' ], + [ '2020-12-28', p1y2w, '2022-01-11' ], + [ '2020-02-29', p2m3w, '2020-05-20' ], + [ '2020-02-28', p2m3w, '2020-05-19' ], + [ '2021-02-28', p2m3w, '2021-05-19' ], + [ '2020-12-28', p2m3w, '2021-03-21' ], + [ '2019-12-28', p2m3w, '2020-03-20' ], + [ '2019-10-28', p2m3w, '2020-01-18' ], + [ '2019-10-31', p2m3w, '2020-01-21' ], +]; + +for (let [dateString, duration, resultString] of testData) { + const date = Temporal.PlainDate.from(dateString); + const result = Temporal.PlainDate.from(resultString); + TemporalHelpers.assertPlainDatesEqual(date.subtract(duration.negated()), result); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/no-observable-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/no-observable-array-iteration.js new file mode 100644 index 0000000000000..628bef5226c14 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/no-observable-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.tozoneddatetime +description: > + Calling GetPossibleEpochNanoseconds (from ToTemporalZonedDateTime > InterpretISODateTimeOffset) + causes no observable array iteration. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +let pd = new Temporal.PlainDate(2000, 1, 1); +let zdt = pd.toZonedDateTime("UTC"); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-invalid-iso-string.js index 47a8f7f92e0b5..73273675dd1fc 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDate(2000, 5, 2); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.until(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..087f47dab637e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic-arithmetic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic-arithmetic.js index 5513978bc1a32..66ab00412b17a 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic-arithmetic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/basic-arithmetic.js @@ -20,11 +20,13 @@ const date19970616 = Temporal.PlainDate.from("1997-06-16"); const date19970716 = Temporal.PlainDate.from("1997-07-16"); const date19971216 = Temporal.PlainDate.from("1997-12-16"); const date19971230 = Temporal.PlainDate.from("1997-12-30"); +const date20110716 = Temporal.PlainDate.from("2011-07-16"); const date20190101 = Temporal.PlainDate.from("2019-01-01"); const date20190201 = Temporal.PlainDate.from("2019-02-01"); const date20190724 = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 }); const date20191230 = Temporal.PlainDate.from("2019-12-30"); const date20200201 = Temporal.PlainDate.from("2020-02-01"); +const date20200315 = Temporal.PlainDate.from("2020-03-15"); const date20200316 = Temporal.PlainDate.from("2020-03-16"); const date20200330 = Temporal.PlainDate.from("2020-03-30"); const date20201216 = Temporal.PlainDate.from("2020-12-16"); @@ -37,8 +39,10 @@ const date20210205 = Temporal.PlainDate.from("2021-02-05"); const date20210228 = new Temporal.PlainDate(2021, 2, 28); const date20210305 = Temporal.PlainDate.from("2021-03-05"); const date20210307 = Temporal.PlainDate.from("2021-03-07"); +const date20210315 = Temporal.PlainDate.from("2021-03-15"); const date20210330 = Temporal.PlainDate.from("2021-03-30"); const date20210615 = Temporal.PlainDate.from("2021-06-15"); +const date20210713 = Temporal.PlainDate.from("2021-07-13"); const date20210715 = Temporal.PlainDate.from("2021-07-15"); const date20210716 = Temporal.PlainDate.from("2021-07-16"); const date20210717 = Temporal.PlainDate.from("2021-07-17"); @@ -47,10 +51,13 @@ const date20210813 = Temporal.PlainDate.from("2021-08-13"); const date20210816 = Temporal.PlainDate.from("2021-08-16"); const date20210817 = Temporal.PlainDate.from("2021-08-17"); const date20210916 = Temporal.PlainDate.from("2021-09-16"); +const date20210921 = Temporal.PlainDate.from("2021-09-21"); +const date20211017 = Temporal.PlainDate.from("2021-10-17"); const date20220228 = new Temporal.PlainDate(2022, 2, 28); const date20220716 = Temporal.PlainDate.from("2022-07-16"); const date20220719 = Temporal.PlainDate.from("2022-07-19"); const date20220919 = Temporal.PlainDate.from("2022-09-19"); +const date20221017 = Temporal.PlainDate.from("2022-10-17"); const date20310716 = Temporal.PlainDate.from("2031-07-16"); const date20311216 = Temporal.PlainDate.from("2031-12-16"); @@ -75,12 +82,39 @@ const tests = [ ["months", 0, 0, 0, 7], ["weeks", 0, 0, 1, 0], ], + [ + date20210716, date20210817, "32 days", + ["years", 0, 1, 0, 1], + ["months", 0, 1, 0, 1], + ["weeks", 0, 0, 4, 4], + ["days", 0, 0, 0, 32], + ], + [ + date20210716, date20210916, "62 days", + ["years", 0, 2, 0, 0], + ["months", 0, 2, 0, 0], + ["weeks", 0, 0, 8, 6], + ["days", 0, 0, 0, 62], + ], + [ + date20210716, date20210813, "4 weeks", + ["years", 0, 0, 0, 28], + ["months", 0, 0, 0, 28], + ["weeks", 0, 0, 4, 0], + ["days", 0, 0, 0, 28], + ], [ date20210716, date20210816, "1 month in same year", ["years", 0, 1, 0, 0], ["months", 0, 1, 0, 0], ["weeks", 0, 0, 4, 3], ], + [ + date20210713, date20210816, "1 month and 3 days", + ["years", 0, 1, 0, 3], + ["months", 0, 1, 0, 3], + ["weeks", 0, 0, 4, 6], + ], [ date20201216, date20210116, "1 month in different year", ["years", 0, 1, 0, 0], @@ -91,6 +125,16 @@ const tests = [ ["years", 0, 1, 0, 0], ["months", 0, 1, 0, 0], ], + [ + date20210107, date20210307, "2 months in same year", + ["years", 0, 2, 0, 0], + ["months", 0, 2, 0, 0], + ], + [ + date20210716, date20210921, "2 months and 5 days", + ["years", 0, 2, 0, 5], + ["months", 0, 2, 0, 5], + ], [ date20210716, date20210817, "1 month and 1 day in a month with 31 days", ["years", 0, 1, 0, 1], @@ -117,6 +161,23 @@ const tests = [ ["weeks", 0, 0, 52, 1], ["days", 0, 0, 0, 365], ], + [ + date20210716, date20220719, "1 year, 3 days", + ["years", 1, 0, 0, 3], + ["months", 0, 12, 0, 3], + ["weeks", 0, 0, 52, 4], + ["days", 0, 0, 0, 368], + ], + [ + date20210716, date20220919, "1 year, 2 months, 3 days", + ["years", 1, 2, 0, 3], + ["months", 0, 14, 0, 3], + ], + [ + date20210716, date20221017, "1 year, 3 months, 1 day", + ["years", 1, 3, 0, 1], + ["months", 0, 15, 0, 1], + ], [ date20200201, date20210201, "start of February", ["years", 1, 0, 0, 0], @@ -138,6 +199,11 @@ const tests = [ ["weeks", 0, 0, 521, 5], ["days", 0, 0, 0, 3652], ], + [ + date20210716, date20311216, "10 years and 5 months", + ["years", 10, 5, 0, 0], + ["months", 0, 125, 0, 0], + ], [ date20210716, date20220719, "1 year and 3 days", ["years", 1, 0, 0, 3], @@ -150,6 +216,10 @@ const tests = [ date20210716, date20311216, "10 years and 5 months", ["years", 10, 5, 0, 0], ], + [ + date19971216, date20110716, "13 years and 7 months", + ["years", 13, 7, 0, 0], + ], [ date19971216, date20210716, "23 years and 7 months", ["years", 23, 7, 0, 0], @@ -166,6 +236,10 @@ const tests = [ date19970616, date20210615, "23 years, 11 months and 30 days", ["years", 23, 11, 0, 30], ], + [ + date19600216, date20200315, "60 years, 28 days", + ["years", 60, 0, 0, 28], + ], [ date19600216, date20200316, "60 years, 1 month", ["years", 60, 1, 0, 0], @@ -178,6 +252,10 @@ const tests = [ date20200330, date20210716, "1 year, 3 months and 16 days", ["years", 1, 3, 0, 16], ], + [ + date19600216, date20210315, "61 years, 27 days", + ["years", 61, 0, 0, 27], + ], [ date19600330, date20210716, "61 years, 3 months and 16 days", ["years", 61, 3, 0, 16], @@ -241,6 +319,12 @@ const tests = [ ["months", 0, -1, 0, -1], ["days", 0, 0, 0, -32], ], + [ + date20210816, date20210713, "negative 1 month and 3 days", + ["years", 0, -1, 0, -3], + ["months", 0, -1, 0, -3], + ["weeks", 0, 0, -4, -6], + ], [ date20210813, date20210716, "negative 28 days across a month which has 31 days", ["years", 0, 0, 0, -28], @@ -254,6 +338,25 @@ const tests = [ ["weeks", 0, 0, -8, -6], ["days", 0, 0, 0, -62], ], + [ + date20210921, date20210716, "negative 2 months and 5 days", + ["years", 0, -2, 0, -5], + ["months", 0, -2, 0, -5], + ], + [ + date20210817, date20210716, "negative 32 days", + ["years", 0, -1, 0, -1], + ["months", 0, -1, 0, -1], + ["weeks", 0, 0, -4, -4], + ["days", 0, 0, 0, -32], + ], + [ + date20210916, date20210716, "negative 62 days", + ["years", 0, -2, 0, 0], + ["months", 0, -2, 0, 0], + ["weeks", 0, 0, -8, -6], + ["days", 0, 0, 0, -62], + ], [ date20220716, date20210716, "negative 1 year", ["years", -1, 0, 0, 0], @@ -276,10 +379,19 @@ const tests = [ date20220919, date20210716, "negative 1 year 2 months and 3 days", ["years", -1, -2, 0, -3], ], + [ + date20221017, date20210716, "negative 1 year, 3 months, and 1 day", + ["years", -1, -3, 0, -1], + ["months", 0, -15, 0, -1], + ], [ date20311216, date20210716, "negative 10 years and 5 months", ["years", -10, -5, 0, 0], ], + [ + date20110716, date19971216, "negative 13 years and 7 months", + ["years", -13, -7, 0, 0], + ], [ date20210716, date19971216, "negative 23 years and 7 months", ["years", -23, -7, 0, 0], @@ -296,6 +408,10 @@ const tests = [ date20210615, date19970616, "negative 23 years, 11 months and 29 days", ["years", -23, -11, 0, -29], ], + [ + date20200315, date19600216, "negative 60 years, 28 days", + ["years", -60, 0, 0, -28], + ], [ date20200316, date19600216, "negative 60 years, 1 month", ["years", -60, -1, 0, 0], diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..a202dbd3a498b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDate(2000, 5, 2) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/throws-with-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/throws-with-time-units.js new file mode 100644 index 0000000000000..44d63b0bf7ad8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/until/throws-with-time-units.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Throws if largestUnit or smallestUnit is a time unit +features: [Temporal] +---*/ + +const from = new Temporal.PlainDate(2021, 7, 16); +const to = new Temporal.PlainDate(2021, 7, 17); + +const units = ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond']; + +for (const largestUnit of units) { + for (const smallestUnit of units) { + assert.throws(RangeError, () => from.until(to, { largestUnit, smallestUnit }), + `Can't use ${largestUnit} and ${smallestUnit} as largestUnit and smallestUnit for PlainDate`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js index 4d1e4226904e1..1375c31752943 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js @@ -23,6 +23,8 @@ const tests = [ [Symbol(), "symbol"], [42, "number"], [42n, "bigint"], + [NaN, "NaN"], + [Infinity, "Infinity"], // Step 4. // RejectObjectWithCalendarOrTimeZone step 2. @@ -37,10 +39,10 @@ const tests = [ // RejectObjectWithCalendarOrTimeZone step 5-6. [{ year: 2021, timeZone: "UTC" }, "timeZone"], - // Step 7. + // Step 6. [{}, "empty object"], + [[], "array"], [{ months: 12 }, "only plural property"], - ]; for (const [value, message = String(value)] of tests) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/time-units-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/time-units-ignored.js new file mode 100644 index 0000000000000..a3a4a64165583 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/with/time-units-ignored.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.with +description: Time units in the property bag are ignored +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let d1 = new Temporal.PlainDate(1911, 10, 10); +TemporalHelpers.assertPlainDate(d1.with({ + year: 2021, + hour: 30 +}), 2021, 10, 'M10', 10); +TemporalHelpers.assertPlainDate(d1.with({ + month: 11, + minute: 71 +}), 1911, 11, 'M11', 10); +TemporalHelpers.assertPlainDate(d1.with({ + monthCode: 'M05', + second: 90 +}), 1911, 5, 'M05', 10); +TemporalHelpers.assertPlainDate(d1.with({ + day: 30, + era: 'BC' +}), 1911, 10, 'M10', 30); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-invalid-iso-string.js index 6541c80ecbe3a..a524d5f240a35 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-invalid-iso-string.js @@ -12,6 +12,7 @@ const instance = new Temporal.PlainDate(1976, 11, 18, "iso8601"); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..978efac17399b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.withcalendar +description: A time string is valid input for Calendar; default is iso8601 +features: [Temporal] +---*/ + +const tests = [ + "15:23", + "15:23:30", + "15:23:30.123", + "15:23:30.123456", + "15:23:30.123456789", + "1976-11-18T15:23:30.1", + "1976-11-18T15:23:30.12", + "1976-11-18T15:23:30.123", + "1976-11-18T15:23:30.1234", + "1976-11-18T15:23:30.12345", + "1976-11-18T15:23:30.123456", + "1976-11-18T15:23:30.1234567", + "1976-11-18T15:23:30.12345678", + "1976-11-18T15:23:30.123456789", + "1976-11-18T15:23:30,12", + "1976-11-18T15:23:30.12-02:00", + "152330", + "152330.1", + "152330-08", + "152330.1-08", + "152330-0800", + "152330.1-0800", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "15", + "T15:23:30", + "t152330", +]; + +const instance = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18}); + +tests.forEach((arg) => { + const result = instance.withCalendar(arg); + assert.sameValue(result.calendarId, "iso8601", `Calendar created from string "${arg}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/basic.js new file mode 100644 index 0000000000000..9563f1f05cb2a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDate/prototype/year/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindate.prototype.year +description: The "year" property of Temporal.PlainDate.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).year, 2021); +assert.sameValue(Temporal.PlainDate.from('2019-03-15').year, 2019); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/argument-convert.js new file mode 100644 index 0000000000000..5fe0d639b90d8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/argument-convert.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime +description: PlainDateTime constructor with non-integer arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2020.6, 11.7, 24.1, 11.9, 12.8, 13.7, 14.6, 15.5, 1.999999), + 2020, 11, "M11", 24, 11, 12, 13, 14, 15, 1, "positive fractional"); + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(-2020.6, 11.7, 24.1, 11.9, 12.8, 13.7, 14.6, 15.5, 1.999999), + -2020, 11, "M11", 24, 11, 12, 13, 14, 15, 1, "negative fractional"); + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(null, 11, 24, 1, 2, 3, 4, 5, 6), + 0, 11, "M11", 24, 1, 2, 3, 4, 5, 6, "null defaults to zero"); + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(false, 11, 24, true), + 0, 11, "M11", 24, 1, 0, 0, 0, 0, 0, "boolean defaults"); + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2020, 11, 24, undefined), + 2020, 11, "M11", 24, 0, 0, 0, 0, 0, 0, "undefined hour defaults to 0"); + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime("2020.6", "11.7", "24.1", "11.9", "12.8", "13.7", "14.6", "15.5", "1.999999"), + 2020, 11, "M11", 24, 11, 12, 13, 14, 15, 1, "fractional strings"); + +for (const invalid of [Symbol(), 1n]) { + assert.throws(TypeError, () => new Temporal.PlainDateTime(invalid, 11, 24), `year ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainDateTime(2020, invalid, 24), `month ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainDateTime(2020, 11, invalid), `day ${typeof invalid}`); +} + +for (const invalid of [undefined, "invalid"]) { + assert.throws(RangeError, () => new Temporal.PlainDateTime(invalid, 11, 24), `year ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainDateTime(2020, invalid, 24), `month ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainDateTime(2020, 11, invalid), `day ${typeof invalid}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/basic.js new file mode 100644 index 0000000000000..7070d26b61875 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/basic.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.constructor +description: basic tests for the PlainDateTime constructor +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7, 8, 9), 1, 2, 'M02', 3, 4, 5, 6, 7, 8, 9); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7, 8), 1, 2, 'M02', 3, 4, 5, 6, 7, 8, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7), 1, 2, 'M02', 3, 4, 5, 6, 7, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6), 1, 2, 'M02', 3, 4, 5, 6, 0, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5), 1, 2, 'M02', 3, 4, 5, 0, 0, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4), 1, 2, 'M02', 3, 4, 0, 0, 0, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3), 1, 2, 'M02', 3, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(-25406, 1, 1), -25406, 1, 'M01', 1, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(29345, 12, 31, 23, 59, 59, 999, 999, 999), 29345, 12, 'M12', 31, 23, 59, 59, 999, 999, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-invalid-iso-string.js index 8c550ae0a6f20..7477c1d3f4f3c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/calendar-invalid-iso-string.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.plaindatetime.constructor -description: Various invalid ISO string values for calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], ["1997-12-04[u-ca=iso8601]", "ISO string with calendar annotation"], + ["notacal", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js index c1c7a62f2f962..fc2bb67b245ff 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js @@ -10,10 +10,12 @@ features: [Temporal] const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; - -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..b07a06692ce68 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.compare +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(1976, 11, 18)), + `${description} is not a valid calendar ID (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(1976, 11, 18), arg), + `${description} is not a valid calendar ID (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-invalid.js new file mode 100644 index 0000000000000..5018e886ae69b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/argument-string-invalid.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.compare +description: Throws RangeError when the argument is an invalid string +features: [Temporal] +---*/ + +let t1 = new Temporal.PlainDateTime(2021, 3, 14, 1, 2, 3, 4, 5, 6); +assert.throws(RangeError, () => Temporal.PlainDateTime.compare(t1, 'invalid iso8601 string'), "invalid string in second argument"); +assert.throws(RangeError, () => Temporal.PlainDateTime.compare('invalid iso8601 string', t1), "invalid string in first argument"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js index dfd83e2876847..67c31447c8732 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/cast.js @@ -11,6 +11,8 @@ features: [Temporal] const dt1 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); const dt2 = new Temporal.PlainDateTime(2019, 10, 29, 10, 46, 38, 271, 986, 102); +// outcome smaller + assert.sameValue( Temporal.PlainDateTime.compare({ year: 1976, month: 11, day: 18, hour: 15 }, dt2), -1, @@ -35,4 +37,77 @@ assert.sameValue( "casts second argument (string)" ); +// outcome equal + +assert.sameValue( + Temporal.PlainDateTime.compare({ + year: 2019, + month: 10, + day: 29, + hour: 10, + minute: 46, + second: 38, + millisecond: 271, + microsecond: 986, + nanosecond: 102 + }, dt2), + 0, + "casts first argument (plain object)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare("2019-10-29T10:46:38,271986102", dt2), + 0, + "casts first argument (string)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare(dt2, + { + year: 2019, + month: 10, + day: 29, + hour: 10, + minute: 46, + second: 38, + millisecond: 271, + microsecond: 986, + nanosecond: 102 + }), + 0, + "casts second argument (plain object)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare(dt2, "2019-10-29T10:46:38,271986102"), + 0, + "casts second argument (string)" +); + +// larger + +assert.sameValue( + Temporal.PlainDateTime.compare({ year: 2025, month: 11, day: 18, hour: 15 }, dt2), + 1, + "casts first argument (plain object)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare("2025-11-18T15:23:30.123456789", dt2), + 1, + "casts first argument (string)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare(dt1, { year: 1800, month: 10, day: 29, hour: 10 }), + 1, + "casts second argument (plain object)" +); + +assert.sameValue( + Temporal.PlainDateTime.compare(dt2, "2019-10-29T10:46:38,271986101"), + 1, + "casts second argument (string)" +); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..6daed8e42a3c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDateTime.compare(arg, new Temporal.PlainDateTime(2025, 4, 3, 20, 4, 3)), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainDateTime.compare(new Temporal.PlainDateTime(2025, 4, 3, 20, 4, 3), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-invalid-iso-string.js index b8a1b50b72dfe..da6835e721458 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-invalid-iso-string.js @@ -4,21 +4,23 @@ /*--- esid: sec-temporal.plaindatetime.from -description: Various invalid ISO string values for calendar in a property bag +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ - ["", "empty string"], + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; - assert.throws( - RangeError, - () => Temporal.PlainDateTime.from(arg), - `${description} is not a valid calendar ID` - ); +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; + assert.throws( + RangeError, + () => Temporal.PlainDateTime.from(arg), + `${description} is not a valid calendar ID` + ); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..73cd4e64f18f9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDateTime.from(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js index 0d086de776250..6f0db9c0b1950 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js @@ -49,6 +49,14 @@ const invalidStrings = [ // valid, but outside the supported range: "-999999-01-01", "+999999-01-01", + // "00:0000" is invalid (the hour/minute and minute/second separator + // or lack thereof needs to match). + "2025-01-01T00:00:00+00:0000", + "2025-01-01T00:00:00+0000:00", + "202501-01T00:00:00", + "2025-0101T00:00:00", + "2025-01-01T00:0000", + "2025-01-01T0000:00", ]; invalidStrings.forEach((s) => { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js index 13deec81ceb51..53937e7b4eefb 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.PlainDateTime.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -25,6 +27,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.PlainDateTime.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.PlainDateTime.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -36,6 +46,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.PlainDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.PlainDateTime.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/month-code-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/month-code-wrong-type.js new file mode 100644 index 0000000000000..985d72eaad62c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/month-code-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: Month code must be a string +features: [Temporal] +---*/ + +const monthCodeValues = [ + 5, 5n, false, Symbol(), null, { toString: () => 5 } +]; + +const year = 2026; + +for (const monthCode of monthCodeValues) { + assert.throws(TypeError, () => Temporal.PlainDateTime.from({ + year, + monthCode, + day: 1, hour: 12, minute: 34 + }), typeof monthCode === 'symbol' ? + "Symbol should be rejected as month code" : + `month code ${monthCode} should be rejected`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/negative-month-or-day.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/negative-month-or-day.js new file mode 100644 index 0000000000000..3931643657f2f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/negative-month-or-day.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: Months and days must be non-negative integers +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.PlainDateTime.from({ year: 2000, day: 1, month: -1 })); +assert.throws(RangeError, () => Temporal.PlainDateTime.from({ year: 2000, month: 1, day: -1 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..3f338a10fabe9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainDateTime.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js index 02e8c067e47d5..33daf588ad55f 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/options-wrong-type.js @@ -15,6 +15,9 @@ const badOptions = [ Symbol(), 1, 2n, + Infinity, + NaN, + null, ]; for (const value of badOptions) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/roundtrip-from-property-bag.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/roundtrip-from-property-bag.js index cddfe41cafb8e..dc8bd625296c9 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/roundtrip-from-property-bag.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/from/roundtrip-from-property-bag.js @@ -13,20 +13,30 @@ features: [Temporal, Intl.Era-monthcode] const options = { overflow: "reject" }; -testRoundtrip(2000); -testRoundtrip(1); +const testData = [ + [2000, 1, "M01", 1], + [1, 1, "M01", 1], + [2021, 7, "M07", 15], + [2021, 7, "M07", 3], + [2021, 12, "M12", 31], + [2021, 7, "M07", 15], +]; -function testRoundtrip(year) { - const dateFromYearMonth = Temporal.PlainDateTime.from({ year, month: 1, day: 1, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }, options); +for (const [year, month, monthCode, day] of testData) { + testRoundtrip(year, month, monthCode, day); +} + +function testRoundtrip(year, month, monthCode, day) { + const dateFromYearMonth = Temporal.PlainDateTime.from({ year, month, day, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }, options); TemporalHelpers.assertPlainDateTime( dateFromYearMonth, - year, 1, "M01", 1, 12, 34, 56, 987, 654, 321, + year, month, monthCode, day, 12, 34, 56, 987, 654, 321, `${dateFromYearMonth} - created from year and month`); - const dateFromYearMonthCode = Temporal.PlainDateTime.from({ year, monthCode: "M01", day: 1, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }, options); + const dateFromYearMonthCode = Temporal.PlainDateTime.from({ year, monthCode, day, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }, options); TemporalHelpers.assertPlainDateTime( dateFromYearMonthCode, - year, 1, "M01", 1, 12, 34, 56, 987, 654, 321, + year, month, monthCode, day, 12, 34, 56, 987, 654, 321, `${dateFromYearMonthCode} - created from year and month code`); } diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js index a87d6ebe4c201..fad5f259c224d 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/missing-arguments.js @@ -22,4 +22,8 @@ const args = [ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args)); assert.compareArray(actual, expected, "order of operations"); +assert.throws(RangeError, () => new Temporal.PlainDateTime()); +assert.throws(RangeError, () => new Temporal.PlainDateTime(2021)); +assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7)); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..143cda09af82c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/add/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.add +description: Maximum allowed duration adding to minimum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const min = Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19, nanosecond: 1 }); + +const maxCases = [ + ["P547581Y4M24DT23H59M59.999999999S", "string with max years"], + [{ years: 547581, months: 4, days: 24, nanoseconds: 86399999999999 }, "property bag with max years"], + ["P6570976M24DT23H59M59.999999999S", "string with max months"], + [{ months: 6570976, days: 24, nanoseconds: 86399999999999 }, "property bag with max months"], + ["P28571428W4DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: 28_571_428, days: 4, nanoseconds: 86399999999999 }, "property bag with max weeks"], + ["P200000000DT23H59M59.999999999S", "string with max days"], + [{ days: 200_000_000, nanoseconds: 86399999999999 }, "property bag with max days"], + ["PT4800000023H59M59.999999999S", "string with max hours"], + [{ hours: 4_800_000_023, minutes: 59, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max hours"], + ["PT288000001439M59.999999999S", "string with max minutes"], + [{ minutes: 288_000_001_439, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max minutes"], + ["PT17280000086399.999999999S", "string with max seconds"], + [{ seconds: 17_280_000_086_399, nanoseconds: 999999999 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = min.add(arg); + TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`); +} + +const max = Temporal.PlainDateTime.from({ year: 275760, month: 9, day: 13 }); + +const minCases = [ + ["-P547581Y4M23DT23H59M59.999999999S", "string with max years"], + [{ years: -547581, months: -4, days: -23, nanoseconds: -86399999999999 }, "property bag with max years"], + ["-P6570976M23DT23H59M59.999999999S", "string with max months"], + [{ months: -6570976, days: -23, nanoseconds: -86399999999999 }, "property bag with max months"], + ["-P28571428W4DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: -28_571_428, days: -4, nanoseconds: -86399999999999 }, "property bag with max weeks"], + ["-P200000000DT23H59M59.999999999S", "string with max days"], + [{ days: -200_000_000, nanoseconds: -86399999999999 }, "property bag with max days"], + ["-PT4800000023H59M59.999999999S", "string with max hours"], + [{ hours: -4_800_000_023, minutes: -59, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max hours"], + ["-PT288000001439M59.999999999S", "string with max minutes"], + [{ minutes: -288_000_001_439, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max minutes"], + ["-PT17280000086399.999999999S", "string with max seconds"], + [{ seconds: -17_280_000_086_399, nanoseconds: -999999999 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = max.add(arg); + TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/basic.js new file mode 100644 index 0000000000000..fb5de03ac699e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/day/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindatetime.prototype.day +description: The "day" property of Temporal.PlainDateTime.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDateTime(2021, 7, 15, 5, 30, 13)).day, 15); +assert.sameValue(Temporal.PlainDateTime.from('2019-03-18T05:30:13').day, 18); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js index 8636a89a32daf..ff8430353a898 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/basic.js @@ -11,4 +11,17 @@ features: [Temporal] const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, "iso8601"); assert.sameValue(datetime.dayOfYear, 323, "check day of year information"); +assert.sameValue((new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)).dayOfYear, 23); +assert.sameValue((new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)).dayOfYear, 54); +assert.sameValue((new Temporal.PlainDateTime(1996, 3, 23, 5, 30, 13)).dayOfYear, 83); +assert.sameValue((new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)).dayOfYear, 82); +assert.sameValue((new Temporal.PlainDateTime(1997, 12, 31, 5, 30, 13)).dayOfYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1996, 12, 31, 5, 30, 13)).dayOfYear, 366); +assert.sameValue(Temporal.PlainDateTime.from("1997-01-23T05:30:13").dayOfYear, 23); +assert.sameValue(Temporal.PlainDateTime.from("1997-02-23T05:30:13").dayOfYear, 54); +assert.sameValue(Temporal.PlainDateTime.from("1996-03-23T05:30:13").dayOfYear, 83); +assert.sameValue(Temporal.PlainDateTime.from("1997-03-23T05:30:13").dayOfYear, 82); +assert.sameValue(Temporal.PlainDateTime.from("1997-12-31T05:30:13").dayOfYear, 365); +assert.sameValue(Temporal.PlainDateTime.from("1996-12-31T05:30:13").dayOfYear, 366); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js index bfa4e7afc4896..40b0cf9e5111c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/basic.js @@ -9,13 +9,29 @@ features: [Temporal] ---*/ const tests = [ - [new Temporal.PlainDateTime(1976, 2, 18, 15, 23, 30, 123, 456, 789), 29], - [new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789), 30], - [new Temporal.PlainDateTime(1976, 12, 18, 15, 23, 30, 123, 456, 789), 31], - [new Temporal.PlainDateTime(1977, 2, 18, 15, 23, 30, 123, 456, 789), 28], + [1976, 2, 18, 15, 23, 30, 29], + [1976, 11, 18, 15, 23, 30, 30], + [1976, 12, 18, 15, 23, 30, 31], + [1977, 2, 18, 15, 23, 30, 28], + [1997, 1, 23, 5, 30, 13, 31], + [1996, 2, 23, 5, 30, 13, 29], + [2000, 2, 23, 5, 30, 13, 29], + [1997, 2, 23, 5, 30, 13, 28], + [1997, 3, 23, 5, 30, 13, 31], + [1997, 4, 23, 5, 30, 13, 30], + [1997, 5, 23, 5, 30, 13, 31], + [1997, 6, 23, 5, 30, 13, 30], + [1997, 7, 23, 5, 30, 13, 31], + [1997, 8, 23, 5, 30, 13, 31], + [1997, 9, 23, 5, 30, 13, 30], + [1997, 10, 23, 5, 30, 13, 31], + [1997, 11, 23, 5, 30, 13, 30], + [1997, 12, 23, 5, 30, 13, 31], ]; -for (const [plainDateTime, expected] of tests) { +for (const [year, month, day, hour, minute, second, expected] of tests) { + const plainDateTime = new Temporal.PlainDateTime(year, month, day, hour, minute, second, 123, 456, 789); assert.sameValue(plainDateTime.daysInMonth, expected, `${expected} days in the month of ${plainDateTime}`); } + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js index de422d729bb24..88ed5421c45c8 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/daysInYear/basic.js @@ -12,5 +12,16 @@ assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 366, "leap year"); assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).daysInYear, 365, "non-leap year"); +assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).daysInYear, 365); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js index b112c99a59044..46ebce87f31ae 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 32 const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.equals(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..8c6fc8f8b611d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.equals +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..e48fd4d1b5ec8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js index a6b88825c3399..368759fab929d 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/basic.js @@ -12,5 +12,16 @@ assert.sameValue((new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, true, "leap year"); assert.sameValue((new Temporal.PlainDateTime(1977, 11, 18, 15, 23, 30, 123, 456, 789)).inLeapYear, false, "non-leap year"); +assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).inLeapYear, false); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/basic.js new file mode 100644 index 0000000000000..897e727fcb8ae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/month/basic.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindate.prototype.month +description: Month property for PlainDateTime +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).month, 8); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/basic.js new file mode 100644 index 0000000000000..86b2f455ae303 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthCode/basic.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.monthcode +description: monthCode property for PlainDateTime +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).monthCode, 'M08'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js index 82d112afd06f8..545bc2c61d365 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/basic.js @@ -10,5 +10,6 @@ features: [Temporal] const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, "iso8601"); assert.sameValue(datetime.monthsInYear, 12, "check months in year information"); +assert.sameValue((new Temporal.PlainDateTime(1234, 8, 23, 5, 30, 13)).monthsInYear, 12); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js index 3e44f90b21d17..1e2881a1d85f8 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 32 const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.since(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..97f269232444b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..c006edfeda559 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/since/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..70bbece38f52c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.subtract +description: Maximum allowed duration subtracting from maximum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const max = Temporal.PlainDateTime.from({ year: 275760, month: 9, day: 13 }); + +const maxCases = [ + ["P547581Y4M23DT23H59M59.999999999S", "string with max years"], + [{ years: 547581, months: 4, days: 23, nanoseconds: 86399999999999 }, "property bag with max years"], + ["P6570976M23DT23H59M59.999999999S", "string with max months"], + [{ months: 6570976, days: 23, nanoseconds: 86399999999999 }, "property bag with max months"], + ["P28571428W4DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: 28_571_428, days: 4, nanoseconds: 86399999999999 }, "property bag with max weeks"], + ["P200000000DT23H59M59.999999999S", "string with max days"], + [{ days: 200_000_000, nanoseconds: 86399999999999 }, "property bag with max days"], + ["PT4800000023H59M59.999999999S", "string with max hours"], + [{ hours: 4_800_000_023, minutes: 59, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max hours"], + ["PT288000001439M59.999999999S", "string with max minutes"], + [{ minutes: 288_000_001_439, seconds: 59, milliseconds: 999, microseconds: 999, nanoseconds: 999 }, "property bag with max minutes"], + ["PT17280000086399.999999999S", "string with max seconds"], + [{ seconds: 17_280_000_086_399, nanoseconds: 999999999 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = max.subtract(arg); + TemporalHelpers.assertPlainDateTime(result, -271821, 4, "M04", 19, 0, 0, 0, 0, 0, 1, `operation succeeds with ${descr}`); +} + +const min = Temporal.PlainDateTime.from({ year: -271821, month: 4, day: 19, nanosecond: 1 }); + +const minCases = [ + ["-P547581Y4M24DT23H59M59.999999999S", "string with max years"], + [{ years: -547581, months: -4, days: -24, nanoseconds: -86399999999999 }, "property bag with max years"], + ["-P6570976M24DT23H59M59.999999999S", "string with max months"], + [{ months: -6570976, days: -24, nanoseconds: -86399999999999 }, "property bag with max months"], + ["-P28571428W4DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: -28_571_428, days: -4, nanoseconds: -86399999999999 }, "property bag with max weeks"], + ["-P200000000DT23H59M59.999999999S", "string with max days"], + [{ days: -200_000_000, nanoseconds: -86399999999999 }, "property bag with max days"], + ["-PT4800000023H59M59.999999999S", "string with max hours"], + [{ hours: -4_800_000_023, minutes: -59, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max hours"], + ["-PT288000001439M59.999999999S", "string with max minutes"], + [{ minutes: -288_000_001_439, seconds: -59, milliseconds: -999, microseconds: -999, nanoseconds: -999 }, "property bag with max minutes"], + ["-PT17280000086399.999999999S", "string with max seconds"], + [{ seconds: -17_280_000_086_399, nanoseconds: -999999999 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = min.subtract(arg); + TemporalHelpers.assertPlainDateTime(result, 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/basic.js new file mode 100644 index 0000000000000..8bf07f0a56793 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/basic.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.withplaindate +description: basic tests for toPlainDate +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let d1 = new Temporal.PlainDateTime(2021, 12, 11, 1, 2, 3, 4, 5, 6); +TemporalHelpers.assertPlainDate(d1.toPlainDate(), 2021, 12, 'M12', 11); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js index 41eb31d42d7dd..2bd6bb10273f9 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-number.js @@ -17,6 +17,8 @@ assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04 "pads parts with 0"); assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1976-11-18T15:23:30", "truncates 4 decimal places to 0"); +assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 1 }), "1976-11-18T15:23:00.0", + "pads zero seconds to 1 decimal place"); assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:00.00", "pads zero seconds to 2 decimal places"); assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18T15:23:30.00", @@ -25,6 +27,10 @@ assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1976-11-18 "truncates 4 decimal places to 2"); assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1976-11-18T15:23:30.123", "truncates 4 decimal places to 3"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 4 }), "1976-11-18T15:23:30.1234", + "does not pad"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 5 }), "1976-11-18T15:23:30.12340", + "pads 4 decimal places to 5"); assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1976-11-18T15:23:30.123400", "pads 4 decimal places to 6"); assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:00.0000000", @@ -33,6 +39,8 @@ assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11- "pads whole seconds to 7 decimal places"); assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1976-11-18T15:23:30.1234000", "pads 4 decimal places to 7"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 8 }), "1976-11-18T15:23:30.12340000", + "pads 4 decimal places to 8"); assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1976-11-18T15:23:30.123400000", "pads 4 decimal places to 9"); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/no-observable-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/no-observable-array-iteration.js new file mode 100644 index 0000000000000..7c59da2d2b9af --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/no-observable-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tozoneddatetime +description: > + Calling GetPossibleEpochNanoseconds (from ToTemporalZonedDateTime > InterpretISODateTimeOffset) + causes no observable array iteration. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +let pdt = new Temporal.PlainDateTime(2000, 1, 1, 12, 0, 0, 0); +let zdt = pdt.toZonedDateTime("UTC"); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js index bc8ea59c4b683..ca487170c3d11 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 32 const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.until(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..4a4f2369e2f97 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..6fb2fc30710cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/until/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js index 8cc67cdb3bdb2..63187a26733e6 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/valueOf/basic.js @@ -12,6 +12,7 @@ const dt1 = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789); const dt1again = new Temporal.PlainDateTime(1963, 2, 13, 9, 36, 29, 123, 456, 789); const dt2 = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.throws(TypeError, () => dt1.valueOf(), "always throws"); assert.sameValue(dt1 === dt1, true, "object equality implies ==="); assert.sameValue(dt1 !== dt1again, true, "object non-equality, even if all data is the same, implies !=="); assert.throws(TypeError, () => dt1 < dt1, "< throws (same objects)"); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js index 7d451b870c331..e6b298a4b9448 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/basic.js @@ -4,11 +4,37 @@ /*--- esid: sec-get-temporal.plaindatetime.prototype.weekofyear -description: Checking week of year for a "normal" case (non-undefined, non-boundary case, etc.) +description: Checking week of year for a "normal" case, as well as for dates near the turn of the year. features: [Temporal] ---*/ -const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, "iso8601"); +const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); assert.sameValue(datetime.weekOfYear, 47, "check week of year information"); +for (let i = 29; i <= 31; ++i) { + const datetime = new Temporal.PlainDateTime(1975, 12, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 1, `${datetime} should be in week 1`); +} +for (let i = 1; i <= 4; ++i) { + const datetime = new Temporal.PlainDateTime(1976, 1, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 1, `${datetime} should be in week 1`); +} +for (let i = 5; i <= 11; ++i) { + const datetime = new Temporal.PlainDateTime(1976, 1, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 2, `${datetime} should be in week 2`); +} +for (let i = 20; i <= 26; ++i) { + const datetime = new Temporal.PlainDateTime(1976, 12, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 52, `${datetime} should be in week 52`); +} +for (let i = 27; i <= 31; ++i) { + const datetime = new Temporal.PlainDateTime(1976, 12, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 53, `${datetime} should be in week 53`); +} +for (let i = 1; i <= 2; ++i) { + const datetime = new Temporal.PlainDateTime(1977, 1, i, 15, 23, 30, 123, 456, 789); + assert.sameValue(datetime.weekOfYear, 53, `${datetime} should be in week 53`); +} + + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js index f0076b021faf8..d08013167e846 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/argument-not-object.js @@ -17,6 +17,7 @@ const args = [ Symbol(), 2020, 2020n, + NaN, ]; for (const argument of args) { assert.throws(TypeError, () => instance.with(argument), `Does not support ${typeof argument}`); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js index 9e5eab90fb0f1..b2bec2095ab45 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js @@ -17,6 +17,9 @@ const values = [ Temporal.PlainTime.from("15:19:45"), Temporal.PlainYearMonth.from("2022-04"), Temporal.ZonedDateTime.from("2022-04-12T15:19:45[UTC]"), + Temporal.Now.plainDateTimeISO(), + Temporal.Now.plainDateISO(), + Temporal.Now.plainTimeISO(), ]; for (const value of values) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-invalid-iso-string.js index 671fc947f924a..a49e2089c596a 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-invalid-iso-string.js @@ -4,7 +4,7 @@ /*--- esid: sec-temporal.plaindatetime.prototype.withcalendar -description: Invalid ISO 8601 string is not accepted as calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -12,6 +12,7 @@ const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..7ce3460b1040d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.withcalendar +description: A time string is valid input for Calendar; default is iso8601 +features: [Temporal] +---*/ + +const tests = [ + "15:23", + "15:23:30", + "15:23:30.123", + "15:23:30.123456", + "15:23:30.123456789", + "1976-11-18T15:23:30.1", + "1976-11-18T15:23:30.12", + "1976-11-18T15:23:30.123", + "1976-11-18T15:23:30.1234", + "1976-11-18T15:23:30.12345", + "1976-11-18T15:23:30.123456", + "1976-11-18T15:23:30.1234567", + "1976-11-18T15:23:30.12345678", + "1976-11-18T15:23:30.123456789", + "1976-11-18T15:23:30,12", + "1976-11-18T15:23:30.12-02:00", + "152330", + "152330.1", + "152330-08", + "152330.1-08", + "152330-0800", + "152330.1-0800", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "15", + "T15:23:30", + "t152330", +]; + +const instance = Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18, hour: 12, minute: 34}); + +tests.forEach((arg) => { + const result = instance.withCalendar(arg); + assert.sameValue(result.calendarId, "iso8601", `Calendar created from string "${arg}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/basic.js new file mode 100644 index 0000000000000..2f9ca0b9e3fac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainDateTime/prototype/year/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plaindatetime.prototype.year +description: The "year" property of Temporal.PlainDateTime.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainDateTime(2021, 7, 15, 15, 30, 26, 123, 456, 789)).year, 2021); +assert.sameValue(Temporal.PlainDateTime.from('2019-03-15T15:30:26').year, 2019); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/argument-convert.js new file mode 100644 index 0000000000000..00e7274bab109 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/argument-convert.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday +description: PlainMonthDay constructor with non-integer arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertPlainMonthDay(new Temporal.PlainMonthDay(11.7, 24.1), + "M11", 24, "positive fractional"); + +TemporalHelpers.assertPlainMonthDay(new Temporal.PlainMonthDay("11.7", "24.1"), + "M11", 24, "fractional strings"); + +for (const invalid of [Symbol(), 1n]) { + assert.throws(TypeError, () => new Temporal.PlainMonthDay(invalid, 24), `month ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainMonthDay(11, invalid), `day ${typeof invalid}`); +} + +for (const invalid of [undefined, "invalid"]) { + assert.throws(RangeError, () => new Temporal.PlainMonthDay(invalid, 24), `month ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainMonthDay(11, invalid), `day ${typeof invalid}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid-iso-string.js index 28954173c46aa..03b28447b1d0b 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/calendar-invalid-iso-string.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.plainmonthday.constructor -description: Various invalid ISO string values for calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], ["1997-12-04[u-ca=iso8601]", "ISO string with calendar annotation"], + ["notacal", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-invalid-iso-string.js index 7904658110358..aab87f22667a9 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-invalid-iso-string.js @@ -4,21 +4,23 @@ /*--- esid: sec-temporal.plainmonthday.from -description: Various invalid ISO string values for calendar in a property bag +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ - ["", "empty string"], + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { monthCode: "M11", day: 18, calendar }; - assert.throws( - RangeError, - () => Temporal.PlainMonthDay.from(arg), - `${description} is not a valid calendar ID` - ); +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; + assert.throws( + RangeError, + () => Temporal.PlainMonthDay.from(arg), + `${description} is not a valid calendar ID` + ); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..dbff9d6493718 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.from +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainMonthDay.from(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js index 7ee63cf148b7f..1083d1c07bcd3 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.PlainMonthDay.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -25,6 +27,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.PlainMonthDay.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.PlainMonthDay.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -36,6 +46,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.PlainMonthDay.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.PlainMonthDay.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/negative-month-or-day.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/negative-month-or-day.js new file mode 100644 index 0000000000000..c1009c49d324f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/negative-month-or-day.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.from +description: Months and days must be non-negative integers +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ day: 1, month: -1 })); +assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ month: 1, day: -1 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..89789a3f5dec0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainMonthDay.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js index 31432de575e92..e771256baff63 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/missing-arguments.js @@ -21,4 +21,6 @@ const args = [ assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args)); assert.compareArray(actual, expected, "order of operations"); +assert.throws(RangeError, () => new Temporal.PlainMonthDay(), "no arguments"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js index 7d90de96cd046..6916051dae1bc 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js @@ -4,8 +4,7 @@ /*--- esid: sec-temporal.plainmonthday.prototype.equals -description: > - A string value for calendar in a property bag that is not a valid calendar ID +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -13,10 +12,12 @@ const instance = new Temporal.PlainMonthDay(5, 2); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.equals(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..c938aa0f73ec7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.prototype.equals +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainMonthDay(5, 2); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..90cdbdf26fe05 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainMonthDay/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainMonthDay(5, 2) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/argument-convert.js new file mode 100644 index 0000000000000..32b241b3dc278 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/argument-convert.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime +description: PlainTime constructor with non-integer arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertPlainTime(new Temporal.PlainTime(11.9, 12.8, 13.7, 14.6, 15.5, 1.999999), + 11, 12, 13, 14, 15, 1, "positive fractional"); + +TemporalHelpers.assertPlainTime(new Temporal.PlainTime(null, 1, 2, 3, 4, 5), + 0, 1, 2, 3, 4, 5, "null defaults to zero"); + +TemporalHelpers.assertPlainTime(new Temporal.PlainTime(false, true), + 0, 1, 0, 0, 0, 0, "boolean defaults"); + +TemporalHelpers.assertPlainTime(new Temporal.PlainTime(11, 24, undefined), + 11, 24, 0, 0, 0, 0, "undefined defaults to 0"); + +TemporalHelpers.assertPlainTime(new Temporal.PlainTime("11.9", "12.8", "13.7", "14.6", "15.5", "1.999999"), + 11, 12, 13, 14, 15, 1, "fractional strings"); + +for (const invalid of [Symbol(), 1n]) { + assert.throws(TypeError, () => new Temporal.PlainTime(invalid, 2, 3, 4, 5, 6), `hour ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainTime(1, invalid, 3, 4, 5, 6), `minute ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainTime(1, 2, invalid, 4, 5, 6), `second ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainTime(1, 2, 3, invalid, 5, 6), `millisecond ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainTime(1, 2, 3, 4, invalid, 6), `microsecond ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainTime(1, 2, 3, 4, 5, invalid), `nanosecond ${typeof invalid}`); +} + +for (const invalid of ["invalid"]) { + assert.throws(RangeError, () => new Temporal.PlainTime(invalid, 2, 3, 4, 5, 6), `hour ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainTime(1, invalid, 3, 4, 5, 6), `minute ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainTime(1, 2, invalid, 4, 5, 6), `second ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainTime(1, 2, 3, invalid, 5, 6), `millisecond ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainTime(1, 2, 3, 4, invalid, 6), `microsecond ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainTime(1, 2, 3, 4, 5, invalid), `nanosecond ${typeof invalid}`); +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..f9846c456c6d2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["05:07.123", "Fractional minutes"], + ["12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainTime.compare(arg, new Temporal.PlainTime(20, 4, 3)), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainTime.compare(new Temporal.PlainTime(20, 4, 3), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-invalid.js new file mode 100644 index 0000000000000..e34a0c5a555dd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-string-invalid.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.from +description: > + RangeError thrown if an invalid ISO string (or syntactically valid ISO string + that is not supported) is used as a PlainTime +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + // invalid ISO strings: + "", + "invalid iso8601", + "00:00Z", + "Z", + "25:00:00Z", + "01:60:00Z", + "01:60:61Z", + "00:00Zjunk", + "00:00:00Zjunk", + "00:00:00.000000000Zjunk", + "00:00:00+00:00junk", + "00:00:00+00:00[UTC]junk", + "00:00:00+00:00[UTC][u-ca=iso8601]junk", + "001Z", + "01:001Z", + "01:01:001Z", + "00:00-24:00", + "00:00+24:00", + // "00:0000" is invalid (the hour/minute and minute/second separator + // or lack thereof needs to match). + "00:00:00+00:0000", + "0000:00", + "00:0000", + "00:00:00+0000:00", +]; +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainTime.from(arg), + `"${arg}" should not be a valid ISO string for a PlainTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js index 4a3b01707dd1a..68e5e234ccc69 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.PlainTime.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -25,6 +27,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.PlainTime.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.PlainTime.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -36,6 +46,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.PlainTime.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.PlainTime.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..b6ff2789aeb50 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["05:07.123", "Fractional minutes"], + ["12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainTime.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-divides.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-divides.js new file mode 100644 index 0000000000000..dfc157cdd3392 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-divides.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.round +description: Rounding increment should properly divide the relevant time unit +features: [Temporal] +---*/ + +const t = new Temporal.PlainTime(14, 23, 30, 123, 456, 789); + +const minutesSeconds = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30]; +const subSeconds = [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 200, 250, 500]; + +const unitsAndIncrements = { + "hour": [1, 2, 3, 4, 6, 8, 12], + "minute": minutesSeconds, + "second": minutesSeconds, + "millisecond": subSeconds, + "microsecond": subSeconds, + "nanosecond": subSeconds, +}; + +// Just check that each combination of unit and increment doesn't throw +Object.entries(unitsAndIncrements).forEach(([unit, increments]) => { + increments.forEach((increment) => { + const result = t.round({ smallestUnit: unit, roundingMode: "ceil", roundingIncrement: increment }); + assert.sameValue(result instanceof Temporal.PlainTime, true, `${unit} ${increment}`); + }) +}); + +const nextIncrements = { + "hour": 24, + "minute": 60, + "second": 60, + "millisecond": 1000, + "microsecond": 1000, + "nanosecond": 1000 +}; + +Object.entries(nextIncrements).forEach(([unit, next]) => { + assert.throws( + RangeError, + () => t.round({ smallestUnit: unit, roundingIncrement: next }), + `throws on increments that are equal to the next highest (unit = ${unit}, increment = ${next})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/argument-convert.js new file mode 100644 index 0000000000000..92cbdbc066ac2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/argument-convert.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth +description: PlainYearMonth constructor with non-integer arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(2020.6, 11.7), + 2020, 11, "M11", "positive fractional"); + +TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(-2020.6, 11.7), + -2020, 11, "M11", "negative fractional"); + +TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(null, 11), + 0, 11, "M11", "null defaults to zero"); + +TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(false, true), + 0, 1, "M01", "boolean defaults"); + +TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth("2020.6", "11.7"), + 2020, 11, "M11", "fractional strings"); + +for (const invalid of [Symbol(), 1n]) { + assert.throws(TypeError, () => new Temporal.PlainYearMonth(invalid, 11), `year ${typeof invalid}`); + assert.throws(TypeError, () => new Temporal.PlainYearMonth(2020, invalid), `month ${typeof invalid}`); +} + +for (const invalid of [undefined, "invalid"]) { + assert.throws(RangeError, () => new Temporal.PlainYearMonth(invalid, 11), `year ${typeof invalid}`); + assert.throws(RangeError, () => new Temporal.PlainYearMonth(2020, invalid), `month ${typeof invalid}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid-iso-string.js index dbdce0379a6c5..2111acaf0aaba 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid-iso-string.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.plainyearmonth.constructor -description: Various invalid ISO string values for calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], ["1997-12-04[u-ca=iso8601]", "ISO string with calendar annotation"], + ["notacal", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-invalid-iso-string.js index 5328d7eb971d4..aa128be5e67e7 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-invalid-iso-string.js @@ -10,10 +10,12 @@ features: [Temporal] const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; - -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M06", calendar }; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..a26eae925af3b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.compare +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), + `${description} is not a valid calendar ID (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg), + `${description} is not a valid calendar ID (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..89f154d4b097f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2025, 4)), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2025, 4), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-invalid-iso-string.js index b2d4d911f352c..c9f875769f03c 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-invalid-iso-string.js @@ -4,16 +4,18 @@ /*--- esid: sec-temporal.plainyearmonth.from -description: Various invalid ISO string values for calendar in a property bag +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; - -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", calendar }; + +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.PlainYearMonth.from(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..8e1999071fc86 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.from +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.from(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js index de5a5590f6a4e..c07075d890bfc 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.PlainYearMonth.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -25,6 +27,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.PlainYearMonth.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.PlainYearMonth.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -36,6 +46,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.PlainYearMonth.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.PlainYearMonth.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/month-code-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/month-code-wrong-type.js new file mode 100644 index 0000000000000..10bf442001563 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/month-code-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.from +description: Month code must be a string +features: [Temporal] +---*/ + +const monthCodeValues = [ + 5, 5n, false, Symbol(), null, { toString: () => 5 } +]; + +const year = 2026; + +for (const monthCode of monthCodeValues) { + assert.throws(TypeError, () => Temporal.PlainYearMonth.from({ + year, + monthCode, + + }), typeof monthCode === 'symbol' ? + "Symbol should be rejected as month code" : + `month code ${monthCode} should be rejected`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/negative-month.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/negative-month.js new file mode 100644 index 0000000000000..6614fdf122f65 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/negative-month.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.from +description: Months must be non-negative integers +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ year: 1, month: -1 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..6481baacd57f8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.PlainYearMonth.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js index 0c0f0d973e3b3..a4ed3f2002655 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js @@ -20,4 +20,6 @@ const args = [ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args)); assert.compareArray(actual, expected, "order of operations"); +assert.throws(RangeError, () => new Temporal.PlainYearMonth(), "no arguments"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js index 7189fa8806d95..b64ae2344292f 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js @@ -12,20 +12,10 @@ features: [Temporal] const instance = new Temporal.PlainYearMonth(1970, 1); const maxCases = [ - ["P273790Y8M12DT23H59M59.999999999S", "string with max years"], - [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"], - ["P3285488M12DT23H59M59.999999999S", "string with max months"], - [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"], - ["P14285714W2DT23H59M59.999999999S", "string with max weeks"], - [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"], - ["P100000000DT23H59M59.999999999S", "string with max days"], - [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"], - ["PT2400000023H59M59.999999999S", "string with max hours"], - [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"], - ["PT144000001439M59.999999999S", "string with max minutes"], - [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"], - ["PT8640000086399.999999999S", "string with max seconds"], - [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"], + ["P273790Y8M", "string with max years"], + [{ years: 273790, months: 8 }, "property bag with max years"], + ["P3285488M", "string with max months"], + [{ months: 3285488 }, "property bag with max months"], ]; for (const [arg, descr] of maxCases) { @@ -34,25 +24,15 @@ for (const [arg, descr] of maxCases) { } const minCases = [ - ["-P273790Y8M42DT23H59M59.999999999S", "string with min years"], - [{ years: -273790, months: -8, days: -42, nanoseconds: -86399999999999 }, "property bag with min years"], - ["-P3285488M42DT23H59M59.999999999S", "string with min months"], - [{ months: -3285488, days: -42, nanoseconds: -86399999999999 }, "property bag with min months"], - ["-P14285718W5DT23H59M59.999999999S", "string with min weeks"], - [{ weeks: -14285718, days: -5, nanoseconds: -86399999999999 }, "property bag with min weeks"], - ["-P100000031DT23H59M59.999999999S", "string with min days"], - [{ days: -100000031, nanoseconds: -86399999999999 }, "property bag with min days"], - ["-PT2400000767H59M59.999999999S", "string with min hours"], - [{ hours: -2400000767, nanoseconds: -3599999999999 }, "property bag with min hours"], - ["-PT144000046079M59.999999999S", "string with min minutes"], - [{ minutes: -144000046079, nanoseconds: -59999999999 }, "property bag with min minutes"], - ["-PT8640002764799.999999999S", "string with min seconds"], - [{ seconds: -8640002764799, nanoseconds: -999999999 }, "property bag with min seconds"], + ["-P273790Y8M", "string with min years"], + [{ years: -273790, months: -8 }, "property bag with min years"], + ["-P3285488M", "string with min months"], + [{ months: -3285488 }, "property bag with min months"], ]; for (const [arg, descr] of minCases) { const result = instance.add(arg); - TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`); + TemporalHelpers.assertPlainYearMonth(result, -271821, 5, "M05", `operation succeeds with ${descr}`); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js index be8dff7e3e540..887fda016e288 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js @@ -24,48 +24,6 @@ const cases = [ [{ weeks: 4294967296 }, "property bag with weeks > max"], ["-P4294967296W", "string with weeks < min"], [{ weeks: -4294967296 }, "property bag with weeks < min"], - - // ceil(max safe integer / 86400) = 104249991375 - ["P104249991375D", "string with days > max"], - [{ days: 104249991375 }, "property bag with days > max"], - ["P104249991374DT24H", "string where hours balance into days > max"], - [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"], - ["-P104249991375D", "string with days < min"], - [{ days: -104249991375 }, "property bag with days < min"], - ["-P104249991374DT24H", "string where hours balance into days < min"], - [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"], - - // ceil(max safe integer / 3600) = 2501999792984 - ["PT2501999792984H", "string with hours > max"], - [{ hours: 2501999792984 }, "property bag with hours > max"], - ["PT2501999792983H60M", "string where minutes balance into hours > max"], - [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"], - ["-PT2501999792984H", "string with hours < min"], - [{ hours: -2501999792984 }, "property bag with hours < min"], - ["-PT2501999792983H60M", "string where minutes balance into hours < min"], - [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"], - - // ceil(max safe integer / 60) = 150119987579017 - ["PT150119987579017M", "string with minutes > max"], - [{ minutes: 150119987579017 }, "property bag with minutes > max"], - ["PT150119987579016M60S", "string where seconds balance into minutes > max"], - [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"], - ["-PT150119987579017M", "string with minutes < min"], - [{ minutes: -150119987579017 }, "property bag with minutes < min"], - ["-PT150119987579016M60S", "string where seconds balance into minutes < min"], - [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"], - - // 2^53 = 9007199254740992 - ["PT9007199254740992S", "string with seconds > max"], - [{ seconds: 9007199254740992 }, "property bag with seconds > max"], - [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"], - [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"], - [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"], - ["-PT9007199254740992S", "string with seconds < min"], - [{ seconds: -9007199254740992 }, "property bag with seconds < min"], - [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"], - [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"], - [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"], ]; for (const [arg, descr] of cases) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js index e7fe44f2175c4..49d0f9cd34450 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js @@ -4,38 +4,37 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.add -description: Using lower units in add() works -includes: [temporalHelpers.js] +description: Using lower units in add() throws features: [Temporal] ---*/ const ym = Temporal.PlainYearMonth.from("2019-11"); const tests = [ - [{ days: 1 }, 2019, 11, "M11"], - [{ days: 29 }, 2019, 11, "M11"], - [{ hours: 1 }, 2019, 11, "M11"], - [{ minutes: 1 }, 2019, 11, "M11"], - [{ seconds: 1 }, 2019, 11, "M11"], - [{ milliseconds: 1 }, 2019, 11, "M11"], - [{ microseconds: 1 }, 2019, 11, "M11"], - [{ nanoseconds: 1 }, 2019, 11, "M11"], - [{ days: 30 }, 2019, 12, "M12"], - [{ days: 31 }, 2019, 12, "M12"], - [{ days: 60 }, 2019, 12, "M12"], - [{ days: 61 }, 2020, 1, "M01"], - [{ hours: 720 }, 2019, 12, "M12"], - [{ minutes: 43200 }, 2019, 12, "M12"], - [{ seconds: 2592000 }, 2019, 12, "M12"], - [{ milliseconds: 2592000_000 }, 2019, 12, "M12"], - [{ microseconds: 2592000_000_000 }, 2019, 12, "M12"], - [{ nanoseconds: 2592000_000_000_000 }, 2019, 12, "M12"], + { days: 1 }, + { days: 29 }, + { hours: 1 }, + { minutes: 1 }, + { seconds: 1 }, + { milliseconds: 1 }, + { microseconds: 1 }, + { nanoseconds: 1 }, + { days: 30 }, + { days: 31 }, + { days: 60 }, + { days: 61 }, + { hours: 720 }, + { minutes: 43200 }, + { seconds: 2592000 }, + { milliseconds: 2592000_000 }, + { microseconds: 2592000_000_000 }, + { nanoseconds: 2592000_000_000_000 }, ]; -for (const [argument, ...expected] of tests) { - TemporalHelpers.assertPlainYearMonth(ym.add(argument), ...expected, "no options"); - TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "constrain" }), ...expected, "constrain"); - TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "reject" }), ...expected, "reject"); +for (const argument of tests) { + assert.throws(RangeError, function () { ym.add(argument); }, "adding a unit lower than months should throw, no options"); + assert.throws(RangeError, function () { ym.add(argument, { overflow: "constrain" }); }, "adding a unit lower than months should throw, constrain"); + assert.throws(RangeError, function () { ym.add(argument, { overflow: "reject" }); }, "adding a unit lower than months should throw, reject"); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js index 100bd0924c97f..ba1ea52b470bd 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js @@ -13,7 +13,7 @@ const instance = new Temporal.PlainYearMonth(2000, 5); ["constrain", "reject"].forEach((overflow) => { assert.throws( RangeError, - () => instance.add({ hours: 1, minutes: -30 }, { overflow }), + () => instance.add({ years: 1, months: -3 }, { overflow }), `mixed positive and negative values always throw (overflow = "${overflow}")` ); }); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-propertybag-optional-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-propertybag-optional-properties.js index bbdeb279dfd5f..cecb4dd1e69aa 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-propertybag-optional-properties.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-propertybag-optional-properties.js @@ -5,22 +5,22 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.add description: > - A property bag missing optional properties is equivalent to a property bag - with all the optional properties having their default values + A duration property bag with value 0 for disallowed properties is the same as + a property bag with no disallowed properties features: [Temporal] ---*/ const instance = new Temporal.PlainYearMonth(1970, 1); const oneProperty = { - hours: 1, + months: 1, }; const allProperties = { years: 0, - months: 0, + months: 1, weeks: 0, days: 0, - hours: 1, + hours: 0, minutes: 0, seconds: 0, milliseconds: 0, @@ -29,6 +29,6 @@ const allProperties = { }; const resultWithout = instance.add(oneProperty); const resultWith = instance.add(allProperties); -assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties"); +assert(resultWithout.equals(resultWith), "results should be the same with 0 for disallowed properties and without disallowed properties"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js deleted file mode 100644 index 525a26d87f699..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js +++ /dev/null @@ -1,20 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2021 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.plainyearmonth.prototype.add -description: Strings with fractional duration units are treated with the correct sign -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -const instance = new Temporal.PlainYearMonth(2000, 5); - -const resultHours = instance.add("-PT24.567890123H"); -TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours"); - -const resultMinutes = instance.add("-PT1440.567890123M"); -TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes"); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js deleted file mode 100644 index 9ab1999987358..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.plainyearmonth.prototype.add -description: add() takes month length into account -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -const ym = Temporal.PlainYearMonth.from("2019-11"); - -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 27 }), - 2019, 2, "M02"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 28 }), - 2019, 3, "M03"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 28 }), - 2020, 2, "M02"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 29 }), - 2020, 3, "M03"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 29 }), - 2019, 11, "M11"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 30 }), - 2019, 12, "M12"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 30 }), - 2020, 1, "M01"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 31 }), - 2020, 2, "M02"); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js index dd159fd36c6ad..c33a398571f28 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js @@ -28,4 +28,13 @@ assert.throws(RangeError, function () { }, "exception thrown when converting -271821-04 to date"); assert.compareArray(actual, expected, "all options should be read first"); +actual.splice(0); // clear + +const instance2 = new Temporal.PlainYearMonth(1999, 12); + +assert.throws(RangeError, function () { + instance2.add(new Temporal.Duration(0, 0, 1), options); +}, "exception thrown when attempting to add too-low unit"); +assert.compareArray(actual, expected, "all options should be read first"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js index bc04566e23a08..df6593bcfdfd2 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js @@ -53,14 +53,14 @@ const instance = new Temporal.PlainYearMonth(2000, 5); const fields = TemporalHelpers.propertyBagObserver(actual, { years: 1, months: 1, - weeks: 1, - days: 1, - hours: 1, - minutes: 1, - seconds: 1, - milliseconds: 1, - microseconds: 1, - nanoseconds: 1, + weeks: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 0, }, "fields"); const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow.js new file mode 100644 index 0000000000000..2ac7ff757dcc8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Overflow option has no effect in ISO 8601 calendar +features: [Temporal] +---*/ + +const year1 = new Temporal.Duration(1); +const year1n = new Temporal.Duration(-1); +const month1 = new Temporal.Duration(0, 1); +const month1n = new Temporal.Duration(0, -1); + +for (const year of [2023, 2024]) { + for (const month of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) { + const yearmonth = new Temporal.PlainYearMonth(year, month); + for (const duration of [year1, year1n, month1, month1n]) { + const resultConstrain = yearmonth.add(duration, { overflow: "constrain" }); + const resultReject = yearmonth.add(duration, { overflow: "reject" }); + assert.sameValue(resultReject.year, resultConstrain.year, "year should be identical"); + assert.sameValue(resultReject.month, resultConstrain.month, "month should be identical"); + assert.sameValue(resultReject.toString(), resultConstrain.toString(), "toString should be identical"); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js index 8f92191094e65..96c435476fc32 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js @@ -4,21 +4,15 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.add -description: RangeError thrown when adding negative duration to last representable month. +description: Adding negative duration to last representable month works features: [Temporal] +includes: [temporalHelpers.js] ---*/ const lastMonth = new Temporal.PlainYearMonth(275760, 9); -// See https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoyearmonth -// (step 10d) -assert.throws(RangeError, () => lastMonth.add({seconds: -1})); -assert.throws(RangeError, () => lastMonth.add({minutes: -1})); -assert.throws(RangeError, () => lastMonth.add({hours: -1})); -assert.throws(RangeError, () => lastMonth.add({days: -1})); -assert.throws(RangeError, () => lastMonth.add({weeks: -1})); -assert.throws(RangeError, () => lastMonth.add({months: -1})); -assert.throws(RangeError, () => lastMonth.add({years: -1})); +TemporalHelpers.assertPlainYearMonth(lastMonth.add({ months: -1 }), 275760, 8, "M08", "-1 month"); +TemporalHelpers.assertPlainYearMonth(lastMonth.add({ years: -1 }), 275759, 9, "M09", "-1 year"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js index 075cb5193ecbc..2014ac1a39acc 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js @@ -4,24 +4,24 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.equals -description: > - Appropriate error thrown when a calendar property from a property bag cannot - be converted to a calendar ID -features: [BigInt, Symbol, Temporal] +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] ---*/ const instance = new Temporal.PlainYearMonth(2000, 5); -const primitiveTests = [ - ["", "empty string"] +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of primitiveTests) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.equals(arg), - `${description} does not convert to a valid ISO string` + `${description} is not a valid calendar ID` ); } diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..0cbe44bce407b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.equals +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..d87566020a7e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/basic.js new file mode 100644 index 0000000000000..880acf460cee3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plainyearmonth.prototype.month +description: Month property for PlainYearMonth +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainYearMonth(1999, 6)).month, 6); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/basic.js new file mode 100644 index 0000000000000..20b6447de82b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/basic.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.monthcode +description: monthCode property for PlainYearMonth +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainYearMonth(1999, 6)).monthCode, 'M06'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-invalid-iso-string.js index e07b9f05445f1..660fec22d56b8 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-invalid-iso-string.js @@ -4,8 +4,7 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.since -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -13,10 +12,12 @@ const instance = new Temporal.PlainYearMonth(2000, 5); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.since(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..bad7bab1a8354 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.since +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..01eadb2e24bfc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.since +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js index 2396a974c4e87..3b3c07674678e 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js @@ -12,42 +12,22 @@ features: [Temporal] const instance = new Temporal.PlainYearMonth(1970, 1); const maxCases = [ - ["P273790Y8M42DT23H59M59.999999999S", "string with max years"], - [{ years: 273790, months: 8, days: 42, nanoseconds: 86399999999999 }, "property bag with max years"], - ["P3285488M42DT23H59M59.999999999S", "string with max months"], - [{ months: 3285488, days: 42, nanoseconds: 86399999999999 }, "property bag with max months"], - ["P14285718W5DT23H59M59.999999999S", "string with max weeks"], - [{ weeks: 14285718, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"], - ["P100000031DT23H59M59.999999999S", "string with max days"], - [{ days: 100000031, nanoseconds: 86399999999999 }, "property bag with max days"], - ["PT2400000767H59M59.999999999S", "string with max hours"], - [{ hours: 2400000767, nanoseconds: 3599999999999 }, "property bag with max hours"], - ["PT144000046079M59.999999999S", "string with max minutes"], - [{ minutes: 144000046079, nanoseconds: 59999999999 }, "property bag with max minutes"], - ["PT8640002764799.999999999S", "string with max seconds"], - [{ seconds: 8640002764799, nanoseconds: 999999999 }, "property bag with max seconds"], + ["P273790Y8M", "string with max years"], + [{ years: 273790, months: 8 }, "property bag with max years"], + ["P3285488M", "string with max months"], + [{ months: 3285488 }, "property bag with max months"], ]; for (const [arg, descr] of maxCases) { const result = instance.subtract(arg); - TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`); + TemporalHelpers.assertPlainYearMonth(result, -271821, 5, "M05", `operation succeeds with ${descr}`); } const minCases = [ - ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"], - [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"], - ["-P3285488M12DT23H59M59.999999999S", "string with min months"], - [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"], - ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"], - [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"], - ["-P100000000DT23H59M59.999999999S", "string with min days"], - [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"], - ["-PT2400000023H59M59.999999999S", "string with min hours"], - [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"], - ["-PT144000001439M59.999999999S", "string with min minutes"], - [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"], - ["-PT8640000086399.999999999S", "string with min seconds"], - [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"], + ["-P273790Y8M", "string with min years"], + [{ years: -273790, months: -8 }, "property bag with min years"], + ["-P3285488M", "string with min months"], + [{ months: -3285488 }, "property bag with min months"], ]; for (const [arg, descr] of minCases) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js index d0977de87a810..f4827f45446fa 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js @@ -24,48 +24,6 @@ const cases = [ [{ weeks: 4294967296 }, "property bag with weeks > max"], ["-P4294967296W", "string with weeks < min"], [{ weeks: -4294967296 }, "property bag with weeks < min"], - - // ceil(max safe integer / 86400) = 104249991375 - ["P104249991375D", "string with days > max"], - [{ days: 104249991375 }, "property bag with days > max"], - ["P104249991374DT24H", "string where hours balance into days > max"], - [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"], - ["-P104249991375D", "string with days < min"], - [{ days: -104249991375 }, "property bag with days < min"], - ["-P104249991374DT24H", "string where hours balance into days < min"], - [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"], - - // ceil(max safe integer / 3600) = 2501999792984 - ["PT2501999792984H", "string with hours > max"], - [{ hours: 2501999792984 }, "property bag with hours > max"], - ["PT2501999792983H60M", "string where minutes balance into hours > max"], - [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"], - ["-PT2501999792984H", "string with hours < min"], - [{ hours: -2501999792984 }, "property bag with hours < min"], - ["-PT2501999792983H60M", "string where minutes balance into hours < min"], - [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"], - - // ceil(max safe integer / 60) = 150119987579017 - ["PT150119987579017M", "string with minutes > max"], - [{ minutes: 150119987579017 }, "property bag with minutes > max"], - ["PT150119987579016M60S", "string where seconds balance into minutes > max"], - [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"], - ["-PT150119987579017M", "string with minutes < min"], - [{ minutes: -150119987579017 }, "property bag with minutes < min"], - ["-PT150119987579016M60S", "string where seconds balance into minutes < min"], - [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"], - - // 2^53 = 9007199254740992 - ["PT9007199254740992S", "string with seconds > max"], - [{ seconds: 9007199254740992 }, "property bag with seconds > max"], - [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"], - [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"], - [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"], - ["-PT9007199254740992S", "string with seconds < min"], - [{ seconds: -9007199254740992 }, "property bag with seconds < min"], - [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"], - [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"], - [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"], ]; for (const [arg, descr] of cases) { diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js index ea3156580b18d..2d59878231723 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js @@ -4,37 +4,36 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.subtract -description: Using lower units in subtract() works -includes: [temporalHelpers.js] +description: Using lower units in subtract() throws features: [Temporal] ---*/ const ym = Temporal.PlainYearMonth.from("2019-11"); const tests = [ - [{ days: 1 }, 2019, 11, "M11"], - [{ hours: 1 }, 2019, 11, "M11"], - [{ minutes: 1 }, 2019, 11, "M11"], - [{ seconds: 1 }, 2019, 11, "M11"], - [{ milliseconds: 1 }, 2019, 11, "M11"], - [{ microseconds: 1 }, 2019, 11, "M11"], - [{ nanoseconds: 1 }, 2019, 11, "M11"], - [{ days: 29 }, 2019, 11, "M11"], - [{ days: 30 }, 2019, 10, "M10"], - [{ days: 60 }, 2019, 10, "M10"], - [{ days: 61 }, 2019, 9, "M09"], - [{ hours: 720 }, 2019, 10, "M10"], - [{ minutes: 43200 }, 2019, 10, "M10"], - [{ seconds: 2592000 }, 2019, 10, "M10"], - [{ milliseconds: 2592000_000 }, 2019, 10, "M10"], - [{ microseconds: 2592000_000_000 }, 2019, 10, "M10"], - [{ nanoseconds: 2592000_000_000_000 }, 2019, 10, "M10"], + { days: 1 }, + { hours: 1 }, + { minutes: 1 }, + { seconds: 1 }, + { milliseconds: 1 }, + { microseconds: 1 }, + { nanoseconds: 1 }, + { days: 29 }, + { days: 30 }, + { days: 60 }, + { days: 61 }, + { hours: 720 }, + { minutes: 43200 }, + { seconds: 2592000 }, + { milliseconds: 2592000_000 }, + { microseconds: 2592000_000_000 }, + { nanoseconds: 2592000_000_000_000 }, ]; -for (const [argument, ...expected] of tests) { - TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options"); - TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain"); - TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject"); +for (const argument of tests) { + assert.throws(RangeError, function () { ym.subtract(argument); }, "subtracting a unit lower than months should throw, no options"); + assert.throws(RangeError, function () { ym.subtract(argument, { overflow: "constrain" }); }, "subtracting a unit lower than months should throw, constrain"); + assert.throws(RangeError, function () { ym.subtract(argument, { overflow: "reject" }); }, "subtracting a unit lower than months should throw, reject"); } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js index f66b8abadacc1..5b17cfff49747 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js @@ -13,7 +13,7 @@ const instance = new Temporal.PlainYearMonth(2000, 5); ["constrain", "reject"].forEach((overflow) => { assert.throws( RangeError, - () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }), + () => instance.subtract({ years: 1, months: -3 }, { overflow }), `mixed positive and negative values always throw (overflow = "${overflow}")` ); }); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-propertybag-optional-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-propertybag-optional-properties.js index 3b3feea59be63..9ba7fe5501ebf 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-propertybag-optional-properties.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-propertybag-optional-properties.js @@ -5,22 +5,22 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.subtract description: > - A property bag missing optional properties is equivalent to a property bag - with all the optional properties having their default values + A duration property bag with value 0 for disallowed properties is the same as + a property bag with no disallowed properties features: [Temporal] ---*/ const instance = new Temporal.PlainYearMonth(1970, 1); const oneProperty = { - hours: 1, + months: 1, }; const allProperties = { years: 0, - months: 0, + months: 1, weeks: 0, days: 0, - hours: 1, + hours: 0, minutes: 0, seconds: 0, milliseconds: 0, @@ -29,6 +29,6 @@ const allProperties = { }; const resultWithout = instance.subtract(oneProperty); const resultWith = instance.subtract(allProperties); -assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties"); +assert(resultWithout.equals(resultWith), "results should be the same with 0 for disallowed properties and without disallowed properties"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js deleted file mode 100644 index b3e7c9a74c284..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js +++ /dev/null @@ -1,20 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2021 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.plainyearmonth.prototype.subtract -description: Strings with fractional duration units are treated with the correct sign -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -const instance = new Temporal.PlainYearMonth(2000, 5); - -const resultHours = instance.subtract("-PT24.567890123H"); -TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours"); - -const resultMinutes = instance.subtract("-PT1440.567890123M"); -TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes"); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js deleted file mode 100644 index ab18f8ef9a63c..0000000000000 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.plainyearmonth.prototype.subtract -description: subtract() takes month length into account -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -const ym = Temporal.PlainYearMonth.from("2019-11"); - -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 27 }), - 2019, 2, "M02"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 28 }), - 2019, 1, "M01"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 28 }), - 2020, 2, "M02"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 29 }), - 2020, 1, "M01"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 29 }), - 2019, 11, "M11"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 30 }), - 2019, 10, "M10"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 30 }), - 2020, 1, "M01"); -TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 31 }), - 2019, 12, "M12"); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js index c4b9805d3ea8f..e1555f7baf166 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js @@ -28,4 +28,13 @@ assert.throws(RangeError, function () { }, "exception thrown when converting -271821-04 to date"); assert.compareArray(actual, expected, "all options should be read first"); +actual.splice(0); // clear + +const instance2 = new Temporal.PlainYearMonth(1999, 12); + +assert.throws(RangeError, function () { + instance2.add(new Temporal.Duration(0, 0, 1), options); +}, "exception thrown when attempting to add too-low unit"); +assert.compareArray(actual, expected, "all options should be read first"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js index 5dce99a42962b..2da7a789ba665 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js @@ -52,14 +52,14 @@ const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601"); const fields = TemporalHelpers.propertyBagObserver(actual, { years: 1, months: 1, - weeks: 1, - days: 1, - hours: 1, - minutes: 1, - seconds: 1, - milliseconds: 1, - microseconds: 1, - nanoseconds: 1, + weeks: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 0, }, "fields"); const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow.js new file mode 100644 index 0000000000000..b4d453ebc161a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Overflow option has no effect in ISO 8601 calendar +features: [Temporal] +---*/ + +const year1 = new Temporal.Duration(1); +const year1n = new Temporal.Duration(-1); +const month1 = new Temporal.Duration(0, 1); +const month1n = new Temporal.Duration(0, -1); + +for (const year of [2023, 2024]) { + for (const month of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) { + const yearmonth = new Temporal.PlainYearMonth(year, month); + for (const duration of [year1, year1n, month1, month1n]) { + const resultConstrain = yearmonth.subtract(duration, { overflow: "constrain" }); + const resultReject = yearmonth.subtract(duration, { overflow: "reject" }); + assert.sameValue(resultReject.year, resultConstrain.year, "year should be identical"); + assert.sameValue(resultReject.month, resultConstrain.month, "month should be identical"); + assert.sameValue(resultReject.toString(), resultConstrain.toString(), "toString should be identical"); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js index efceaa1c46cb4..e09ed6f0fd898 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js @@ -4,21 +4,15 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.subtract -description: RangeError thrown when subtracting duration from last representable month. +description: Subtracting duration from last representable month works features: [Temporal] +includes: [temporalHelpers.js] ---*/ const lastMonth = new Temporal.PlainYearMonth(275760, 9); -// See https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoyearmonth -// (step 10d) -assert.throws(RangeError, () => lastMonth.subtract({seconds: 1})); -assert.throws(RangeError, () => lastMonth.subtract({minutes: 1})); -assert.throws(RangeError, () => lastMonth.subtract({hours: 1})); -assert.throws(RangeError, () => lastMonth.subtract({days: 1})); -assert.throws(RangeError, () => lastMonth.subtract({weeks: 1})); -assert.throws(RangeError, () => lastMonth.subtract({months: 1})); -assert.throws(RangeError, () => lastMonth.subtract({years: 1})); +TemporalHelpers.assertPlainYearMonth(lastMonth.subtract({ months: 1 }), 275760, 8, "M08", "1 month"); +TemporalHelpers.assertPlainYearMonth(lastMonth.subtract({ years: 1 }), 275759, 9, "M09", "1 year"); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-invalid-iso-string.js index a0db80c895a66..22b4403f9b2c7 100644 --- a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-invalid-iso-string.js @@ -4,19 +4,20 @@ /*--- esid: sec-temporal.plainyearmonth.prototype.until -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const instance = new Temporal.PlainYearMonth(2000, 5); const invalidStrings = [ - ["", "empty string"] + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.until(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..97e754bfc3c5b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.until +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..9ea030ea010c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.until +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5) + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/basic.js new file mode 100644 index 0000000000000..7e37d7931cd9f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.plainyearmonth.prototype.year +description: The "year" property of Temporal.PlainYearMonth.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.PlainYearMonth(2021, 7)).year, 2021); +assert.sameValue(Temporal.PlainYearMonth.from('2019-03').year, 2019); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/argument-convert.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/argument-convert.js new file mode 100644 index 0000000000000..a12e8f1d2a532 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/argument-convert.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: ZonedDateTime constructor with non-integer arguments. +features: [Temporal] +---*/ + +assert.sameValue(new Temporal.ZonedDateTime(false, "UTC").epochNanoseconds, + 0n, "boolean defaults"); + +assert.sameValue(new Temporal.ZonedDateTime(true, "UTC").epochNanoseconds, + 1n, "boolean defaults"); + +assert.throws(TypeError, () => new Temporal.ZonedDateTime(Symbol(), "UTC"), `symbol`); +assert.throws(TypeError, () => new Temporal.ZonedDateTime(undefined, "UTC"), `undefined`); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-invalid-iso-string.js index b9e504f210751..2f376b61907ed 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-invalid-iso-string.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.zoneddatetime.constructor -description: Various invalid ISO string values for calendar +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const invalidStrings = [ ["", "empty string"], ["1997-12-04[u-ca=iso8601]", "ISO string with calendar annotation"], + ["notacal", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js index 670fdfe7d0e06..618cb1aa2e1d7 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-invalid-iso-string.js @@ -12,10 +12,12 @@ const datetime = new Temporal.ZonedDateTime(0n, "UTC"); const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1970, monthCode: "M01", day: 1, calendar, timeZone: "UTC" }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal, timeZone: "UTC" }; assert.throws( RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime), diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..de343f8a05048 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(0n, "UTC"); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `${description} is not a valid calendar ID (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `${description} is not a valid calendar ID (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..3f50412024232 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/no-fractional-minutes-hours.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, new Temporal.ZonedDateTime(0n, "CET")), + `${description} not allowed in time string (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(0n, "CET"), arg), + `${description} not allowed in time string (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-invalid-iso-string.js index f3d350d162a51..2f769fc585142 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-invalid-iso-string.js @@ -4,7 +4,7 @@ /*--- esid: sec-temporal.zoneddatetime.from -description: Various invalid ISO string values for calendar in a property bag +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -12,10 +12,12 @@ const timeZone = "UTC"; const invalidStrings = [ ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => Temporal.ZonedDateTime.from(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-object.js new file mode 100644 index 0000000000000..824a46eac4cfe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-object.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: A ZonedDateTime is valid in a property bag for a time zone +features: [Temporal] +---*/ + +const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: new Temporal.ZonedDateTime(0n, "UTC") }); +assert.sameValue(result.timeZoneId, "UTC", 'Time zone created from ZonedDateTime object'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..c9837fee874d0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Invalid calendar string should throw RangeError +features: [Temporal] +---*/ + +const timeZone = "UTC"; + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-invalid.js new file mode 100644 index 0000000000000..b25fc8cafdebb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-invalid.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Check that UTC offsets are parsed properly +features: [Temporal] +---*/ + +const invalidStrings = [ + // UTCOffset must be of the form hh:mm:ss or hhmmss"); + "2025-01-01T00:00:00+00:0000[UTC]", + "2025-01-01T00:00:00+0000:00[UTC]", + // Invalid date or time components, valid offset + "202501-01T00:00:00+00:00[UTC]", + "2025-0101T00:00:00+00:00[UTC]", + "2025-01-01T00:0000+00:00[UTC]", + "2025-01-01T0000:00+00:00[UTC]", +]; + +invalidStrings.forEach((s) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(s), + `invalid date-time string (${s})` + ); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js index 874ca33f46473..4abeb74aed4de 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js @@ -10,6 +10,8 @@ description: > features: [BigInt, Symbol, Temporal] ---*/ +assert.throws(TypeError, () => Temporal.ZonedDateTime.from(), "no argument"); + const primitiveTests = [ [undefined, "undefined"], [null, "null"], @@ -26,6 +28,14 @@ for (const [arg, description] of primitiveTests) { () => Temporal.ZonedDateTime.from(arg), `${description} does not convert to a valid ISO string` ); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.from(arg, options), + `${description} does not convert to a valid ISO string with options ${options}` + ); + } } const typeErrorTests = [ @@ -37,6 +47,10 @@ const typeErrorTests = [ for (const [arg, description] of typeErrorTests) { assert.throws(TypeError, () => Temporal.ZonedDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`); + + for (const options of [undefined, { overflow: 'constrain' }, { overflow: 'reject' }]) { + assert.throws(TypeError, () => Temporal.ZonedDateTime.from(arg, options), `${description} is not a valid property bag and does not convert to a string with options ${options}`); + } } reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/month-code-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/month-code-wrong-type.js new file mode 100644 index 0000000000000..151d4ac3f9f22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/month-code-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Month code must be a string +features: [Temporal] +---*/ + +const monthCodeValues = [ + 5, 5n, false, Symbol(), null, { toString: () => 5 } +]; + +const year = 2026; + +for (const monthCode of monthCodeValues) { + assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ + year, + monthCode, + day: 1, hour: 12, minute: 34, timeZone: "UTC" + }), typeof monthCode === 'symbol' ? + "Symbol should be rejected as month code" : + `month code ${monthCode} should be rejected`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/negative-month-or-day.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/negative-month-or-day.js new file mode 100644 index 0000000000000..7f03e77368884 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/negative-month-or-day.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Months and days must be non-negative integers +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, day: 1, timeZone: "UTC", month: -1 })); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 1, timeZone: "UTC", day: -1 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..04f1020887b51 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/no-fractional-minutes-hours.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-string-invalid.js index 14190a87cf36a..7598548df1850 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-string-invalid.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-string-invalid.js @@ -4,13 +4,14 @@ /*--- esid: sec-temporal.zoneddatetime.from -description: Validation of monthCode +description: Validation of offset features: [Temporal] ---*/ -const bag = { year: 2024, monthCode: "M10", day: 3, timeZone: "UTC" }; - -["garbage", "00:00", "+000:00", "-00:000", "-00:00:000", "+00:00.0", "+00:00:00.0000000000"].forEach((offset) => { +// "+00:0000" is invalid (the hour/minute and minute/second separator +// or lack thereof needs to match). A valid offset would be either +// +00:00:00 or +000000. +["garbage", "00:00", "+000:00", "-00:000", "-00:00:000", "+00:00.0", "+00:00:00.0000000000", "+00:0000"].forEach((offset) => { assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ offset, year: 2024, monthCode: "M10", day: 3, timeZone: "UTC" }), `UTC offset '${offset}' is not well-formed`); }); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/roundtrip-from-property-bag.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/roundtrip-from-property-bag.js index 7d77669c5ff6f..aaf1c5028de6e 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/roundtrip-from-property-bag.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/roundtrip-from-property-bag.js @@ -13,20 +13,30 @@ features: [Temporal, Intl.Era-monthcode] const options = { overflow: "reject" }; -testRoundtrip(2000); -testRoundtrip(1); +const testData = [ + [2000, 1, "M01", 1], + [1, 1, "M01", 1], + [2021, 7, "M07", 15], + [2021, 7, "M07", 3], + [2021, 12, "M12", 31], + [2021, 7, "M07", 15], +]; -function testRoundtrip(year) { - const dateFromYearMonth = Temporal.ZonedDateTime.from({ year, month: 1, day: 1, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }, options); +for (const [year, month, monthCode, day] of testData) { + testRoundtrip(year, month, monthCode, day); +} + +function testRoundtrip(year, month, monthCode, day) { + const dateFromYearMonth = Temporal.ZonedDateTime.from({ year, month, day, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }, options); TemporalHelpers.assertPlainDateTime( dateFromYearMonth.toPlainDateTime(), - year, 1, "M01", 1, 12, 34, 56, 987, 654, 321, + year, month, monthCode, day, 12, 34, 56, 987, 654, 321, `${dateFromYearMonth} - created from year and month`); - const dateFromYearMonthCode = Temporal.ZonedDateTime.from({ year, monthCode: "M01", day: 1, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }, options); + const dateFromYearMonthCode = Temporal.ZonedDateTime.from({ year, monthCode, day, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }, options); TemporalHelpers.assertPlainDateTime( dateFromYearMonthCode.toPlainDateTime(), - year, 1, "M01", 1, 12, 34, 56, 987, 654, 321, + year, month, monthCode, day, 12, 34, 56, 987, 654, 321, `${dateFromYearMonthCode} - created from year and month code`); } diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..881a035f0d820 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Maximum allowed duration adding to minimum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const min = Temporal.ZonedDateTime.from({ year: -271821, month: 4, day: 20, timeZone: "UTC" }); + +const maxCases = [ + ["P547581Y4M24D", "string with max years"], + [{ years: 547581, months: 4, days: 24 }, "property bag with max years"], + ["P6570976M24D", "string with max months"], + [{ months: 6570976, days: 24 }, "property bag with max months"], + ["P28571428W4D", "string with max weeks"], + [{ weeks: 28_571_428, days: 4 }, "property bag with max weeks"], + ["P200000000D", "string with max days"], + [{ days: 200_000_000 }, "property bag with max days"], + ["PT4800000000H", "string with max hours"], + [{ hours: 4_800_000_000 }, "property bag with max hours"], + ["PT288000000000M", "string with max minutes"], + [{ minutes: 288_000_000_000 }, "property bag with max minutes"], + ["PT17280000000000S", "string with max seconds"], + [{ seconds: 17_280_000_000_000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = min.add(arg); + TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`) +} + +const max = Temporal.ZonedDateTime.from({ year: 275760, month: 9, day: 13, timeZone: "UTC" }); + +const minCases = [ + ["-P547581Y4M23D", "string with max years"], + [{ years: -547581, months: -4, days: -23 }, "property bag with max years"], + ["-P6570976M23D", "string with max months"], + [{ months: -6570976, days: -23 }, "property bag with max months"], + ["-P28571428W4D", "string with max weeks"], + [{ weeks: -28_571_428, days: -4 }, "property bag with max weeks"], + ["-P200000000D", "string with max days"], + [{ days: -200_000_000 }, "property bag with max days"], + ["-PT4800000000H", "string with max hours"], + [{ hours: -4_800_000_000 }, "property bag with max hours"], + ["-PT288000000000M", "string with max minutes"], + [{ minutes: -288_000_000_000 }, "property bag with max minutes"], + ["-PT17280000000000S", "string with max seconds"], + [{ seconds: -17_280_000_000_000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = max.add(arg); + TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), -271821, 4, "M04", 20, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/basic.js new file mode 100644 index 0000000000000..4859c33a87d59 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/basic.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: The "day" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +// (new Temporal.PlainDateTime(2021, 7, 15, 5, 30, 13)).toZonedDateTime("UTC") +assert.sameValue(new Temporal.ZonedDateTime(1626327013000000000n, "UTC").day, 15); +assert.sameValue(Temporal.ZonedDateTime.from('2019-03-18T05:30:13+00:00[UTC]').day, 18); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/basic.js new file mode 100644 index 0000000000000..ad1b527cf9583 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/basic.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: Checking day of year for a "normal" case (non-undefined, non-boundary case, etc.) +features: [Temporal] +---*/ + +const datetime = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, "iso8601").toZonedDateTime("UTC"); +assert.sameValue(datetime.dayOfYear, 323, "check day of year information"); + +assert.sameValue((new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 23); +assert.sameValue((new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 54); +assert.sameValue((new Temporal.PlainDateTime(1996, 3, 23, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 83); +assert.sameValue((new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 82); +assert.sameValue((new Temporal.PlainDateTime(1997, 12, 31, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1996, 12, 31, 5, 30, 13)).toZonedDateTime("UTC").dayOfYear, 366); +assert.sameValue(Temporal.PlainDateTime.from("1997-01-23T05:30:13").toZonedDateTime("UTC").dayOfYear, 23); +assert.sameValue(Temporal.PlainDateTime.from("1997-02-23T05:30:13").toZonedDateTime("UTC").dayOfYear, 54); +assert.sameValue(Temporal.PlainDateTime.from("1996-03-23T05:30:13").toZonedDateTime("UTC").dayOfYear, 83); +assert.sameValue(Temporal.PlainDateTime.from("1997-03-23T05:30:13").toZonedDateTime("UTC").dayOfYear, 82); +assert.sameValue(Temporal.PlainDateTime.from("1997-12-31T05:30:13").toZonedDateTime("UTC").dayOfYear, 365); +assert.sameValue(Temporal.PlainDateTime.from("1996-12-31T05:30:13").toZonedDateTime("UTC").dayOfYear, 366); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/basic.js new file mode 100644 index 0000000000000..f874ba05fae56 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/basic.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: Checking days in month for a "normal" case (non-undefined, non-boundary case, etc.) +features: [Temporal] +---*/ + +const tests = [ + [1976, 2, 18, 29], + [1976, 11, 18, 30], + [1976, 12, 18, 31], + [1977, 2, 18, 28], + [1997, 1, 23, 31], + [1996, 2, 23, 29], + [2000, 2, 23, 29], + [1997, 2, 23, 28], + [1997, 3, 23, 31], + [1997, 4, 23, 30], + [1997, 5, 23, 31], + [1997, 6, 23, 30], + [1997, 7, 23, 31], + [1997, 8, 23, 31], + [1997, 9, 23, 30], + [1997, 10, 23, 31], + [1997, 11, 23, 30], + [1997, 12, 23, 31], +]; +for (const [y, m, d, expected] of tests) { + const plainDateTime = new Temporal.PlainDateTime(y, m, d, 15, 23, 30, 123, 456, 789); + assert.sameValue(plainDateTime.toZonedDateTime("UTC").daysInMonth, expected, `${expected} days in the month of ${plainDateTime}`); +} + + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/basic.js new file mode 100644 index 0000000000000..624f47cc8e865 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/basic.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: Checking days in week for a "normal" case (non-undefined, non-boundary case, etc.) +features: [Temporal] +---*/ + +const tests = [ + new Temporal.ZonedDateTime(189357810123456789n, "UTC"), + new Temporal.ZonedDateTime(217178610123456789n, "UTC"), + new Temporal.ZonedDateTime(220893810123456789n, "UTC"), +]; +for (const zonedDateTime of tests) { + assert.sameValue(zonedDateTime.daysInWeek, 7, `Seven days in the week of ${zonedDateTime}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js index 1d18d79ffd4a7..112af64bda4d7 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js @@ -12,5 +12,16 @@ assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).daysIn 366, "leap year"); assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).daysInYear, 365, "non-leap year"); +assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); +assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 366); +assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").daysInYear, 365); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js index 65a0ea66fd403..ff232f2ab8c39 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-invalid-iso-string.js @@ -4,8 +4,7 @@ /*--- esid: sec-temporal.zoneddatetime.prototype.equals -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -13,11 +12,13 @@ const timeZone = "UTC"; const instance = new Temporal.ZonedDateTime(0n, timeZone); const invalidStrings = [ - ["", "empty string"] + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.equals(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..23ea357010f3e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..5464c0d9298e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "CET") + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js index 1b745bc8f4f9c..2b5e0d2c1e8bb 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js @@ -12,5 +12,16 @@ assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).inLeap true, "leap year"); assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).inLeapYear, false, "non-leap year"); +assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); +assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, true); +assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).toZonedDateTime("UTC").inLeapYear, false); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/basic.js new file mode 100644 index 0000000000000..f8881129bfc16 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/basic.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: Month property for ZonedDateTime +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.ZonedDateTime(0n, "UTC")).month, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/basic.js new file mode 100644 index 0000000000000..d9890b3cf570b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/basic.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.monthcode +description: monthCode property for ZonedDateTime +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.ZonedDateTime(0n, "UTC")).monthCode, 'M01'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/basic.js new file mode 100644 index 0000000000000..07bfcbc22463e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/basic.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: Checking months in year for a "normal" case (non-undefined, non-boundary case, etc.) +features: [Temporal] +---*/ + +const zdt = new Temporal.ZonedDateTime(217178610123456789n, "UTC"); +assert.sameValue(zdt.monthsInYear, 12, "check months in year information"); +assert.sameValue((new Temporal.ZonedDateTime(-23205637787000000000n, "UTC")).monthsInYear, 12); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-increments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-increments.js index 7d4eb667083c4..49c754923f0bf 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-increments.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-increments.js @@ -67,5 +67,21 @@ TemporalHelpers.assertZonedDateTimesEqual(zdt.round({ roundingIncrement: 1 }), expected1Day); +const unitsAndIncrements = { + "hour": [1, 2, 4, 6, 8, 12], + "minute": [1, 3, 5, 6, 10, 30], + "second": [1, 3, 5, 6, 10, 30], + "millisecond": [1, 5, 10, 20, 25, 50, 100, 500], + "microsecond": [1, 5, 10, 20, 25, 50, 100, 500], + "nanosecond": [1, 5, 10, 20, 25, 50, 100, 500], +}; + +// Just check that each combination of unit and increment doesn't throw +Object.entries(unitsAndIncrements).forEach(([unit, increments]) => { + increments.forEach((increment) => { + const result = zdt.round({ smallestUnit: unit, roundingMode: "ceil", roundingIncrement: increment }); + assert.sameValue(result instanceof Temporal.ZonedDateTime, true, `${unit} ${increment}`); + }) +}); reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js index 883927ee3b340..4ffa47c736f8c 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-invalid-iso-string.js @@ -4,8 +4,7 @@ /*--- esid: sec-temporal.zoneddatetime.prototype.since -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -13,11 +12,13 @@ const timeZone = "UTC"; const instance = new Temporal.ZonedDateTime(0n, timeZone); const invalidStrings = [ - ["", "empty string"] + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.since(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..1a239351c65b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..87dce645d6bb9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "CET") + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max-plus-min-date.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max-plus-min-date.js new file mode 100644 index 0000000000000..4c8c6166e64d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max-plus-min-date.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Maximum allowed duration subtracting from maximum allowed date +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const max = Temporal.ZonedDateTime.from({ year: 275760, month: 9, day: 13, timeZone: "UTC" }); + +const maxCases = [ + ["P547581Y4M23D", "string with max years"], + [{ years: 547581, months: 4, days: 23 }, "property bag with max years"], + ["P6570976M23D", "string with max months"], + [{ months: 6570976, days: 23 }, "property bag with max months"], + ["P28571428W4D", "string with max weeks"], + [{ weeks: 28_571_428, days: 4 }, "property bag with max weeks"], + ["P200000000D", "string with max days"], + [{ days: 200_000_000 }, "property bag with max days"], + ["PT4800000000H", "string with max hours"], + [{ hours: 4_800_000_000 }, "property bag with max hours"], + ["PT288000000000M", "string with max minutes"], + [{ minutes: 288_000_000_000 }, "property bag with max minutes"], + ["PT17280000000000S", "string with max seconds"], + [{ seconds: 17_280_000_000_000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = max.subtract(arg); + TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), -271821, 4, "M04", 20, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`); +} + +const min = Temporal.ZonedDateTime.from({ year: -271821, month: 4, day: 20, timeZone: "UTC" }); + +const minCases = [ + ["-P547581Y4M24D", "string with max years"], + [{ years: -547581, months: -4, days: -24 }, "property bag with max years"], + ["-P6570976M24D", "string with max months"], + [{ months: -6570976, days: -24 }, "property bag with max months"], + ["-P28571428W4D", "string with max weeks"], + [{ weeks: -28_571_428, days: -4 }, "property bag with max weeks"], + ["-P200000000D", "string with max days"], + [{ days: -200_000_000 }, "property bag with max days"], + ["-PT4800000000H", "string with max hours"], + [{ hours: -4_800_000_000 }, "property bag with max hours"], + ["-PT288000000000M", "string with max minutes"], + [{ minutes: -288_000_000_000 }, "property bag with max minutes"], + ["-PT17280000000000S", "string with max seconds"], + [{ seconds: -17_280_000_000_000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = min.subtract(arg); + TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 275760, 9, "M09", 13, 0, 0, 0, 0, 0, 0, `operation succeeds with ${descr}`) +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js index fb4af69eca8fb..42577b1ab887b 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-invalid-iso-string.js @@ -4,8 +4,7 @@ /*--- esid: sec-temporal.zoneddatetime.prototype.until -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ @@ -13,11 +12,13 @@ const timeZone = "UTC"; const instance = new Temporal.ZonedDateTime(0n, timeZone); const invalidStrings = [ - ["", "empty string"] + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], + ["notacal", "Unknown calendar"], ]; -for (const [calendar, description] of invalidStrings) { - const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; +for (const [cal, description] of invalidStrings) { + const arg = { year: 1970, monthCode: "M11", day: 18, calendar: cal }; assert.throws( RangeError, () => instance.until(arg), diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js new file mode 100644 index 0000000000000..8e6ea7028d3d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-invalid-iso-string.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Invalid ISO string as calendar should throw RangeError +features: [Temporal] +---*/ + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const invalidStrings = [ + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} is not a valid calendar ID` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/no-fractional-minutes-hours.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/no-fractional-minutes-hours.js new file mode 100644 index 0000000000000..fe1a49f1e5477 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/no-fractional-minutes-hours.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fractional minutes or hours in time string should throw RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "CET") + +const invalidStrings = [ + ["2025-04-03T05:07.123", "Fractional minutes"], + ["2025-04-03T12.5", "Fractional hours"], +]; + +for (const [arg, description] of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `${description} not allowed in time string` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/basic.js new file mode 100644 index 0000000000000..6e08e69a3105a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/basic.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: Checking week of year for a "normal" case, as well as for dates near the turn of the year. +features: [Temporal] +---*/ + +const zdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); +assert.sameValue(zdt.weekOfYear, 47, "check week of year information"); + +for (let i = 29; i <= 31; ++i) { + const zdt = new Temporal.PlainDateTime(1975, 12, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 1, `${zdt} should be in week 1`); +} +for (let i = 1; i <= 4; ++i) { + const zdt = new Temporal.PlainDateTime(1976, 1, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 1, `${zdt} should be in week 1`); +} +for (let i = 5; i <= 11; ++i) { + const zdt = new Temporal.PlainDateTime(1976, 1, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 2, `${zdt} should be in week 2`); +} +for (let i = 20; i <= 26; ++i) { + const zdt = new Temporal.PlainDateTime(1976, 12, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 52, `${zdt} should be in week 52`); +} +for (let i = 27; i <= 31; ++i) { + const zdt = new Temporal.PlainDateTime(1976, 12, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 53, `${zdt} should be in week 53`); +} +for (let i = 1; i <= 2; ++i) { + const zdt = new Temporal.PlainDateTime(1977, 1, i, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + assert.sameValue(zdt.weekOfYear, 53, `${zdt} should be in week 53`); +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-invalid-iso-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-invalid-iso-string.js index d439f14a9142d..4d5461c4bc8bc 100644 --- a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-invalid-iso-string.js +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-invalid-iso-string.js @@ -4,15 +4,15 @@ /*--- esid: sec-temporal.zoneddatetime.prototype.withcalendar -description: > - An ISO string that cannot be converted to a calendar ID should throw a RangeError +description: Invalid ISO string as calendar should throw RangeError features: [Temporal] ---*/ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); const invalidStrings = [ - ["", "empty string"] + ["", "empty string"], + ["1997-12-04[u-ca=notacal]", "Unknown calendar"], ]; for (const [arg, description] of invalidStrings) { diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..3da967d8f5906 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: A time string is valid input for Calendar; default is iso8601 +features: [Temporal] +---*/ + +const tests = [ + "15:23", + "15:23:30", + "15:23:30.123", + "15:23:30.123456", + "15:23:30.123456789", + "1976-11-18T15:23:30.1", + "1976-11-18T15:23:30.12", + "1976-11-18T15:23:30.123", + "1976-11-18T15:23:30.1234", + "1976-11-18T15:23:30.12345", + "1976-11-18T15:23:30.123456", + "1976-11-18T15:23:30.1234567", + "1976-11-18T15:23:30.12345678", + "1976-11-18T15:23:30.123456789", + "1976-11-18T15:23:30,12", + "1976-11-18T15:23:30.12-02:00", + "152330", + "152330.1", + "152330-08", + "152330.1-08", + "152330-0800", + "152330.1-0800", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "15", + "T15:23:30", + "t152330", +]; + +const instance = Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, hour: 12, minute: 34, timeZone: "UTC"}); + +tests.forEach((arg) => { + const result = instance.withCalendar(arg); + assert.sameValue(result.calendarId, "iso8601", `Calendar created from string "${arg}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/basic.js new file mode 100644 index 0000000000000..9cd9c9bafa46a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/basic.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: The "year" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.ZonedDateTime(0n, "UTC")).year, 1970); +assert.sameValue(Temporal.ZonedDateTime.from('2019-03-15T15:30:26+00:00[UTC]').year, 2019); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/shell.js b/js/src/tests/test262/built-ins/Temporal/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/built-ins/Temporal/shell.js +++ b/js/src/tests/test262/built-ins/Temporal/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindate-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindate-formatting-timezonename.js new file mode 100644 index 0000000000000..c37a075a9be95 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindate-formatting-timezonename.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDate can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const date = new Temporal.PlainDate(2026, 1, 5); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 5)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.format(date); + assert.sameValue(typeof result, "string", + `can format a PlainDate with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindatetime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindatetime-formatting-timezonename.js new file mode 100644 index 0000000000000..f5b9b23f5dc24 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaindatetime-formatting-timezonename.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDateTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const pdt = new Temporal.PlainDateTime(2026, 1, 5, 11, 22); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 5, 11, 22)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.format(pdt); + assert.sameValue(typeof result, "string", + `can format a PlainDateTime with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainmonthday-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainmonthday-formatting-timezonename.js new file mode 100644 index 0000000000000..22cab0d710987 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainmonthday-formatting-timezonename.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainMonthDay can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US-u-ca-gregory] +features: [Temporal] +---*/ + +const locale = "en-US-u-ca-gregory"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const md = new Temporal.PlainMonthDay(1, 5, "gregory", 1972); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(1972, 0, 5)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.format(md); + assert.sameValue(typeof result, "string", + `can format a PlainMonthDay with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaintime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaintime-formatting-timezonename.js new file mode 100644 index 0000000000000..d53a530ed5fbd --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plaintime-formatting-timezonename.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const time = new Temporal.PlainTime(12, 34); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(1970, 0, 1, 12, 34)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.format(time); + assert.sameValue(typeof result, "string", + `can format a PlainTime with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainyearmonth-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainyearmonth-formatting-timezonename.js new file mode 100644 index 0000000000000..15340e24d4baa --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/format/temporal-plainyearmonth-formatting-timezonename.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainYearMonth can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const ym = new Temporal.PlainYearMonth(2026, 1, "gregory", 1); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 1)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.format(ym); + assert.sameValue(typeof result, "string", + `can format a PlainYearMonth with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindate-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindate-formatting-timezonename.js new file mode 100644 index 0000000000000..a2aee84f13618 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindate-formatting-timezonename.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDate can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const date1 = new Temporal.PlainDate(2026, 1, 5); +const date2 = new Temporal.PlainDate(2026, 1, 6); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 5)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.formatRange(date1, date2); + assert.sameValue(typeof result, "string", + `can format a PlainDate with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindatetime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindatetime-formatting-timezonename.js new file mode 100644 index 0000000000000..552f2b004874e --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaindatetime-formatting-timezonename.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDateTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const pdt1 = new Temporal.PlainDateTime(2026, 1, 5, 11, 22); +const pdt2 = new Temporal.PlainDateTime(2026, 1, 5, 11, 23); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 5, 11, 22)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.formatRange(pdt1, pdt2); + assert.sameValue(typeof result, "string", + `can format a PlainDateTime with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainmonthday-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainmonthday-formatting-timezonename.js new file mode 100644 index 0000000000000..6ebfacad8cab9 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainmonthday-formatting-timezonename.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainMonthDay can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US-u-ca-gregory] +features: [Temporal] +---*/ + +const locale = "en-US-u-ca-gregory"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const md1 = new Temporal.PlainMonthDay(1, 5, "gregory", 1972); +const md2 = new Temporal.PlainMonthDay(1, 6, "gregory", 1972); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(1972, 0, 5)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.formatRange(md1, md2); + assert.sameValue(typeof result, "string", + `can format a PlainMonthDay with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaintime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaintime-formatting-timezonename.js new file mode 100644 index 0000000000000..f812c5fa5ff32 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plaintime-formatting-timezonename.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const time1 = new Temporal.PlainTime(12, 34); +const time2 = new Temporal.PlainTime(18, 45); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(1970, 0, 1, 12, 34)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.formatRange(time1, time2); + assert.sameValue(typeof result, "string", + `can format a PlainTime with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainyearmonth-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainyearmonth-formatting-timezonename.js new file mode 100644 index 0000000000000..05c551444494c --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRange/temporal-plainyearmonth-formatting-timezonename.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainYearMonth can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const ym1 = new Temporal.PlainYearMonth(2026, 1, "gregory", 1); +const ym2 = new Temporal.PlainYearMonth(2026, 2, "gregory", 1); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + + const timeZoneDisplayName = dtf.formatToParts(Date.UTC(2026, 0, 1)).find(({ type }) => { + return type === 'timeZoneName'; + }).value; + + const result = dtf.formatRange(ym1, ym2); + assert.sameValue(typeof result, "string", + `can format a PlainYearMonth with timeZoneName = ${timeZoneNameStyle}`); + assert.sameValue(result.indexOf(timeZoneDisplayName), -1, + `"${result}" should not include ${timeZoneDisplayName}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/chinese-calendar-dates.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/chinese-calendar-dates.js index 0b5c8ef311ad1..677b360bac9b1 100644 --- a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/chinese-calendar-dates.js +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/chinese-calendar-dates.js @@ -21,11 +21,13 @@ function compareFormatRangeToPartsSnapshot(isoString1, isoString2, expectedCompo for (let [expectedType, expectedValue] of Object.entries(list)) { const part = actualComponents.find(({type, source}) => type === expectedType && source == sourceVal); const contextMessage = `${expectedType} component of ${isoString} formatted in ${calendar}`; - assert.notSameValue(part, undefined, contextMessage); - if (typeof part.value === "string") - assert(part.value === expectedValue || (expectedValue.length === 1 && part.value === '0' + expectedValue)); - else - assert.sameValue(part.value, `${expectedValue}`, contextMessage); + assert.notSameValue(part, undefined, `${contextMessage} is missing`); + assert.sameValue(typeof part.value, "string", `${contextMessage} is not a string`); + assert( + part.value === String(expectedValue) || + part.value === String(expectedValue).padStart(2, "0"), + `${contextMessage} has unexpected value` + ); } } diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/dangi-calendar-dates.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/dangi-calendar-dates.js index e44637a7f4db2..19c680ac1cbde 100644 --- a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/dangi-calendar-dates.js +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/dangi-calendar-dates.js @@ -21,11 +21,13 @@ function compareFormatRangeToPartsSnapshot(isoString1, isoString2, expectedCompo for (let [expectedType, expectedValue] of Object.entries(list)) { const part = actualComponents.find(({type, source}) => type === expectedType && source == sourceVal); const contextMessage = `${expectedType} component of ${isoString} formatted in ${calendar}`; - assert.notSameValue(part, undefined, contextMessage); - if (typeof part.value === "string") - assert(part.value === expectedValue || (expectedValue.length === 1 && part.value === '0' + expectedValue)); - else - assert.sameValue(part.value, `${expectedValue}`, contextMessage); + assert.notSameValue(part, undefined, `${contextMessage} is missing`); + assert.sameValue(typeof part.value, "string", `${contextMessage} is not a string`); + assert( + part.value === String(expectedValue) || + part.value === String(expectedValue).padStart(2, "0"), + `${contextMessage} has unexpected value` + ); } } diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindate-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindate-formatting-timezonename.js new file mode 100644 index 0000000000000..3ba262678a615 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindate-formatting-timezonename.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDate can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const date1 = new Temporal.PlainDate(2026, 1, 5); +const date2 = new Temporal.PlainDate(2026, 1, 6); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatRangeToParts(date1, date2); + assert(Array.isArray(result), + `can format a PlainDate with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainDate with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindatetime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindatetime-formatting-timezonename.js new file mode 100644 index 0000000000000..a3c5e62a52819 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaindatetime-formatting-timezonename.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDateTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const pdt1 = new Temporal.PlainDateTime(2026, 1, 5, 11, 22); +const pdt2 = new Temporal.PlainDateTime(2026, 1, 5, 11, 23); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatRangeToParts(pdt1, pdt2); + assert(Array.isArray(result), + `can format a PlainDateTime with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainDateTime with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainmonthday-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainmonthday-formatting-timezonename.js new file mode 100644 index 0000000000000..7f48929020fa3 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainmonthday-formatting-timezonename.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainMonthDay can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US-u-ca-gregory] +features: [Temporal] +---*/ + +const locale = "en-US-u-ca-gregory"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const md1 = new Temporal.PlainMonthDay(1, 5, "gregory", 1972); +const md2 = new Temporal.PlainMonthDay(1, 6, "gregory", 1972); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatRangeToParts(md1, md2); + assert(Array.isArray(result), + `can format a PlainMonthDay with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainMonthDay with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaintime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaintime-formatting-timezonename.js new file mode 100644 index 0000000000000..886008512805b --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plaintime-formatting-timezonename.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const time1 = new Temporal.PlainTime(12, 34); +const time2 = new Temporal.PlainTime(18, 45); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatRangeToParts(time1, time2); + assert(Array.isArray(result), + `can format a PlainTime with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainTime with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainyearmonth-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainyearmonth-formatting-timezonename.js new file mode 100644 index 0000000000000..86adccd28fcf6 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-plainyearmonth-formatting-timezonename.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainYearMonth can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const ym1 = new Temporal.PlainYearMonth(2026, 1, "gregory", 1); +const ym2 = new Temporal.PlainYearMonth(2026, 2, "gregory", 1); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatRangeToParts(ym1, ym2); + assert(Array.isArray(result), + `can format a PlainYearMonth with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainYearMonth with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/chinese-calendar-dates.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/chinese-calendar-dates.js index 3e4d219aafffe..1a8be0b83ef2e 100644 --- a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/chinese-calendar-dates.js +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/chinese-calendar-dates.js @@ -18,11 +18,13 @@ function compareFormatToPartsSnapshot(isoString, expectedComponents) { for (let [expectedType, expectedValue] of Object.entries(expectedComponents)) { const part = actualComponents.find(({type}) => type === expectedType); const contextMessage = `${expectedType} component of ${isoString} formatted in ${calendar}`; - assert.notSameValue(part, undefined, contextMessage); - if (typeof part.value === "string") - assert(part.value === expectedValue || (expectedValue.length === 1 && part.value === '0' + expectedValue)); - else - assert.sameValue(part.value, `${expectedValue}`, contextMessage); + assert.notSameValue(part, undefined, `${contextMessage} is missing`); + assert.sameValue(typeof part.value, "string", `${contextMessage} is not a string`); + assert( + part.value === String(expectedValue) || + part.value === String(expectedValue).padStart(2, "0"), + `${contextMessage} has unexpected value` + ); } } diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/dangi-calendar-dates.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/dangi-calendar-dates.js index a04417b2a6fb6..047bf3094be77 100644 --- a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/dangi-calendar-dates.js +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/dangi-calendar-dates.js @@ -22,11 +22,13 @@ function compareFormatToPartsSnapshot(isoString, expectedComponents) { for (let [expectedType, expectedValue] of Object.entries(expectedComponents)) { const part = actualComponents.find(({type}) => type === expectedType); const contextMessage = `${expectedType} component of ${isoString} formatted in ${calendar}`; - assert.notSameValue(part, undefined, contextMessage); - if (typeof part.value === "string") - assert(part.value === expectedValue || (expectedValue.length === 1 && part.value === '0' + expectedValue)); - else - assert.sameValue(part.value, `${expectedValue}`, contextMessage); + assert.notSameValue(part, undefined, `${contextMessage} is missing`); + assert.sameValue(typeof part.value, "string", `${contextMessage} is not a string`); + assert( + part.value === String(expectedValue) || + part.value === String(expectedValue).padStart(2, "0"), + `${contextMessage} has unexpected value` + ); } } diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindate-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindate-formatting-timezonename.js new file mode 100644 index 0000000000000..dfc2b21df1b51 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindate-formatting-timezonename.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDate can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const date = new Temporal.PlainDate(2026, 1, 5); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatToParts(date); + assert(Array.isArray(result), + `can format a PlainDate with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainDate with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindatetime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindatetime-formatting-timezonename.js new file mode 100644 index 0000000000000..f6e5ed31499fd --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaindatetime-formatting-timezonename.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainDateTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const pdt = new Temporal.PlainDateTime(2026, 1, 5, 11, 22); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatToParts(pdt); + assert(Array.isArray(result), + `can format a PlainDateTime with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainDateTime with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainmonthday-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainmonthday-formatting-timezonename.js new file mode 100644 index 0000000000000..8960fec04d012 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainmonthday-formatting-timezonename.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainMonthDay can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US-u-ca-gregory] +features: [Temporal] +---*/ + +const locale = "en-US-u-ca-gregory"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const md = new Temporal.PlainMonthDay(1, 5, "gregory", 1972); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatToParts(md); + assert(Array.isArray(result), + `can format a PlainMonthDay with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainMonthDay with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaintime-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaintime-formatting-timezonename.js new file mode 100644 index 0000000000000..6f8fbccb82561 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plaintime-formatting-timezonename.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainTime can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const time = new Temporal.PlainTime(12, 34); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatToParts(time); + assert(Array.isArray(result), + `can format a PlainTime with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainTime with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainyearmonth-formatting-timezonename.js b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainyearmonth-formatting-timezonename.js new file mode 100644 index 0000000000000..8a7bd23753891 --- /dev/null +++ b/js/src/tests/test262/intl402/DateTimeFormat/prototype/formatToParts/temporal-plainyearmonth-formatting-timezonename.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-datetime-format-functions +description: > + PlainYearMonth can be formatted with a formatter created with the timeZoneName + option, but no time zone name is included. +locale: [en-US] +features: [Temporal] +---*/ + +const locale = "en-US"; +const timeZoneNameStyles = [ + "long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric" +]; +const ym = new Temporal.PlainYearMonth(2026, 1, "gregory", 1); + +for (const timeZoneNameStyle of timeZoneNameStyles) { + const dtf = new Intl.DateTimeFormat(locale, { timeZoneName: timeZoneNameStyle }); + const result = dtf.formatToParts(ym); + assert(Array.isArray(result), + `can format a PlainYearMonth with timeZoneName = ${timeZoneNameStyle}`); + for (const { type } of result) { + assert.notSameValue(type, "timeZoneName", + `formatting a PlainYearMonth with timeZoneName = ${timeZoneNameStyle} should not print a time zone`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-accepted-by-DisplayNames.js b/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-accepted-by-DisplayNames.js index b1c34fd51cb5e..a050489fc609c 100644 --- a/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-accepted-by-DisplayNames.js +++ b/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-accepted-by-DisplayNames.js @@ -35,10 +35,7 @@ for (let calendar of calendars) { } for (let calendar of allCalendars()) { - if (typeof obj.of(calendar) === "string") { - assert(calendars.includes(calendar), - `${calendar} supported but not returned by supportedValuesOf`); - } else { + if (typeof obj.of(calendar) !== "string") { assert(!calendars.includes(calendar), `${calendar} not supported but returned by supportedValuesOf`); } diff --git a/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-required-by-intl-era-monthcode.js b/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-required-by-intl-era-monthcode.js index 7d6e3ab8c03da..e0b1fa824053b 100644 --- a/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-required-by-intl-era-monthcode.js +++ b/js/src/tests/test262/intl402/Intl/supportedValuesOf/calendars-required-by-intl-era-monthcode.js @@ -19,8 +19,12 @@ info: | 9. Return CreateArrayFromList( list ). AvailableCalendars ( ) - The implementation-defined abstract operation AvailableCalendars takes no arguments and returns a List of calendar types. The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form (6.9) identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both of "islamicc" and "islamic-civil"). The List must include the Calendar Type value of every row of Table 1, except the header row. -locale: [en] + The implementation-defined abstract operation AvailableCalendars takes no arguments and returns + a List of calendar types. The returned List is sorted according to lexicographic code unit order, + and contains unique calendar types in canonical form (6.9) identifying the calendars for which + the implementation provides the functionality of Intl.DateTimeFormat objects, including their + aliases (e.g., both "islamicc" and "islamic-civil"). The List must consist + of the Calendar Type value of every row of Table 1, except the header row. features: [Intl-enumeration, Intl.Era-monthcode] ---*/ @@ -31,21 +35,32 @@ const requiredCalendars = [ "dangi", "ethioaa", "ethiopic", + // "ethiopic-amete-alem", // Alias for "ethioaa". "gregory", "hebrew", "indian", "islamic-civil", "islamic-tbla", "islamic-umalqura", + // "islamicc", // Alias for "islamic-civil". "iso8601", "japanese", "persian", "roc" -] +]; + +assert.compareArray( + requiredCalendars, + requiredCalendars.slice(0).sort(), + "requiredCalendars is sorted" +); const supportedCalendars = Intl.supportedValuesOf("calendar"); -for (const calendar of requiredCalendars) { - assert(supportedCalendars.includes(calendar), "Required calendar: " + calendar + " must be supported"); -} + +assert.compareArray( + supportedCalendars, + requiredCalendars, + "only the required calendars are supported" +); reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/PluralRules/prototype/select/notation.js b/js/src/tests/test262/intl402/PluralRules/prototype/select/notation.js index 24405bf5088e2..259aa85284883 100644 --- a/js/src/tests/test262/intl402/PluralRules/prototype/select/notation.js +++ b/js/src/tests/test262/intl402/PluralRules/prototype/select/notation.js @@ -21,37 +21,27 @@ var values = [ { value: 1e6, standard: "many", - engineering: "many", - scientific: "many", compact: "many", }, { value: 1.5e6, standard: "other", - engineering: "many", - scientific: "many", compact: "many", }, { value: 1e-6, standard: "one", - engineering: "many", - scientific: "many", compact: "one", }, ]; var prstandard = new Intl.PluralRules("fr", { notation: "standard" }); -var prengineering = new Intl.PluralRules("fr", { notation: "engineering" }); -var prscientific = new Intl.PluralRules("fr", { notation: "scientific" }); var prcompact = new Intl.PluralRules("fr", { notation: "compact" }); -for (var {value, standard, engineering, scientific, compact} of values) { +for (var {value, standard, compact} of values) { assert.sameValue(prstandard.select(value), standard, `standard notation: ${value}`); - assert.sameValue(prengineering.select(value), engineering, `engineering notation: ${value}`); - assert.sameValue(prscientific.select(value), scientific, `scientific notation: ${value}`); assert.sameValue(prcompact.select(value), compact, `compact notation: ${value}`); } diff --git a/js/src/tests/test262/intl402/Temporal/Duration/prototype/round/shell.js b/js/src/tests/test262/intl402/Temporal/Duration/prototype/round/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/Duration/prototype/round/shell.js +++ b/js/src/tests/test262/intl402/Temporal/Duration/prototype/round/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/Duration/prototype/total/shell.js b/js/src/tests/test262/intl402/Temporal/Duration/prototype/total/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/Duration/prototype/total/shell.js +++ b/js/src/tests/test262/intl402/Temporal/Duration/prototype/total/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..f67533fb9afd7 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.tolocalestring +description: Using both dateStyle and timeStyle should not throw +features: [Temporal] +---*/ + +const item = new Temporal.Instant(0n); +var result = item.toLocaleString("en", { dateStyle: "full", timeStyle: "full" }); +assert.sameValue(result.includes(":00"), true, "using both dateStyle and timeStyle should not throw"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/hourcycle.js b/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/hourcycle.js new file mode 100644 index 0000000000000..0e240f1328088 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/Instant/prototype/toLocaleString/hourcycle.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.prototype.tolocalestring +description: Hour cycle should be correctly set when defaults are used +features: [Temporal] +---*/ + +const output0 = "00:00:00"; +const output11 = "0:00:00"; +const output12 = "12:00:00"; +const output24 = "24:00:00"; + +const item = new Temporal.Instant(0n); +var result = item.toLocaleString("en", { hour12: false, timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hour12: false should include ${output0}`); + +result = item.toLocaleString("en", { hour12: true, timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hour12: true should include ${output12}`); + +result = item.toLocaleString("en", { hourCycle: "h23", timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hourCycle: h23 should include ${output0}`); + +result = item.toLocaleString("en", { hourCycle: "h24", timeZone: "UTC" }); +assert.sameValue(result.includes(output24), true, `output for hourCycle: h24 should include ${output24}`); + +result = item.toLocaleString("en", { hourCycle: "h11", timeZone: "UTC" }); +assert.sameValue(result.includes(output11), true, `output for hourCycle: h11 should include ${output11}`); + +result = item.toLocaleString("en", { hourCycle: "h12", timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hourCycle: h12 should include ${output12}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/canonicalize-calendar.js b/js/src/tests/test262/intl402/Temporal/PlainDate/canonicalize-calendar.js index 3760c3449db0e..e074eccfb89d3 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/canonicalize-calendar.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/canonicalize-calendar.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// Copyright (C) 2025 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,7 +8,12 @@ description: Calendar ID is canonicalized features: [Temporal] ---*/ -const result = new Temporal.PlainDate(2024, 7, 2, "islamicc"); +var result = new Temporal.PlainDate(2024, 7, 2, "islamicc"); assert.sameValue(result.calendarId, "islamic-civil", "calendar ID is canonicalized"); +// May need to be removed in the future. +// See https://github.com/tc39/ecma402/issues/285 +result = new Temporal.PlainDate(2024, 7, 2, "ethiopic-amete-alem"); +assert.sameValue(result.calendarId, "ethioaa", "calendar ID is canonicalized"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/compare/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/compare/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/compare/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/compare/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era-with-era-year.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era-with-era-year.js new file mode 100644 index 0000000000000..93a1e8bc9a9d4 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era-with-era-year.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: RangeError thrown if era is invalid for this calendar with year absent and eraYear present +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.PlainDate.from({ month: 1, day: 1, era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era.js new file mode 100644 index 0000000000000..4c7414c94d9c1 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/calendar-invalid-era.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: RangeError thrown if era is invalid for this calendar +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +const calendarsWithoutEras = [ + "chinese", + "dangi", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.PlainDate.from({ year: 2025, month: 1, day: 1, era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +calendarsWithoutEras.forEach((calendar) => { + // era is ignored + const result = Temporal.PlainDate.from({ year: 2025, month: 1, day: 1, era: "xyz", eraYear: 2025, calendar }); + assert.sameValue(result instanceof Temporal.PlainDate, true, `era should be ignored for calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js index ce1d614259e35..dffc858aa6d2d 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js @@ -116,6 +116,24 @@ TemporalHelpers.assertPlainDate( 1868, 10, "M10", 22, "Meiji 1 resolves to CE 1868 before era start date", "ce", 1868); +const meiji1AfterStart = Temporal.PlainDate.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, calendar }, options); +TemporalHelpers.assertPlainDate( + meiji1AfterStart, + 1868, 10, "M10", 23, "Meiji 1 still resolves to CE 1868 after era start date", + "ce", 1868); + +const meiji5 = Temporal.PlainDate.from({ era: "meiji", eraYear: 5, monthCode: "M12", day: 31, calendar }, options); +TemporalHelpers.assertPlainDate( + meiji5, + 1872, 12, "M12", 31, "Meiji 5 resolves to CE 1872", + "ce", 1872); + +const ce1873 = Temporal.PlainDate.from({ era: "ce", eraYear: 1873, monthCode: "M01", day: 1, calendar }, options); +TemporalHelpers.assertPlainDate( + ce1873, + 1873, 1, "M01", 1, "CE 1873 resolves to Meiji 6", + "meiji", 6); + const meiji0 = Temporal.PlainDate.from({ era: "meiji", eraYear: 0, monthCode: "M10", day: 23, calendar }, options); TemporalHelpers.assertPlainDate( meiji0, @@ -128,12 +146,6 @@ TemporalHelpers.assertPlainDate( 1866, 10, "M10", 23, "Meiji -1 resolves to CE 1866", "ce", 1866); -const ce1868AfterStart = Temporal.PlainDate.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 23, calendar }, options); -TemporalHelpers.assertPlainDate( - ce1868AfterStart, - 1868, 10, "M10", 23, "CE 1868 resolves to Meiji 1 after era start date", - "meiji", 1); - const ce0 = Temporal.PlainDate.from({ era: "ce", eraYear: 0, monthCode: "M01", day: 1, calendar }, options); TemporalHelpers.assertPlainDate( ce0, diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-japanese.js index dce348f9bd908..679a6f8e3a332 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/era-japanese.js @@ -16,7 +16,7 @@ const reiwa = Temporal.PlainDate.from({ era: "reiwa", eraYear: 2, month: 1, day: const heisei = Temporal.PlainDate.from({ era: "heisei", eraYear: 2, month: 1, day: 1, calendar }); const showa = Temporal.PlainDate.from({ era: "showa", eraYear: 2, month: 1, day: 1, calendar }); const taisho = Temporal.PlainDate.from({ era: "taisho", eraYear: 2, month: 1, day: 1, calendar }); -const meiji = Temporal.PlainDate.from({ era: "meiji", eraYear: 2, month: 1, day: 1, calendar }); +const meiji = Temporal.PlainDate.from({ era: "meiji", eraYear: 6, month: 1, day: 1, calendar }); const ce = Temporal.PlainDate.from({ era: "ce", eraYear: 1000, month: 1, day: 1, calendar }); const bce = Temporal.PlainDate.from({ era: "bce", eraYear: 1, month: 1, day: 1, calendar }); @@ -28,7 +28,7 @@ TemporalHelpers.assertPlainDate(showa, 1927, 1, "M01", 1, `${showa}`, "showa", TemporalHelpers.assertPlainDate(taisho, 1913, 1, "M01", 1, `${taisho}`, "taisho", 2); -TemporalHelpers.assertPlainDate(meiji, 1869, 1, "M01", 1, `${meiji}`, "meiji", 2); +TemporalHelpers.assertPlainDate(meiji, 1873, 1, "M01", 1, `${meiji}`, "meiji", 6); TemporalHelpers.assertPlainDate(ce, 1000, 1, "M01", 1, `${ce} (CE)`, "ce", 1000); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/invalid-month-codes-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/invalid-month-codes-hebrew.js index 057875a2f2251..3cb8670658991 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/from/invalid-month-codes-hebrew.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/invalid-month-codes-hebrew.js @@ -10,10 +10,16 @@ features: [Temporal, Intl.Era-monthcode] const calendar = "hebrew"; +// 5779 is a leap year assert.throws(RangeError, () => { Temporal.PlainDate.from({ year: 5779, monthCode: "M13", day: 1, calendar }); }, "M13 should not be a valid month code"); +// 5781 is a common year +assert.throws(RangeError, () => { + Temporal.PlainDate.from({ year: 5781, monthCode: "M13", day: 1, calendar }); +}, "M13 should not be a valid month code"); + // Invalid leap months: e.g. M02L for (var i = 1; i <= 12; i++) { if (i === 5) diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/from/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/from/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/from/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/from/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/add/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/add/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/add/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/add/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/equals/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/equals/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/equals/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/equals/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/monthCode/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/monthCode/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/monthCode/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/monthCode/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/era-boundary-japanese.js index 87c13aeef56b7..2298bae9027e8 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainDate.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, calendar }, options); const ce1 = Temporal.PlainDate.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, calendar }, options); -const ce1868 = Temporal.PlainDate.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, calendar }, options); -const meiji1 = Temporal.PlainDate.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, calendar}, options); -const meiji5 = Temporal.PlainDate.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, calendar }, options); +const ce1872 = Temporal.PlainDate.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, calendar }, options); +const meiji6 = Temporal.PlainDate.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, calendar}, options); +const meiji7 = Temporal.PlainDate.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, calendar }, options); const meiji45 = Temporal.PlainDate.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, calendar }, options); const taisho1 = Temporal.PlainDate.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, calendar }, options); const taisho6 = Temporal.PlainDate.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js index 8dae7c7f48af8..2aa70733753a7 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 29, calendar }, options).since(end, { largestUnit }), 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 30, calendar }, options).since(end, { largestUnit }), 0, -4, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2019, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2025, monthCode: "M06L", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 2018, monthCode: "M06", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js index 9f09a0164d016..a29e1ff8a7a6f 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).since(end, { largestUnit }), 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).since(end, { largestUnit }), 0, -3, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2019, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 1987, monthCode: "M06L", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 1979, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 1979, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 1956, monthCode: "M03", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 1955, monthCode: "M03L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 1955, monthCode: "M03L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..f24670e80572d --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.since +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.PlainDate.from({ year: 5783, monthCode: "M08", day: 29, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M07", day: 29, calendar }, options).since(end, { largestUnit }), + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M07", day: 30, calendar }, options).since(end, { largestUnit }), + 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.PlainDate.from({ year: 5783, monthCode: "M12", day: 29, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M09", day: 29, calendar }, options).since(end, { largestUnit }), + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M09", day: 30, calendar }, options).since(end, { largestUnit }), + 0, -2, 0, -29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.PlainDate.from({ year: 5786, monthCode: "M04", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M11", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M11", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -2, -5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -29, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -2, -4, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.PlainDate.from({ year: 5784, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.PlainDate.from({ year: 5784, monthCode: "M03", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M03", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M03", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 5785, monthCode: "M06", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5784, monthCode: "M05L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5784, monthCode: "M05L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/subtract/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/subtract/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/subtract/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/subtract/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..44183d6bd5fb0 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.tolocalestring +description: Using timeStyle, even if dateStyle is present, should throw +features: [Temporal] +---*/ + +const item = new Temporal.PlainDate(2026, 1, 20); + +assert.throws(TypeError, function() { + item.toLocaleString("en", { dateStyle: "full", timeStyle: "full" }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toString/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toString/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toString/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toString/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toZonedDateTime/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toZonedDateTime/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/toZonedDateTime/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/era-boundary-japanese.js index 962ee2767c44d..25d810279c40d 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainDate.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, calendar }, options); const ce1 = Temporal.PlainDate.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, calendar }, options); -const ce1868 = Temporal.PlainDate.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, calendar }, options); -const meiji1 = Temporal.PlainDate.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, calendar}, options); -const meiji5 = Temporal.PlainDate.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, calendar }, options); +const ce1872 = Temporal.PlainDate.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, calendar }, options); +const meiji6 = Temporal.PlainDate.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, calendar}, options); +const meiji7 = Temporal.PlainDate.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, calendar }, options); const meiji45 = Temporal.PlainDate.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, calendar }, options); const taisho1 = Temporal.PlainDate.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, calendar }, options); const taisho6 = Temporal.PlainDate.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js index 8df9029be3bd2..d9209164ca8b7 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 29, calendar }, options).until(end, { largestUnit }), 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M04", day: 30, calendar }, options).until(end, { largestUnit }), 0, 4, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2019, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2025, monthCode: "M06L", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 2018, monthCode: "M06", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2017, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js index 099a55548646c..3bbc011e39444 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M06", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2020, monthCode: "M04L", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 29, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2020, monthCode: "M04", day: 30, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 29, calendar }, options).until(end, { largestUnit }), 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit }), + Temporal.PlainDate.from({ year: 2023, monthCode: "M05", day: 30, calendar }, options).until(end, { largestUnit }), 0, 3, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }); + const end = Temporal.PlainDate.from({ year: 2023, monthCode: "M09", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 29, calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDate.from({ year: 2021, monthCode: "M05", day: 30, calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 2019, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 2018, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDate.from({ year: 1987, monthCode: "M06L", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 1979, monthCode: "M06L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 1979, monthCode: "M06L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 1956, monthCode: "M03", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 1955, monthCode: "M03L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 1955, monthCode: "M03L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..e08bf2ded5c7e --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.until +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.PlainDate.from({ year: 5783, monthCode: "M08", day: 29, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M07", day: 29, calendar }, options).until(end, { largestUnit }), + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M07", day: 30, calendar }, options).until(end, { largestUnit }), + 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.PlainDate.from({ year: 5783, monthCode: "M12", day: 29, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M09", day: 29, calendar }, options).until(end, { largestUnit }), + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDate.from({ year: 5783, monthCode: "M09", day: 30, calendar }, options).until(end, { largestUnit }), + 0, 2, 0, 29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.PlainDate.from({ year: 5786, monthCode: "M04", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M11", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M11", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 29, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 2, 4, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.PlainDate.from({ year: 5784, monthCode: "M02", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M02", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M02", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.PlainDate.from({ year: 5784, monthCode: "M03", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5783, monthCode: "M03", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5783, monthCode: "M03", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDate.from({ year: 5785, monthCode: "M06", day: 29, calendar }, options); + const start1 = Temporal.PlainDate.from({ year: 5784, monthCode: "M05L", day: 29, calendar }, options); + const start2 = Temporal.PlainDate.from({ year: 5784, monthCode: "M05L", day: 30, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/with/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/with/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/with/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/with/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..81545f06dbe21 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.withcalendar +description: A time string is valid input for Calendar +features: [Temporal] +---*/ + +const instance = Temporal.PlainDate.from({ year: 1976, month: 11, day: 18}); + +const calendars = [ + "buddhist", + "chinese", + "coptic", + "dangi", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +] + +calendars.forEach((cal) => { + const str = `T11:30[u-ca=${cal}]`; + const result = instance.withCalendar(str); + assert.sameValue(result.calendarId, cal, `Calendar created from string "${str}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/roundtrip-from-iso8601.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/roundtrip-from-iso8601.js new file mode 100644 index 0000000000000..710217118c6f1 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/roundtrip-from-iso8601.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.prototype.withcalendar +description: Converting from iso8601 and back works as expected. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let d1 = new Temporal.PlainDate(1911, 10, 10); +let d2 = d1.withCalendar('roc'); +assert.sameValue('roc', d2.calendarId); +TemporalHelpers.assertPlainDate(d2, 0, 10, 'M10', 10, '', 'broc', 1); +let d3 = d2.withCalendar('iso8601'); +assert.sameValue('iso8601', d3.calendarId); +TemporalHelpers.assertPlainDate(d3, 1911, 10, 'M10', 10); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withCalendar/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/basic-roc.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/basic-roc.js new file mode 100644 index 0000000000000..0670835cab68a --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/basic-roc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.withplaintime +description: withPlainTime should work for roc calendar dates +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +let timeRecord = { + hour: 9, + minute: 8, + second: 7, + millisecond: 6, + microsecond: 5, + nanosecond: 4 +}; + +let d = new Temporal.PlainDateTime(2020, 3, 15, 4, 5, 6, 7, 8, 9, 'roc'); +TemporalHelpers.assertPlainDateTime(d.withPlainTime(timeRecord), 109, 3, 'M03', 15, 9, 8, 7, 6, 5, 4, '', 'roc', 109); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/browser.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/browser.js similarity index 100% rename from js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/browser.js rename to js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/browser.js diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/shell.js new file mode 100644 index 0000000000000..8d4cef92ca9ea --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/withPlainTime/shell.js @@ -0,0 +1,1278 @@ +// GENERATED, DO NOT EDIT +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith("Symbol.")) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]`; + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`; + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * List of known calendar eras and their possible aliases. + * + * https://tc39.es/proposal-intl-era-monthcode/#table-eras + */ + CalendarEras: { + buddhist: [ + { era: "be" }, + ], + coptic: [ + { era: "am" }, + ], + ethiopic: [ + { era: "aa", aliases: ["mundi"] }, + { era: "am", aliases: ["incar"] }, + ], + ethioaa: [ + { era: "aa", aliases: ["mundi"] }, + ], + gregory: [ + { era: "bce", aliases: ["bc"] }, + { era: "ce", aliases: ["ad"] }, + ], + hebrew: [ + { era: "am" }, + ], + indian: [ + { era: "shaka" }, + ], + islamic: [ + { era: "ah" }, + { era: "bh" }, + ], + "islamic-civil": [ + { era: "bh" }, + { era: "ah" }, + ], + "islamic-rgsa": [ + { era: "bh" }, + { era: "ah" }, + ], + "islamic-tbla": [ + { era: "bh" }, + { era: "ah" }, + ], + "islamic-umalqura": [ + { era: "bh" }, + { era: "ah" }, + ], + japanese: [ + { era: "bce", aliases: ["bc"] }, + { era: "ce", aliases: ["ad"] }, + { era: "heisei" }, + { era: "meiji" }, + { era: "reiwa" }, + { era: "showa" }, + { era: "taisho" }, + ], + persian: [ + { era: "ap" }, + ], + roc: [ + { era: "roc", aliases: ["minguo"] }, + { era: "broc", aliases: ["before-roc", "minguo-qian"] }, + ], + }, + + /* + * Return the canonical era code. + */ + canonicalizeCalendarEra(calendarId, eraName) { + assert.sameValue(typeof calendarId, "string", "calendar must be string in canonicalizeCalendarEra"); + + if (!Object.prototype.hasOwnProperty.call(TemporalHelpers.CalendarEras, calendarId)) { + assert.sameValue(eraName, undefined); + return undefined; + } + + assert.sameValue(typeof eraName, "string", "eraName must be string or undefined in canonicalizeCalendarEra"); + + for (let {era, aliases = []} of TemporalHelpers.CalendarEras[calendarId]) { + if (era === eraName || aliases.includes(eraName)) { + return era; + } + } + throw new Test262Error(`Unsupported era name: ${eraName}`); + }, + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue( + TemporalHelpers.canonicalizeCalendarEra(date.calendarId, date.era), + TemporalHelpers.canonicalizeCalendarEra(date.calendarId, era), + `${prefix}era result:` + ); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue( + TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, datetime.era), + TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, era), + `${prefix}era result:` + ); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + const isoYear = Number(monthDay.toString({ calendarName: "always" }).split("-")[0]); + assert.sameValue(isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + * + * Pass null as the referenceISODay if you don't want to give it explicitly. + * In that case, the expected referenceISODay will be computed using PlainDate + * and only verified for consistency, not for equality with a specific value. + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(typeof referenceISODay === "number" || referenceISODay === null, + `TemporalHelpers.assertPlainYearMonth() referenceISODay argument should be a number or null, not ${referenceISODay}`); + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue( + TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, yearMonth.era), + TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, era), + `${prefix}era result:` + ); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + const isoDay = Number(yearMonth.toString({ calendarName: "always" }).slice(1).split("-")[2].slice(0, 2)); + const expectedISODay = referenceISODay ?? yearMonth.toPlainDate({ day: 1 }).withCalendar("iso8601").day; + assert.sameValue(isoDay, expectedISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZoneId, expected.timeZoneId, `${prefix}time zone same value:`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance. + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = "iso8601"; + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: "years", + month: "months", + week: "weeks", + day: "days", + hour: "hours", + minute: "minutes", + second: "seconds", + millisecond: "milliseconds", + microsecond: "microseconds", + nanosecond: "nanoseconds", + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + */ + checkToTemporalCalendarFastPath(func) { + const plainDate = new Temporal.PlainDate(2000, 5, 2, "iso8601"); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601"); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, "iso8601"); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, "iso8601"); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + Object.defineProperty(temporalObject, "calendar", { + get() { + throw new Test262Error("should not get 'calendar' property"); + }, + }); + Object.defineProperty(temporalObject, "calendarId", { + get() { + throw new Test262Error("should not get 'calendarId' property"); + }, + }); + + func(temporalObject); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, "toString", { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const date = new Temporal.PlainDate(2000, 5, 2, "iso8601"); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return "iso8601"; + }, + }); + + func(date); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set() { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + * If skipToPrimitive is given, it must be an array of property keys. Those + * properties will not have a TemporalHelpers.toPrimitiveObserver returned, + * and instead just be returned directly. + */ + propertyBagObserver(calls, propertyBag, objectName, skipToPrimitive) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + if (skipToPrimitive && skipToPrimitive.indexOf(key) >= 0) { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + "11-18[U-CA=iso8601]", + "11-18[u-CA=iso8601]", + "11-18[FOO=bar]", + "-999999-01-01[u-ca=gregory]", + "-999999-01-01[u-ca=chinese]", + "+999999-01-01[u-ca=gregory]", + "+999999-01-01[u-ca=chinese]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + "-999999-10-01", + "-999999-10-01[u-ca=iso8601]", + "+999999-10-01", + "+999999-10-01[u-ca=iso8601]", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + "[u-ca=iso8601]"); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + "1976-11[u-ca=gregory]", + "1976-11[u-ca=hebrew]", + "1976-11[U-CA=iso8601]", + "1976-11[u-CA=iso8601]", + "1976-11[FOO=bar]", + "+999999-01", + "-999999-01", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1-02:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "-009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/year/arithmetic-year.js b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/year/arithmetic-year.js index c3d136cf40729..684a7f4a9b27c 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/year/arithmetic-year.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDate/prototype/year/arithmetic-year.js @@ -103,7 +103,7 @@ const tests = { [{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1], [{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0], [{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1], - [{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868], + [{ era: "meiji", eraYear: 6, monthCode: "M12", day: 31 }, 1873], [{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912], [{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926], [{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989], diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/canonicalize-calendar.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/canonicalize-calendar.js index f7d68eb49bae7..8ebcea74679b9 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/canonicalize-calendar.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/canonicalize-calendar.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// Copyright (C) 2025 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,7 +8,12 @@ description: Calendar ID is canonicalized features: [Temporal] ---*/ -const result = new Temporal.PlainDateTime(2024, 7, 2, 12, 34, 56, 987, 654, 321, "islamicc"); +var result = new Temporal.PlainDateTime(2024, 7, 2, 12, 34, 56, 987, 654, 321, "islamicc"); assert.sameValue(result.calendarId, "islamic-civil", "calendar ID is canonicalized"); +// May need to be removed in the future. +// See https://github.com/tc39/ecma402/issues/285 +result = new Temporal.PlainDateTime(2024, 7, 2, 12, 34, 56, 987, 654, 321, "ethiopic-amete-alem"); +assert.sameValue(result.calendarId, "ethioaa", "calendar ID is canonicalized"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/compare/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/compare/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/compare/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/compare/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era-with-era-year.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era-with-era-year.js new file mode 100644 index 0000000000000..e783c093ea262 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era-with-era-year.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: RangeError thrown if era is invalid for this calendar with year absent and eraYear present +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.PlainDateTime.from({ month: 1, day: 1, hour: 12, minute: 34, era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era.js new file mode 100644 index 0000000000000..9b951422bc73c --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/calendar-invalid-era.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: RangeError thrown if era is invalid for this calendar +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +const calendarsWithoutEras = [ + "chinese", + "dangi", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.PlainDateTime.from({ year: 2025, month: 1, day: 1, hour: 12, minute: 34, era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +calendarsWithoutEras.forEach((calendar) => { + // era is ignored + const result = Temporal.PlainDateTime.from({ year: 2025, month: 1, day: 1, hour: 12, minute: 34, era: "xyz", eraYear: 2025, calendar }); + assert.sameValue(result instanceof Temporal.PlainDateTime, true, `era should be ignored for calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js index 2290b083f2f9c..6fb344cf10edb 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js @@ -116,6 +116,24 @@ TemporalHelpers.assertPlainDateTime( 1868, 10, "M10", 22, 12, 34, 0, 0, 0, 0, "Meiji 1 resolves to CE 1868 before era start date", "ce", 1868); +const meiji1AfterStart = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, calendar }, options); +TemporalHelpers.assertPlainDateTime( + meiji1AfterStart, + 1868, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "Meiji 1 still resolves to CE 1868 after era start date", + "ce", 1868); + +const meiji5 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M12", day: 31, hour: 12, minute: 34, calendar }, options); +TemporalHelpers.assertPlainDateTime( + meiji5, + 1872, 12, "M12", 31, 12, 34, 0, 0, 0, 0, "Meiji 5 resolves to CE 1872", + "ce", 1872); + +const ce1873 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1873, monthCode: "M01", day: 1, hour: 12, minute: 34, calendar }, options); +TemporalHelpers.assertPlainDateTime( + ce1873, + 1873, 1, "M01", 1, 12, 34, 0, 0, 0, 0, "CE 1873 resolves to Meiji 6", + "meiji", 6); + const meiji0 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 0, monthCode: "M10", day: 23, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertPlainDateTime( meiji0, @@ -128,12 +146,6 @@ TemporalHelpers.assertPlainDateTime( 1866, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "Meiji -1 resolves to CE 1866", "ce", 1866); -const ce1868AfterStart = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 23, hour: 12, minute: 34, calendar }, options); -TemporalHelpers.assertPlainDateTime( - ce1868AfterStart, - 1868, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "CE 1868 resolves to Meiji 1 after era start date", - "meiji", 1); - const ce0 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 0, monthCode: "M01", day: 1, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertPlainDateTime( ce0, diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-japanese.js index b67ac1e333aa1..444ce8f466e13 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/era-japanese.js @@ -16,7 +16,7 @@ const reiwa = Temporal.PlainDateTime.from({ era: "reiwa", eraYear: 2, month: 1, const heisei = Temporal.PlainDateTime.from({ era: "heisei", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, calendar }); const showa = Temporal.PlainDateTime.from({ era: "showa", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, calendar }); const taisho = Temporal.PlainDateTime.from({ era: "taisho", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, calendar }); -const meiji = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, calendar }); +const meiji = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 6, month: 1, day: 1, hour: 12, minute: 34, calendar }); const ce = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1000, month: 1, day: 1, hour: 12, minute: 34, calendar }); const bce = Temporal.PlainDateTime.from({ era: "bce", eraYear: 1, month: 1, day: 1, hour: 12, minute: 34, calendar }); @@ -28,7 +28,7 @@ TemporalHelpers.assertPlainDateTime(showa, 1927, 1, "M01", 1, 12, 34, 0, 0, 0, 0 TemporalHelpers.assertPlainDateTime(taisho, 1913, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${taisho}`, "taisho", 2); -TemporalHelpers.assertPlainDateTime(meiji, 1869, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${meiji}`, "meiji", 2); +TemporalHelpers.assertPlainDateTime(meiji, 1873, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${meiji}`, "meiji", 6); TemporalHelpers.assertPlainDateTime(ce, 1000, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${ce} (CE)`, "ce", 1000); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/invalid-month-codes-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/invalid-month-codes-hebrew.js index 635ff59ae50d8..bc95bdbf71959 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/invalid-month-codes-hebrew.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/invalid-month-codes-hebrew.js @@ -10,10 +10,16 @@ features: [Temporal, Intl.Era-monthcode] const calendar = "hebrew"; +// 5779 is a leap year assert.throws(RangeError, () => { Temporal.PlainDateTime.from({ year: 5779, monthCode: "M13", day: 1, hour: 12, minute: 34, calendar }); }, "M13 should not be a valid month code"); +// 5781 is a common year +assert.throws(RangeError, () => { + Temporal.PlainDateTime.from({ year: 5781, monthCode: "M13", day: 1, hour: 12, minute: 34, calendar }); +}, "M13 should not be a valid month code"); + // Invalid leap months: e.g. M02L for (var i = 1; i <= 12; i++) { if (i === 5) diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/from/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/add/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/add/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/add/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/add/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/equals/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/equals/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/equals/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/equals/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/monthCode/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/monthCode/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/monthCode/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/monthCode/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/era-boundary-japanese.js index 69f1430cb7269..52d25a72ecca0 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainDateTime.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, calendar }, options); const ce1 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, calendar }, options); -const ce1868 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, hour: 12, minute: 34, calendar }, options); -const meiji1 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, calendar}, options); -const meiji5 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, hour: 12, minute: 34, calendar }, options); +const ce1872 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, hour: 12, minute: 34, calendar }, options); +const meiji6 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, hour: 12, minute: 34, calendar}, options); +const meiji7 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, hour: 12, minute: 34, calendar }, options); const meiji45 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, hour: 12, minute: 34, calendar }, options); const taisho1 = Temporal.PlainDateTime.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, hour: 12, minute: 34, calendar }, options); const taisho6 = Temporal.PlainDateTime.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, hour: 12, minute: 34, calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js index c25d4deb5b497..8cf8e37da1a1c 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -4, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2025, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js index 5552a020a8b3d..4ce8bdfcb8488 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), 0, -3, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 1987, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 1979, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 1979, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 1956, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 1955, monthCode: "M03L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 1955, monthCode: "M03L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..c1794ac9d58ff --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M08", day: 29, hour: 12, minute: 34, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M07", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M07", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), + 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M12", day: 29, hour: 12, minute: 34, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M09", day: 30, hour: 12, minute: 34, calendar }, options).since(end, { largestUnit }), + 0, -2, 0, -29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.PlainDateTime.from({ year: 5786, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M11", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M11", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -2, -5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -29, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -2, -4, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M03", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 5785, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M05L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M05L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/subtract/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/subtract/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/subtract/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/subtract/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..245f2b72c1526 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tolocalestring +description: Using both dateStyle and timeStyle should not throw +features: [Temporal] +---*/ + +const item = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0); +var result = item.toLocaleString("en", { dateStyle: "full", timeStyle: "full" }); +assert.sameValue(result.includes(":00"), true, "using both dateStyle and timeStyle should not throw"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/hourcycle.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/hourcycle.js new file mode 100644 index 0000000000000..be557d2387fc6 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toLocaleString/hourcycle.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tolocalestring +description: Hour cycle should be correctly set when defaults are used +features: [Temporal] +---*/ + +const output0 = "00:00:00"; +const output11 = "0:00:00"; +const output12 = "12:00:00"; +const output24 = "24:00:00"; + +const item = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0); +var result = item.toLocaleString("en", { hour12: false, timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hour12: false should include ${output0}`); + +result = item.toLocaleString("en", { hour12: true, timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hour12: true should include ${output12}`); + +result = item.toLocaleString("en", { hourCycle: "h23", timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hourCycle: h23 should include ${output0}`); + +result = item.toLocaleString("en", { hourCycle: "h24", timeZone: "UTC" }); +assert.sameValue(result.includes(output24), true, `output for hourCycle: h24 should include ${output24}`); + +result = item.toLocaleString("en", { hourCycle: "h11", timeZone: "UTC" }); +assert.sameValue(result.includes(output11), true, `output for hourCycle: h11 should include ${output11}`); + +result = item.toLocaleString("en", { hourCycle: "h12", timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hourCycle: h12 should include ${output12}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toString/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toString/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toString/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toString/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/toZonedDateTime/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/adjusted-dates.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/adjusted-dates.js new file mode 100644 index 0000000000000..41a9550c8013e --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/adjusted-dates.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: A case where date-times are internally adjusted to have the same date component +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const largestUnit of ['years', 'months', 'weeks', 'days', 'hours']) { + const d1 = new Temporal.PlainDateTime(2026, 1, 6, 11, 2, 0, 0, 0, 0, "gregory"); + const d2 = new Temporal.PlainDateTime(2026, 1, 7, 9, 2, 0, 0, 0, 0, "gregory"); + TemporalHelpers.assertDuration(d1.until(d2, { largestUnit }), + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, `differencing ${d1} and ${d2}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/era-boundary-japanese.js index 399d4577012ae..a5bc839fe69b9 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainDateTime.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, calendar }, options); const ce1 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, calendar }, options); -const ce1868 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, hour: 12, minute: 34, calendar }, options); -const meiji1 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, calendar}, options); -const meiji5 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, hour: 12, minute: 34, calendar }, options); +const ce1872 = Temporal.PlainDateTime.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, hour: 12, minute: 34, calendar }, options); +const meiji6 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, hour: 12, minute: 34, calendar}, options); +const meiji7 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, hour: 12, minute: 34, calendar }, options); const meiji45 = Temporal.PlainDateTime.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, hour: 12, minute: 34, calendar }, options); const taisho1 = Temporal.PlainDateTime.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, hour: 12, minute: 34, calendar }, options); const taisho6 = Temporal.PlainDateTime.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, hour: 12, minute: 34, calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js index 1544c93e1a01a..22e2557f98ca8 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 4, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2025, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js index 540e8f54a0540..1a47eb4e4897e 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit }), + Temporal.PlainDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), 0, 3, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }); + const end = Temporal.PlainDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }, options); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.PlainDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 1987, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 1979, monthCode: "M06L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 1979, monthCode: "M06L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 1956, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 1955, monthCode: "M03L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 1955, monthCode: "M03L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..76b7cc03d3133 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.until +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M08", day: 29, hour: 12, minute: 34, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M07", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M07", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), + 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M12", day: 29, hour: 12, minute: 34, calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M09", day: 29, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.PlainDateTime.from({ year: 5783, monthCode: "M09", day: 30, hour: 12, minute: 34, calendar }, options).until(end, { largestUnit }), + 0, 2, 0, 29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.PlainDateTime.from({ year: 5786, monthCode: "M04", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M11", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M11", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 29, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 2, 4, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M02", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M02", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M03", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5783, monthCode: "M03", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.PlainDateTime.from({ year: 5785, monthCode: "M06", day: 29, hour: 12, minute: 34, calendar }, options); + const start1 = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M05L", day: 29, hour: 12, minute: 34, calendar }, options); + const start2 = Temporal.PlainDateTime.from({ year: 5784, monthCode: "M05L", day: 30, hour: 12, minute: 34, calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/with/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/with/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/with/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/with/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..940f33aa02982 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.withcalendar +description: A time string is valid input for Calendar +features: [Temporal] +---*/ + +const instance = Temporal.PlainDateTime.from({ year: 1976, month: 11, day: 18, hour: 12, minute: 34}); + +const calendars = [ + "buddhist", + "chinese", + "coptic", + "dangi", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +] + +calendars.forEach((cal) => { + const str = `T11:30[u-ca=${cal}]`; + const result = instance.withCalendar(str); + assert.sameValue(result.calendarId, cal, `Calendar created from string "${str}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/roundtrip-from-iso8601.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/roundtrip-from-iso8601.js new file mode 100644 index 0000000000000..f31f9b25dbee0 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/roundtrip-from-iso8601.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.withcalendar +description: Converting to roc and back to iso8601 works as expeced. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let d1 = new Temporal.PlainDateTime(1911, 10, 10, 4, 5, 6, 7, 8, 9); +let d2 = d1.withCalendar('roc'); +assert.sameValue('roc', d2.calendarId); +TemporalHelpers.assertPlainDateTime(d2, 0, 10, 'M10', 10, 4, 5, 6, 7, 8, 9, '', 'broc', 1); +let d3 = d2.withCalendar('iso8601'); +assert.sameValue('iso8601', d3.calendarId); +TemporalHelpers.assertPlainDateTime(d3, 1911, 10, 'M10', 10, 4, 5, 6, 7, 8, 9); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/shell.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/withCalendar/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/year/arithmetic-year.js b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/year/arithmetic-year.js index 692f13d6cc420..543f628beb8d0 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/year/arithmetic-year.js +++ b/js/src/tests/test262/intl402/Temporal/PlainDateTime/prototype/year/arithmetic-year.js @@ -103,7 +103,7 @@ const tests = { [{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1], [{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0], [{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1], - [{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868], + [{ era: "meiji", eraYear: 6, monthCode: "M12", day: 31 }, 1873], [{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912], [{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926], [{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989], diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/canonicalize-calendar.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/canonicalize-calendar.js index 745aa3ff8b4dd..9ec8bfce2ac94 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/canonicalize-calendar.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/canonicalize-calendar.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// Copyright (C) 2025 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,7 +8,12 @@ description: Calendar ID is canonicalized features: [Temporal] ---*/ -const result = new Temporal.PlainMonthDay(2, 11, "islamicc", 1972); +var result = new Temporal.PlainMonthDay(2, 11, "islamicc", 1972); assert.sameValue(result.calendarId, "islamic-civil", "calendar ID is canonicalized"); +// May need to be removed in the future. +// See https://github.com/tc39/ecma402/issues/285 +result = new Temporal.PlainMonthDay(2, 11, "ethiopic-amete-alem"); +assert.sameValue(result.calendarId, "ethioaa", "calendar ID is canonicalized"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/from/shell.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/from/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/from/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/from/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/equals/shell.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/equals/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/equals/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/equals/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/monthCode/shell.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/monthCode/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/monthCode/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/monthCode/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..9f8822b13bf07 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainmonthday.prototype.tolocalestring +description: Using timeStyle, even if dateStyle is present, should throw +features: [Temporal] +---*/ + +const item = new Temporal.PlainMonthDay(1, 20, "gregory", 1972); + +assert.throws(TypeError, function() { + item.toLocaleString("en-u-ca-gregory", { dateStyle: "full", timeStyle: "full" }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toString/shell.js b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toString/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toString/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainMonthDay/prototype/toString/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..5e3e74b44ae8f --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.tolocalestring +description: Using dateStyle, even if timeStyle is present, should throw +features: [Temporal] +---*/ + +const item = new Temporal.PlainTime(0, 0); + +assert.throws(TypeError, function() { + item.toLocaleString("en", { dateStyle: "full", timeStyle: "full" }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/hourcycle.js b/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/hourcycle.js new file mode 100644 index 0000000000000..da2a405a131cf --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainTime/prototype/toLocaleString/hourcycle.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.tolocalestring +description: Hour cycle should be correctly set when defaults are used +features: [Temporal] +---*/ + +const output0 = "00:00:00"; +const output11 = "0:00:00"; +const output12 = "12:00:00"; +const output24 = "24:00:00"; + +const item = new Temporal.PlainTime(0, 0); +var result = item.toLocaleString("en", { hour12: false, timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hour12: false should include ${output0}`); + +result = item.toLocaleString("en", { hour12: true, timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hour12: true should include ${output12}`); + +result = item.toLocaleString("en", { hourCycle: "h23", timeZone: "UTC" }); +assert.sameValue(result.includes(output0), true, `output for hourCycle: h23 should include ${output0}`); + +result = item.toLocaleString("en", { hourCycle: "h24", timeZone: "UTC" }); +assert.sameValue(result.includes(output24), true, `output for hourCycle: h24 should include ${output24}`); + +result = item.toLocaleString("en", { hourCycle: "h11", timeZone: "UTC" }); +assert.sameValue(result.includes(output11), true, `output for hourCycle: h11 should include ${output11}`); + +result = item.toLocaleString("en", { hourCycle: "h12", timeZone: "UTC" }); +assert.sameValue(result.includes(output12), true, `output for hourCycle: h12 should include ${output12}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/canonicalize-calendar.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/canonicalize-calendar.js index a66324321ab6f..d86151bbbf323 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/canonicalize-calendar.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/canonicalize-calendar.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// Copyright (C) 2025 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,7 +8,12 @@ description: Calendar ID is canonicalized features: [Temporal] ---*/ -const result = new Temporal.PlainYearMonth(2024, 6, "islamicc", 8); +var result = new Temporal.PlainYearMonth(2024, 6, "islamicc", 8); assert.sameValue(result.calendarId, "islamic-civil", "calendar ID is canonicalized"); +// May need to be removed in the future. +// See https://github.com/tc39/ecma402/issues/285 +result = new Temporal.PlainYearMonth(2024, 6, "ethiopic-amete-alem"); +assert.sameValue(result.calendarId, "ethioaa", "calendar ID is canonicalized"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/compare/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/compare/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/compare/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/compare/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js index cab5a3305cee5..3c24fed282619 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js @@ -117,6 +117,24 @@ TemporalHelpers.assertPlainYearMonth( 1868, 10, "M10", "Meiji 1 resolves to CE 1868 before era start date", "ce", 1868); +const meiji1AfterStart = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 1, monthCode: "M11", calendar }, options); +TemporalHelpers.assertPlainYearMonth( + meiji1AfterStart, + 1868, 11, "M11", "Meiji 1 still resolves to CE 1868 after era start date", + "ce", 1868); + +const meiji5 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 5, monthCode: "M12", calendar }, options); +TemporalHelpers.assertPlainYearMonth( + meiji5, + 1872, 12, "M12", "Meiji 5 resolves to CE 1872", + "ce", 1872); + +const ce1873 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1873, monthCode: "M01", calendar }, options); +TemporalHelpers.assertPlainYearMonth( + ce1873, + 1873, 1, "M01", "CE 1873 resolves to Meiji 6", + "meiji", 6); + const meiji0 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 0, monthCode: "M10", calendar }, options); TemporalHelpers.assertPlainYearMonth( meiji0, @@ -129,12 +147,6 @@ TemporalHelpers.assertPlainYearMonth( 1866, 10, "M10", "Meiji -1 resolves to CE 1866", "ce", 1866); -const ce1868AfterStart = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1868, monthCode: "M11", calendar }, options); -TemporalHelpers.assertPlainYearMonth( - ce1868AfterStart, - 1868, 11, "M11", "CE 1868 resolves to Meiji 1 after era start date", - "meiji", 1); - const ce0 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 0, monthCode: "M01", calendar }, options); TemporalHelpers.assertPlainYearMonth( ce0, diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-japanese.js index ab464ecfea4b8..6d6b9d42d343a 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/era-japanese.js @@ -16,7 +16,7 @@ const reiwa = Temporal.PlainYearMonth.from({ era: "reiwa", eraYear: 2, month: 1, const heisei = Temporal.PlainYearMonth.from({ era: "heisei", eraYear: 2, month: 1, calendar }); const showa = Temporal.PlainYearMonth.from({ era: "showa", eraYear: 2, month: 1, calendar }); const taisho = Temporal.PlainYearMonth.from({ era: "taisho", eraYear: 2, month: 1, calendar }); -const meiji = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 2, month: 1, calendar }); +const meiji = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 6, month: 1, calendar }); const ce = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1000, month: 1, calendar }); const bce = Temporal.PlainYearMonth.from({ era: "bce", eraYear: 1, month: 1, calendar }); @@ -28,7 +28,7 @@ TemporalHelpers.assertPlainYearMonth(showa, 1927, 1, "M01", `${showa}`, "showa" TemporalHelpers.assertPlainYearMonth(taisho, 1913, 1, "M01", `${taisho}`, "taisho", 2); -TemporalHelpers.assertPlainYearMonth(meiji, 1869, 1, "M01", `${meiji}`, "meiji", 2); +TemporalHelpers.assertPlainYearMonth(meiji, 1873, 1, "M01", `${meiji}`, "meiji", 6); TemporalHelpers.assertPlainYearMonth(ce, 1000, 1, "M01", `${ce} (CE)`, "ce", 1000); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-dates.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-dates.js index 686c175b3520b..da98240448123 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-dates.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-dates.js @@ -19,15 +19,15 @@ includes: [temporalHelpers.js] const testData = [ ["buddhist", -271278, 5, "M05", "be", -271278, 1, 276303, 9, "M09", "be", 276303, 1], - ["coptic", -272099, 4, "M04", "am", -272099, 27, 275471, 5, "M05", "am", 275471, 23], - ["ethioaa", -266323, 4, "M04", "aa", -266323, 27, 281247, 5, "M05", "aa", 281247, 23], - ["ethiopic", -271823, 4, "M04", "aa", -266323, 27, 275747, 5, "M05", "am", 275747, 23], + ["coptic", -272099, 4, "M04", "am", -272099, 27, 275471, 6, "M06", "am", 275471, 22], + ["ethioaa", -266323, 4, "M04", "aa", -266323, 27, 281247, 6, "M06", "aa", 281247, 22], + ["ethiopic", -271823, 4, "M04", "aa", -266323, 27, 275747, 6, "M06", "am", 275747, 22], ["gregory", -271821, 5, "M05", "bce", 271822, 1, 275760, 9, "M09", "ce", 275760, 1], ["hebrew", -268058, 12, "M12", "am", -268058, 16, 279517, 10, "M09", "am", 279517, 3], - ["indian", -271899, 2, "M02", "shaka", -271899, 21, 275682, 6, "M06", "shaka", 275682, 23], - ["islamic-civil", -280804, 4, "M04", "bh", 280805, 29, 283583, 5, "M05", "ah", 283583, 22], - ["islamic-tbla", -280804, 4, "M04", "bh", 280805, 28, 283583, 5, "M05", "ah", 283583, 21], - ["islamic-umalqura", -280804, 4, "M04", "bh", 280805, 29, 283583, 5, "M05", "ah", 283583, 22], + ["indian", -271899, 2, "M02", "shaka", -271899, 21, 275682, 7, "M07", "shaka", 275682, 23], + ["islamic-civil", -280804, 4, "M04", "bh", 280805, 29, 283583, 6, "M06", "ah", 283583, 21], + ["islamic-tbla", -280804, 4, "M04", "bh", 280805, 28, 283583, 6, "M06", "ah", 283583, 20], + ["islamic-umalqura", -280804, 4, "M04", "bh", 280805, 29, 283583, 6, "M06", "ah", 283583, 21], ["japanese", -271821, 5, "M05", "bce", 271822, 1, 275760, 9, "M09", "reiwa", 273742, 1], ["persian", -272442, 2, "M02", "ap", -272442, 12, 275139, 7, "M07", "ap", 275139, 2], ["roc", -273732, 5, "M05", "broc", 273733, 1, 273849, 9, "M09", "roc", 273849, 1], @@ -46,7 +46,6 @@ for (const [calendar, minYear, minMonth, minMonthCode, minEra, minEraYear, minIS minYear, minMonth, minMonthCode, `${calendar} minimum supported date`, minEra, minEraYear, minISODay); - const max = Temporal.PlainYearMonth.from({ calendar, year: maxYear, diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-unsupported-dates.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-unsupported-dates.js new file mode 100644 index 0000000000000..4ce493581e5b1 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/extreme-unsupported-dates.js @@ -0,0 +1,55 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindate.from +description: from() throws for month before earlier supported month, and month after latest supported month +features: [Temporal, Intl.Era-monthcode] +---*/ + +// Lunisolar/lunar calendars can't accurately predict celestial orbits for dates +// far into the past/future. Skip `chinese` and `dangi`. `islamic-umalqura` is +// okay because it is specified to fall back to `islamic-civil` outside the +// range of accuracy. + +// Note that the earliest PlainYearMonth that can be constructed in a calendar +// is the earliest month whose first day occurs after ISO -271821-04-19 + +const testData = [ + ["buddhist", -271278, 3, "M03", "be", -271278, 276303, 10, "M10", "be", 276303], + ["coptic", -272099, 3, "M03", "am", -272099, 275471, 7, "M07", "am", 275471], + ["ethioaa", -266323, 3, "M03", "aa", -266323, 281247, 7, "M07", "aa", 281247], + ["ethiopic", -271823, 3, "M03", "aa", -266323, 275747, 7, "M07", "am", 275747], + ["gregory", -271821, 3, "M03", "bce", 271822, 275760, 10, "M10", "ce", 275760], + ["hebrew", -268058, 10, "M10", "am", -268058, 279517, 11, "M11", "am", 279517], + ["indian", -271899, 1, "M01", "shaka", -271899, 275682, 8, "M08", "shaka", 275682], + ["islamic-civil", -280804, 3, "M03", "bh", 280805, 283583, 7, "M07", "ah", 283583], + ["islamic-tbla", -280804, 3, "M03", "bh", 280805, 283583, 7, "M07", "ah", 283583], + ["islamic-umalqura", -280804, 3, "M03", "bh", 280805, 283583, 7, "M07", "ah", 283583], + ["japanese", -271821, 3, "M03", "bce", 271822, 275760, 10, "M10", "reiwa", 273742], + ["persian", -272443, 12, "M12", "ap", -272443, 275139, 8, "M08", "ap", 275139], + ["roc", -273732, 3, "M03", "broc", 273733, 273849, 10, "M10", "roc", 273849], +]; + +for (const [calendar, minYear, minMonth, minMonthCode, minEra, minEraYear, maxYear, maxMonth, maxMonthCode, maxEra, maxEraYear] of testData) { + assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ + calendar, + year: minYear, + era: minEra, + eraYear: minEraYear, + month: minMonth, + monthCode: minMonthCode, + }), `calendar ${calendar}: month before min month should throw`); + + assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ + calendar, + year: maxYear, + era: maxEra, + eraYear: maxEraYear, + month: maxMonth, + monthCode: maxMonthCode, + }), `calendar ${calendar}: month after max month should throw`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/invalid-month-codes-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/invalid-month-codes-hebrew.js index a5c5831b41adb..e29d2af6dbe30 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/invalid-month-codes-hebrew.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/invalid-month-codes-hebrew.js @@ -10,10 +10,16 @@ features: [Temporal, Intl.Era-monthcode] const calendar = "hebrew"; +// 5779 is a leap year assert.throws(RangeError, () => { Temporal.PlainYearMonth.from({ year: 5779, monthCode: "M13", calendar }); }, "M13 should not be a valid month code"); +// 5781 is a common year +assert.throws(RangeError, () => { + Temporal.PlainYearMonth.from({ year: 5781, monthCode: "M13", calendar }); +}, "M13 should not be a valid month code"); + // Invalid leap months: e.g. M02L for (var i = 1; i <= 12; i++) { if (i === 5) diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/reference-day-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/reference-day-japanese.js index 509f16eabf4b8..aae8bdd5c4f4b 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/reference-day-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/reference-day-japanese.js @@ -19,7 +19,7 @@ const result1 = Temporal.PlainYearMonth.from({calendar: "japanese", era: "heisei TemporalHelpers.assertPlainYearMonth( result1, 1989, 1, "M01", - "era is corrected based on reference day (Showa begins on January 8)", + "era is corrected based on reference day (Heisei begins on January 8)", "showa", 64 ); @@ -27,7 +27,7 @@ const result2 = Temporal.PlainYearMonth.from({calendar: "japanese", era: "showa" TemporalHelpers.assertPlainYearMonth( result2, 1926, 12, "M12", - "era is corrected based on reference day (Taishō begins on December 25)", + "era is corrected based on reference day (Showa begins on December 25)", "taisho", 15 ); @@ -35,7 +35,7 @@ const result3 = Temporal.PlainYearMonth.from({calendar: "japanese", era: "taisho TemporalHelpers.assertPlainYearMonth( result3, 1912, 7, "M07", - "era is corrected based on reference day (Meiji begins on July 30)", + "era is corrected based on reference day (Taishō begins on July 30)", "meiji", 45 ); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/from/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-chinese.js new file mode 100644 index 0000000000000..c1222c6c0ef2a --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-chinese.js @@ -0,0 +1,323 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Arithmetic around leap months in the chinese calendar +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "chinese"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); + +const leap193807L = Temporal.PlainYearMonth.from({ year: 1938, monthCode: "M07L", calendar }, options); +const leap195205L = Temporal.PlainYearMonth.from({ year: 1952, monthCode: "M05L", calendar }, options); +const leap196603L = Temporal.PlainYearMonth.from({ year: 1966, monthCode: "M03L", calendar }, options); +const common200008 = Temporal.PlainYearMonth.from({ year: 2000, monthCode: "M08", calendar }, options); +const common200108 = Temporal.PlainYearMonth.from({ year: 2001, monthCode: "M08", calendar }, options); +const common201901 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M01", calendar }, options); +const common201904 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M04", calendar }, options); +const leap202004 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04", calendar }, options); +const leap202004L = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04L", calendar }, options); +const common202104 = Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common201901.add(years1), + 2020, 1, "M01", "add 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.add(years1), + 1967, 3, "M03", "Adding 1 year to leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.add(years1, options); +}, "Adding 1 year to leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap193807L.add(years1), + 1939, 7, "M07", "Adding 1 year to leap month M07L on day 30 constrains to M07 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap193807L.add(years1, options); +}, "Adding 1 year to leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(years1, options), + 2020, 4, "M04", "Adding 1 year to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(years1, options), + 2021, 4, "M04", "Adding 1 year to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M04L", calendar }, options).add(new Temporal.Duration(8), options), + 2020, 5, "M04L", "Adding years to go from one M04L to the next M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(years1, options), + 2001, 9, "M08", "Adding 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(2), options), + 2021, 4, "M04", "Adding 2 years to common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201901.add(years1n), + 2018, 1, "M01", "Subtracting 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.add(years1n), + 1965, 3, "M03", "Subtracting 1 year from leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.add(years1n, options); +}, "Subtracting 1 year from leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap195205L.add(years1n), + 1951, 5, "M05", "Subtracting 1 year from leap month M05L on day 30 constrains to M05 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap195205L.add(years1n, options); +}, "Subtracting 1 year from leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(years1n, options), + 2020, 4, "M04", "Subtracting 1 year from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(years1n, options), + 2019, 4, "M04", "Subtracting 1 year from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(new Temporal.Duration(-8), options), + 2012, 5, "M04L", "Subtracting years to go from one M04L to the previous M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(years1n, options), + 2000, 8, "M08", "Subtracting 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(-2), options), + 2019, 4, "M04", "Subtracting 2 years from common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months12 = new Temporal.Duration(0, 12); +const months12n = new Temporal.Duration(0, -12); +const months13 = new Temporal.Duration(0, 13); +const months13n = new Temporal.Duration(0, -13); + +const leap202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); +const leap202006 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1947, monthCode: "M02L", calendar }, options).add(months1), + 1947, 4, "M03", "add 1 month, starting at start of leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1955, monthCode: "M03L", calendar }, options).add(months1), + 1955, 5, "M04", "add 1 month, starting at start of leap month with 30 days", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(months1), + 2020, 4, "M04", "adding 1 month to M03 in leap year lands in M04 (not M04L)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(new Temporal.Duration(0, 2)), + 2020, 5, "M04L", "adding 2 months to M03 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(new Temporal.Duration(0, 3)), + 2020, 6, "M05", "adding 3 months to M03 in leap year lands in M05 (not M06)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(months12), + 2020, 4, "M04", "Adding 12 months to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(months13), + 2020, 5, "M04L", "Adding 13 months to common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months12), + 2021, 3, "M03", "Adding 12 months to leap-year M04 lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months13), + 2021, 4, "M04", "Adding 13 months to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months12), + 2021, 4, "M04", "Adding 12 months to M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(new Temporal.Duration(1, 12), options), + 2002, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(new Temporal.Duration(2, 13), options), + 2004, 9, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(0, 24)), + 2021, 3, "M03", "Adding 24 months to common-year M04 crossing leap year with M04L, lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(0, 25)), + 2021, 4, "M04", "Adding 25 months to common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(months1n), + 2020, 6, "M05", "Subtracting 1 month from M06 in leap year lands in M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(new Temporal.Duration(0, -2)), + 2020, 5, "M04L", "Subtracting 2 months from M06 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(new Temporal.Duration(0, -3)), + 2020, 4, "M04", "Subtracting 3 months from M06 in leap year lands in M04 (not M03)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M05", calendar }, options).add(months1n), + 2020, 5, "M04L", "Subtracting 1 month from M05 in leap year lands in M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months1n), + 2020, 4, "M04", "Subtracting 1 month from M04L in calendar lands in M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(months12n), + 2020, 5, "M04L", "Subtracting 12 months from common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(months13n), + 2020, 4, "M04", "Subtracting 13 months from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months12n), + 2019, 4, "M04", "Subtracting 12 months from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months12n), + 2019, 5, "M05", "Subtracting 12 months from M04L lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months13n), + 2019, 4, "M04", "Subtracting 13 months from M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(new Temporal.Duration(-1, -12), options), + 1999, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(new Temporal.Duration(-2, -13), options), + 1997, 8, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(0, -24)), + 2019, 5, "M05", "Subtracting 24 months from common-year M04 crossing leap year with M04L, lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(0, -25)), + 2019, 4, "M04", "Subtracting 25 months from common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-dangi.js new file mode 100644 index 0000000000000..b8a0d63739e33 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-dangi.js @@ -0,0 +1,323 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Arithmetic around leap months in the dangi calendar +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "dangi"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); + +const leap193807L = Temporal.PlainYearMonth.from({ year: 1938, monthCode: "M07L", calendar }, options); +const leap195205L = Temporal.PlainYearMonth.from({ year: 1952, monthCode: "M05L", calendar }, options); +const leap196603L = Temporal.PlainYearMonth.from({ year: 1966, monthCode: "M03L", calendar }, options); +const common200008 = Temporal.PlainYearMonth.from({ year: 2000, monthCode: "M08", calendar }, options); +const common200108 = Temporal.PlainYearMonth.from({ year: 2001, monthCode: "M08", calendar }, options); +const common201901 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M01", calendar }, options); +const common201904 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M04", calendar }, options); +const leap202004 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04", calendar }, options); +const leap202004L = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04L", calendar }, options); +const common202104 = Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common201901.add(years1), + 2020, 1, "M01", "add 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.add(years1), + 1967, 3, "M03", "Adding 1 year to leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.add(years1, options); +}, "Adding 1 year to leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap193807L.add(years1), + 1939, 7, "M07", "Adding 1 year to leap month M07L on day 30 constrains to M07 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap193807L.add(years1, options); +}, "Adding 1 year to leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(years1, options), + 2020, 4, "M04", "Adding 1 year to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(years1, options), + 2021, 4, "M04", "Adding 1 year to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M03L", calendar }, options).add(new Temporal.Duration(-19), options), + 1993, 4, "M03L", "Subtracting years to go from one M03L to the previous M03L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(years1, options), + 2001, 9, "M08", "Adding 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(2), options), + 2021, 4, "M04", "Adding 2 years to common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201901.add(years1n), + 2018, 1, "M01", "Subtracting 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.add(years1n), + 1965, 3, "M03", "Subtracting 1 year from leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.add(years1n, options); +}, "Subtracting 1 year from leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap195205L.add(years1n), + 1951, 5, "M05", "Subtracting 1 year from leap month M05L on day 30 constrains to M05 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap195205L.add(years1n, options); +}, "Subtracting 1 year from leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(years1n, options), + 2020, 4, "M04", "Subtracting 1 year from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(years1n, options), + 2019, 4, "M04", "Subtracting 1 year from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M03L", calendar }, options).add(new Temporal.Duration(-19), options), + 1993, 4, "M03L", "Subtracting years to go from one M03L to the previous M03L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(years1n, options), + 2000, 8, "M08", "Subtracting 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(-2), options), + 2019, 4, "M04", "Subtracting 2 years from common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months12 = new Temporal.Duration(0, 12); +const months12n = new Temporal.Duration(0, -12); +const months13 = new Temporal.Duration(0, 13); +const months13n = new Temporal.Duration(0, -13); + +const leap202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); +const leap202006 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1947, monthCode: "M02L", calendar }, options).add(months1), + 1947, 4, "M03", "add 1 month, starting at start of leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1955, monthCode: "M03L", calendar }, options).add(months1), + 1955, 5, "M04", "add 1 month, starting at start of leap month with 30 days", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(months1), + 2020, 4, "M04", "adding 1 month to M03 in leap year lands in M04 (not M04L)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(new Temporal.Duration(0, 2)), + 2020, 5, "M04L", "adding 2 months to M03 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.add(new Temporal.Duration(0, 3)), + 2020, 6, "M05", "adding 3 months to M03 in leap year lands in M05 (not M06)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(months12), + 2020, 4, "M04", "Adding 12 months to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(months13), + 2020, 5, "M04L", "Adding 13 months to common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months12), + 2021, 3, "M03", "Adding 12 months to leap-year M04 lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months13), + 2021, 4, "M04", "Adding 13 months to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months12), + 2021, 4, "M04", "Adding 12 months to M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(new Temporal.Duration(1, 12), options), + 2002, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(new Temporal.Duration(2, 13), options), + 2004, 9, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(0, 24)), + 2021, 3, "M03", "Adding 24 months to common-year M04 crossing leap year with M04L, lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.add(new Temporal.Duration(0, 25)), + 2021, 4, "M04", "Adding 25 months to common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(months1n), + 2020, 6, "M05", "Subtracting 1 month from M06 in leap year lands in M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(new Temporal.Duration(0, -2)), + 2020, 5, "M04L", "Subtracting 2 months from M06 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.add(new Temporal.Duration(0, -3)), + 2020, 4, "M04", "Subtracting 3 months from M06 in leap year lands in M04 (not M03)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M05", calendar }, options).add(months1n), + 2020, 5, "M04L", "Subtracting 1 month from M05 in leap year lands in M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months1n), + 2020, 4, "M04", "Subtracting 1 month from M04L in calendar lands in M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(months12n), + 2020, 5, "M04L", "Subtracting 12 months from common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(months13n), + 2020, 4, "M04", "Subtracting 13 months from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.add(months12n), + 2019, 4, "M04", "Subtracting 12 months from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months12n), + 2019, 5, "M05", "Subtracting 12 months from M04L lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.add(months13n), + 2019, 4, "M04", "Subtracting 13 months from M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.add(new Temporal.Duration(-1, -12), options), + 1999, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.add(new Temporal.Duration(-2, -13), options), + 1997, 8, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(0, -24)), + 2019, 5, "M05", "Subtracting 24 months from common-year M04 crossing leap year with M04L, lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.add(new Temporal.Duration(0, -25)), + 2019, 4, "M04", "Subtracting 25 months from common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js new file mode 100644 index 0000000000000..af2203036eb3e --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js @@ -0,0 +1,342 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Arithmetic around leap months in the hebrew calendar +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years2 = new Temporal.Duration(2); +const years2n = new Temporal.Duration(-2); + +const leap1AdarI = Temporal.PlainYearMonth.from({ year: 5782, monthCode: "M05L", calendar }, options); +const leap1AdarII = Temporal.PlainYearMonth.from({ year: 5782, monthCode: "M06", calendar }, options); +const common1Adar = Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M06", calendar }, options); +const common = Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M08", calendar }, options); +const leap2AdarI = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M05L", calendar }, options); +const leap2AdarII = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M06", calendar }, options); +const common2Adar = Temporal.PlainYearMonth.from({ year: 5785, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(years1, options), + 5784, 7, "M06", "Adding 1 year to common-year Adar (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(years2, options), + 5785, 6, "M06", "Adding 2 years to common-year Adar (M06) crossing leap year lands in common-year Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(years1), + 5785, 6, "M06", "Adding 1 year to Adar I (M05L) lands in common-year Adar (M06) with constrain", + "am", 5785, null +); + +assert.throws(RangeError, function () { + leap2AdarI.add(years1, options); +}, "Adding 1 year to Adar I (M05L) rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(years1, options), + 5785, 6, "M06", "Adding 1 year to Adar II (M06) lands in common-year Adar (M06) even with reject", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common.add(years1, options), + 5784, 9, "M08", "Adding 1 year across Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarI.add(years2, options), + 5784, 6, "M05L", "Adding 2 years to leap-year Adar I (M05L) lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarII.add(years2, options), + 5784, 7, "M06", "Adding 2 years to leap-year Adar II (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(years1n, options), + 5784, 7, "M06", "Subtracting 1 year from common-year Adar (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(years2n, options), + 5783, 6, "M06", "Subtracting 2 years from common-year Adar (M06) crossing leap year lands in common-year Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(years1n), + 5783, 6, "M06", "Subtracting 1 year from Adar I (M05L) lands in common-year Adar (M06) with constrain", + "am", 5783, null +); + +assert.throws(RangeError, function () { + leap2AdarI.add(years1n, options); +}, "Subtracting 1 year from Adar I (M05L) rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(years1n, options), + 5783, 6, "M06", "Subtracting 1 year from Adar II (M06) lands in common-year Adar (M06) even with reject", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common.add(years2n, options), + 5781, 8, "M08", "Subtracting 2 years across Adar I (M05L)", + "am", 5781, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(years2n, options), + 5782, 6, "M05L", "Subtracting 2 years from leap-year Adar I (M05L) lands in leap-year Adar I (M05L)", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(years2n, options), + 5782, 7, "M06", "Subtracting 2 years from leap-year Adar II (M06) lands in leap-year Adar II (M06)", + "am", 5782, null +); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months2 = new Temporal.Duration(0, 2); +const months2n = new Temporal.Duration(0, -2); +const months12 = new Temporal.Duration(0, 12); +const months12n = new Temporal.Duration(0, -12); +const months13 = new Temporal.Duration(0, 13); +const months13n = new Temporal.Duration(0, -13); +const months24 = new Temporal.Duration(0, 24); +const months24n = new Temporal.Duration(0, -24); + +const date1 = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M04", calendar }, options); +const date3 = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M07", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date1.add(months1, options), + 5784, 5, "M05", "Adding 1 month to M04 in leap year lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.add(months2, options), + 5784, 6, "M05L", "Adding 2 months to M04 in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.add(new Temporal.Duration(0, 3), options), + 5784, 7, "M06", "Adding 3 months to M04 in leap year lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months1, options), + 5784, 7, "M06", "Adding 1 month to M05L (Adar I) lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M04", calendar }, options).add(months2, options), + 5783, 6, "M06", "Adding 2 months to M04 in non-leap year lands in M06 (no M05L)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(months12, options), + 5784, 6, "M05L", "Adding 12 months to common-year Adar lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(months13, options), + 5784, 7, "M06", "Adding 13 months to common-year Adar lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months12, options), + 5785, 5, "M05", "Adding 12 months to leap-year Adar I lands in Shevat (M05)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months13, options), + 5785, 6, "M06", "Adding 13 months to leap-year Adar I lands in Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(months12, options), + 5785, 6, "M06", "Adding 12 months to leap-year Adar II lands in Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common.add(months13, options), + 5784, 9, "M08", "Adding 13 months across Adar I (M05L) lands in same month code", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common.add(new Temporal.Duration(1, 12), options), + 5785, 8, "M08", "Adding 1y 12mo across Adar I (M05L) in the years part lands in same month code", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.add(new Temporal.Duration(2, 13), options), + 5787, 8, "M07", "Adding 2y 13mo across Adar I (M05L) in the months part lands in same month code", + "am", 5787, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(months24, options), + 5785, 5, "M05", "Adding 24 months to common-year Adar crossing a leap year lands in common-year Shevat (M05)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.add(new Temporal.Duration(0, 25), options), + 5785, 6, "M06", "Adding 25 months to common-year Adar crossing a leap year lands in common-year Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarI.add(months24, options), + 5784, 5, "M05", "Adding 24 months to leap-year Adar I lands in leap-year Shevat (M05)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarII.add(months24, options), + 5784, 6, "M05L", "Adding 24 months to leap-year Adar II lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.add(months1n, options), + 5784, 7, "M06", "Subtracting 1 month from M07 in leap year lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.add(months2n, options), + 5784, 6, "M05L", "Subtracting 2 months from M07 in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.add(new Temporal.Duration(0, -3), options), + 5784, 5, "M05", "Subtracting 3 months from M07 in leap year lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(months1n, options), + 5784, 6, "M05L", "Subtracting 1 month from M06 (Adar II) in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months1n, options), + 5784, 5, "M05", "Subtracting 1 month from M05L (Adar I) lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M07", calendar }).add(months2n, options), + 5783, 5, "M05", "Subtracting 2 months from M07 in non-leap year lands in M05 (no M05L)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(months12n, options), + 5784, 7, "M06", "Subtracting 12 months from common-year Adar lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(months13n, options), + 5784, 6, "M05L", "Subtracting 13 months from common-year Adar lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months12n, options), + 5783, 6, "M06", "Subtracting 12 months from leap-year Adar I lands in Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months13n, options), + 5783, 5, "M05", "Subtracting 13 months from leap-year Adar I lands in Shevat (M05)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(months12n, options), + 5783, 7, "M07", "Subtracting 12 months from leap-year Adar II lands in Nisan (M07)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(months24n, options), + 5783, 7, "M07", "Subtracting 24 months from common-year Adar crossing a leap year lands in common-year Nisan (M07)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.add(new Temporal.Duration(-2, -12), options), + 5781, 4, "M04", "Subtracting 2y 12mo across Adar I (M05L) in the years part lands in same month code", + "am", 5781, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.add(new Temporal.Duration(-1, -13), options), + 5782, 4, "M04", "Subtracting 1y 13mo across Adar I (M05L) in the months part lands in same month code", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.add(new Temporal.Duration(0, -25), options), + 5783, 6, "M06", "Subtracting 25 months from common-year Adar crossing a leap year lands in common-year Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.add(months24n, options), + 5782, 7, "M06", "Subtracting 24 months from leap-year Adar I lands in leap-year Adar (M06)", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.add(months24n, options), + 5782, 8, "M07", "Subtracting 24 months from leap-year Adar II lands in leap-year Nisan (M07)", + "am", 5782, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-buddhist.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-buddhist.js new file mode 100644 index 0000000000000..bae673087c546 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-buddhist.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (buddhist calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "buddhist"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +const date256302 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date256302.add(years1, options), + 2564, 2, "M02", "add 1y to Feb", + "be", 2564); + +TemporalHelpers.assertPlainYearMonth( + date256302.add(years4, options), + 2567, 2, "M02", "add 4y to Feb", + "be", 2567); + +TemporalHelpers.assertPlainYearMonth( + date256302.add(years1n, options), + 2562, 2, "M02", "subtract 1y from Feb", + "be", 2562); + +TemporalHelpers.assertPlainYearMonth( + date256302.add(years4n, options), + 2559, 2, "M02", "subtract 4y from Feb", + "be", 2559); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months5 = new Temporal.Duration(0, 5); +const months11n = new Temporal.Duration(0, -11); +const years1months2 = new Temporal.Duration(1, 2); +const years1months2n = new Temporal.Duration(-1, -2); + +const date256301 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M01", calendar }, options); +const date256303 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date256301.add(months1, options), + 2563, 2, "M02", "add 1mo to Jan in leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M09", calendar }, options).add(months5), + 2565, 2, "M02", "add 5mo with result in the next year", + "be", 2565); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2562, monthCode: "M09", calendar }, options).add(months5), + 2563, 2, "M02", "add 5mo with result in the next leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M12", calendar }, options).add(years1months2), + 2566, 2, "M02", "add 1y 2mo with result in the next year", + "be", 2566); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2565, monthCode: "M12", calendar }, options).add(years1months2), + 2567, 2, "M02", "add 1y 2mo with result in the next leap year", + "be", 2567); + +TemporalHelpers.assertPlainYearMonth( + date256303.add(months1n, options), + 2563, 2, "M02", "subtract 1mo from Mar in leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + date256301.add(months11n), + 2562, 2, "M02", "subtract 11mo with result in the previous year", + "be", 2562); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M01", calendar }, options).add(months11n), + 2563, 2, "M02", "add 11mo with result in the previous leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2565, monthCode: "M04", calendar }, options).add(years1months2n), + 2564, 2, "M02", "add 1y 2mo with result in the previous year", + "be", 2564); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M04", calendar }, options).add(years1months2n), + 2563, 2, "M02", "add 1y 2mo with result in the previous leap year", + "be", 2563); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-coptic.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-coptic.js new file mode 100644 index 0000000000000..856d70016b329 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-coptic.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (coptic calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "coptic"; +const options = { overflow: "reject" }; + +const epagomenalMonth = Temporal.PlainYearMonth.from({ year: 1739, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1, options), + 1740, 13, "M13", "Adding 1 year to epagomenal month", + "am", 1740, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1n, options), + 1738, 13, "M13", "Subtracting 1 year from epagomenal month", + "am", 1738, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4, options), + 1743, 13, "M13", "Adding 4 years to epagomenal month", + "am", 1743, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4n, options), + 1735, 13, "M13", "Subtracting 4 years from epagomenal month", + "am", 1735, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethioaa.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethioaa.js new file mode 100644 index 0000000000000..9b896836b9f10 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethioaa.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (ethioaa calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "ethioaa"; +const options = { overflow: "reject" }; + +const epagomenalMonth = Temporal.PlainYearMonth.from({ year: 7515, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1, options), + 7516, 13, "M13", "Adding 1 year to epagomenal month", + "aa", 7516, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1n, options), + 7514, 13, "M13", "Subtracting 1 year from epagomenal month", + "aa", 7514, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4, options), + 7519, 13, "M13", "Adding 4 years to epagomenal month", + "aa", 7519, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4n, options), + 7511, 13, "M13", "Subtracting 4 years from epagomenal month", + "aa", 7511, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethiopic.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethiopic.js new file mode 100644 index 0000000000000..6230dfb721810 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethiopic.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (ethiopic calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "ethiopic"; +const options = { overflow: "reject" }; + +const epagomenalMonth = Temporal.PlainYearMonth.from({ year: 2015, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1, options), + 2016, 13, "M13", "Adding 1 year to epagomenal month", + "am", 2016, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years1n, options), + 2014, 13, "M13", "Subtracting 1 year from epagomenal month", + "am", 2014, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4, options), + 2019, 13, "M13", "Adding 4 years to epagomenal month", + "am", 2019, null); + +TemporalHelpers.assertPlainYearMonth( + epagomenalMonth.add(years4n, options), + 2011, 13, "M13", "Subtracting 4 years from epagomenal month", + "am", 2011, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-gregory.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-gregory.js new file mode 100644 index 0000000000000..8dbca9488a129 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-gregory.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (gregory calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "gregory"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +const date202002 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years1, options), + 2021, 2, "M02", "add 1y to Feb", + "ce", 2021); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years4, options), + 2024, 2, "M02", "add 4y to Feb", + "ce", 2024); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years1n, options), + 2019, 2, "M02", "subtract 1y from Feb", + "ce", 2019); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years4n, options), + 2016, 2, "M02", "subtract 4y from Feb", + "ce", 2016); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months5 = new Temporal.Duration(0, 5); +const months11n = new Temporal.Duration(0, -11); +const years1months2 = new Temporal.Duration(1, 2); +const years1months2n = new Temporal.Duration(-1, -2); + +const date202001 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M01", calendar }, options); +const date202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202001.add(months1, options), + 2020, 2, "M02", "add 1mo to Jan in leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M09", calendar }, options).add(months5), + 2022, 2, "M02", "add 5mo with result in the next year", + "ce", 2022); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M09", calendar }, options).add(months5), + 2020, 2, "M02", "add 5mo with result in the next leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M12", calendar }, options).add(years1months2), + 2023, 2, "M02", "add 1y 2mo with result in the next year", + "ce", 2023); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M12", calendar }, options).add(years1months2), + 2024, 2, "M02", "add 1y 2mo with result in the next leap year", + "ce", 2024); + +TemporalHelpers.assertPlainYearMonth( + date202003.add(months1n, options), + 2020, 2, "M02", "subtract 1mo from Mar in leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + date202001.add(months11n), + 2019, 2, "M02", "subtract 11mo with result in the previous year", + "ce", 2019); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01", calendar }, options).add(months11n), + 2020, 2, "M02", "add 11mo with result in the previous leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M04", calendar }, options).add(years1months2n), + 2021, 2, "M02", "add 1y 2mo with result in the previous year", + "ce", 2021); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options).add(years1months2n), + 2020, 2, "M02", "add 1y 2mo with result in the previous leap year", + "ce", 2020); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-indian.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-indian.js new file mode 100644 index 0000000000000..f38462fc215f7 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-indian.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (indian calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "indian"; +const options = { overflow: "reject" }; + +const leap = Temporal.PlainYearMonth.from({ year: 1946, monthCode: "M01", calendar }, options); + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +TemporalHelpers.assertPlainYearMonth( + leap.add(years1, options), + 1947, 1, "M01", "Adding 1 year to Chaitra", + "shaka", 1947, null); + +TemporalHelpers.assertPlainYearMonth( + leap.add(years1n, options), + 1945, 1, "M01", "Subtracting 1 year from Chaitra", + "shaka", 1945, null); + +TemporalHelpers.assertPlainYearMonth( + leap.add(years4, options), + 1950, 1, "M01", "Adding 4 years to Chaitra", + "shaka", 1950, null); + +TemporalHelpers.assertPlainYearMonth( + leap.add(years4n, options), + 1942, 1, "M01", "Subtracting 4 years from Chaitra", + "shaka", 1942, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-civil.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-civil.js new file mode 100644 index 0000000000000..6260a2abb3545 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-civil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (islamic-civil calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "islamic-civil"; +const options = { overflow: "reject" }; + +// Month 12 (Dhu al-Hijjah) has 29 days in common years and 30 in leap years. +// AH 1442, 1445, and 1447 are leap years. + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years2 = new Temporal.Duration(2); +const years3n = new Temporal.Duration(-3); + +const date144512 = Temporal.PlainYearMonth.from({ year: 1445, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years1, options), + 1446, 12, "M12", "add 1y to leap", + "ah", 1446, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years2, options), + 1447, 12, "M12", "add 2y to leap landing in next leap year", + "ah", 1447, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years1n, options), + 1444, 12, "M12", "subtract 1y from leap", + "ah", 1444, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years3n, options), + 1442, 12, "M12", "subtract 3y from leap landing in previous leap year", + "ah", 1442, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-tbla.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-tbla.js new file mode 100644 index 0000000000000..a70525193f27c --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-tbla.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (islamic-tbla calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "islamic-tbla"; +const options = { overflow: "reject" }; + +// Month 12 (Dhu al-Hijjah) has 29 days in common years and 30 in leap years. +// AH 1442, 1445, and 1447 are leap years. + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years2 = new Temporal.Duration(2); +const years3n = new Temporal.Duration(-3); + +const date144512 = Temporal.PlainYearMonth.from({ year: 1445, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years1, options), + 1446, 12, "M12", "add 1y to leap", + "ah", 1446, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years2, options), + 1447, 12, "M12", "add 2y to leap landing in next leap year", + "ah", 1447, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years1n, options), + 1444, 12, "M12", "subtract 1y from leap", + "ah", 1444, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.add(years3n, options), + 1442, 12, "M12", "subtract 3y from leap landing in previous leap year", + "ah", 1442, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-japanese.js new file mode 100644 index 0000000000000..fd2c008ff6689 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-japanese.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (japanese calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "japanese"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +const date202002 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years1, options), + 2021, 2, "M02", "add 1y to Feb", + "reiwa", 3); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years4, options), + 2024, 2, "M02", "add 4y to Feb", + "reiwa", 6); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years1n, options), + 2019, 2, "M02", "subtract 1y from Feb", + "heisei", 31); + +TemporalHelpers.assertPlainYearMonth( + date202002.add(years4n, options), + 2016, 2, "M02", "subtract 4y from Feb", + "heisei", 28); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months5 = new Temporal.Duration(0, 5); +const months11n = new Temporal.Duration(0, -11); +const years1months2 = new Temporal.Duration(1, 2); +const years1months2n = new Temporal.Duration(-1, -2); + +const date20200131 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M01", calendar }, options); +const date20200331 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date20200131.add(months1, options), + 2020, 2, "M02", "add 1mo to Jan in leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M09", calendar }, options).add(months5), + 2022, 2, "M02", "add 5mo with result in the next year", + "reiwa", 4); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M09", calendar }, options).add(months5), + 2020, 2, "M02", "add 5mo with result in the next leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M12", calendar }, options).add(years1months2), + 2023, 2, "M02", "add 1y 2mo with result in the next year", + "reiwa", 5); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M12", calendar }, options).add(years1months2), + 2024, 2, "M02", "add 1y 2mo with result in the next leap year", + "reiwa", 6); + +TemporalHelpers.assertPlainYearMonth( + date20200331.add(months1n, options), + 2020, 2, "M02", "subtract 1mo from Mar in leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + date20200131.add(months11n), + 2019, 2, "M02", "subtract 11mo with result in the previous year", + "heisei", 31); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01", calendar }, options).add(months11n), + 2020, 2, "M02", "add 11mo with result in the previous leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M04", calendar }, options).add(years1months2n), + 2021, 2, "M02", "add 1y 2mo with result in the previous year", + "reiwa", 3); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options).add(years1months2n), + 2020, 2, "M02", "add 1y 2mo with result in the previous leap year", + "reiwa", 2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-persian.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-persian.js new file mode 100644 index 0000000000000..40a771edce043 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-persian.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (Persian calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "persian"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +const date136212 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date136212.add(years1, options), + 1363, 12, "M12", "add 1y to leap", + "ap", 1363, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.add(years4, options), + 1366, 12, "M12", "add 4y to leap", + "ap", 1366, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.add(years1n, options), + 1361, 12, "M12", "subtract 1y from leap", + "ap", 1361, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.add(years4n, options), + 1358, 12, "M12", "subtract 4y from leap", + "ap", 1358, null); + +// Months + +const months1n = new Temporal.Duration(0, -1); +const months6 = new Temporal.Duration(0, 6); +const months11n = new Temporal.Duration(0, -11); +const years1months2 = new Temporal.Duration(1, 2); +const years1months2n = new Temporal.Duration(-1, -2); + +const date136206 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M06", calendar }, options); +const date136211 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M11", calendar }, options); +const date136301 = Temporal.PlainYearMonth.from({ year: 1363, monthCode: "M01", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date136206.add(months6, options), + 1362, 12, "M12", "add 6mo to Shahrivar in leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M10", calendar }, options).add(years1months2), + 1363, 12, "M12", "add 1y 2mo with result in the next year", + "ap", 1363, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1361, monthCode: "M10", calendar }, options).add(years1months2), + 1362, 12, "M12", "add 1y 2mo with result in the next leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + date136301.add(months1n, options), + 1362, 12, "M12", "subtract 1mo from Farvardin in leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + date136211.add(months11n), + 1361, 12, "M12", "subtract 11mo with result in the previous year", + "ap", 1361, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1363, monthCode: "M11", calendar }, options).add(months11n), + 1362, 12, "M12", "subtract 11mo with result in the previous leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1364, monthCode: "M02", calendar }, options).add(years1months2n), + 1362, 12, "M12", "add 1y 2mo with result in the previous year", + "ap", 1362, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1365, monthCode: "M02", calendar }, options).add(years1months2n), + 1363, 12, "M12", "add 1y 2mo with result in the previous leap year", + "ap", 1363, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-roc.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-roc.js new file mode 100644 index 0000000000000..11c1976c9bab9 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-roc.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.add +description: Check various basic calculations involving leap years (roc calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "roc"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(1); +const years1n = new Temporal.Duration(-1); +const years4 = new Temporal.Duration(4); +const years4n = new Temporal.Duration(-4); + +const date10902 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date10902.add(years1, options), + 110, 2, "M02", "add 1y to Feb", + "roc", 110); + +TemporalHelpers.assertPlainYearMonth( + date10902.add(years4, options), + 113, 2, "M02", "add 4y to Feb", + "roc", 113); + +TemporalHelpers.assertPlainYearMonth( + date10902.add(years1n, options), + 108, 2, "M02", "subtract 1y from Feb", + "roc", 108); + +TemporalHelpers.assertPlainYearMonth( + date10902.add(years4n, options), + 105, 2, "M02", "subtract 4y from Feb", + "roc", 105); + +// Months + +const months1 = new Temporal.Duration(0, 1); +const months1n = new Temporal.Duration(0, -1); +const months5 = new Temporal.Duration(0, 5); +const months11n = new Temporal.Duration(0, -11); +const years1months2 = new Temporal.Duration(1, 2); +const years1months2n = new Temporal.Duration(-1, -2); + +const date1090131 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M01", calendar }, options); +const date1090331 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date1090131.add(months1, options), + 109, 2, "M02", "add 1mo to Jan in leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M09", calendar }, options).add(months5), + 111, 2, "M02", "add 5mo with result in the next year", + "roc", 111); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 108, monthCode: "M09", calendar }, options).add(months5), + 109, 2, "M02", "add 5mo with result in the next leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M12", calendar }, options).add(years1months2), + 112, 2, "M02", "add 1y 2mo with result in the next year", + "roc", 112); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 111, monthCode: "M12", calendar }, options).add(years1months2), + 113, 2, "M02", "add 1y 2mo with result in the next leap year", + "roc", 113); + +TemporalHelpers.assertPlainYearMonth( + date1090331.add(months1n, options), + 109, 2, "M02", "subtract 1mo from Mar in leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + date1090131.add(months11n), + 108, 2, "M02", "subtract 11mo with result in the previous year", + "roc", 108); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M01", calendar }, options).add(months11n), + 109, 2, "M02", "add 11mo with result in the previous leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 111, monthCode: "M04", calendar }, options).add(years1months2n), + 110, 2, "M02", "add 1y 2mo with result in the previous year", + "roc", 110); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M04", calendar }, options).add(years1months2n), + 109, 2, "M02", "add 1y 2mo with result in the previous leap year", + "roc", 109); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/add/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/equals/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/equals/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/equals/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/equals/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/monthCode/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/monthCode/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/monthCode/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/monthCode/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-japanese.js index 73cae758b07d2..2a4a4fc986d6b 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-japanese.js @@ -14,7 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainYearMonth.from({ era: "bce", eraYear: 1, monthCode: "M06", calendar }, options); const ce1 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1, monthCode: "M06", calendar }, options); -const meiji5 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 5, monthCode: "M01", calendar }, options); +const ce1872 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1872, monthCode: "M12", calendar }, options); +const meiji6 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 6, monthCode: "M01", calendar}, options); +const meiji7 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 7, monthCode: "M01", calendar }, options); const meiji45 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 45, monthCode: "M05", calendar }, options); const taisho1 = Temporal.PlainYearMonth.from({ era: "taisho", eraYear: 1, monthCode: "M08", calendar }, options); const taisho6 = Temporal.PlainYearMonth.from({ era: "taisho", eraYear: 6, monthCode: "M03", calendar }, options); @@ -82,16 +84,16 @@ const tests = [ [63, 0, "63y from Taisho 6 March to Showa 55 March"], [0, 756, "756mo from Taisho 6 March to Showa 55 March"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [-45, -2, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [-43, -2, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [45, 2, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, "542mo from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [43, 2, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, "518mo from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -104,6 +106,17 @@ const tests = [ [0, 3, "3mo from Meiji 45 May to Taisho 1 August"], [0, 3, "3mo from Meiji 45 May to Taisho 1 August"], ], + // Last pre-solar-calendar CE month to first solar-calendar month of Meiji era + [ + ce1872, meiji6, + [0, -1, "backwards from month before solar Meiji era to first month"], + [0, -1, "backwards from month before solar Meiji era to first month"], + ], + [ + meiji6, ce1872, + [0, 1, "from month before solar Meiji era to first month"], + [0, 1, "from month before solar Meiji era to first month"], + ], // CE-BCE boundary [ bce1, ce1, diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/since/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-chinese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-chinese.js new file mode 100644 index 0000000000000..05ffc5187a3ce --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-chinese.js @@ -0,0 +1,323 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Arithmetic around leap months in the chinese calendar +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "chinese"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); + +const leap193807L = Temporal.PlainYearMonth.from({ year: 1938, monthCode: "M07L", calendar }, options); +const leap195205L = Temporal.PlainYearMonth.from({ year: 1952, monthCode: "M05L", calendar }, options); +const leap196603L = Temporal.PlainYearMonth.from({ year: 1966, monthCode: "M03L", calendar }, options); +const common200008 = Temporal.PlainYearMonth.from({ year: 2000, monthCode: "M08", calendar }, options); +const common200108 = Temporal.PlainYearMonth.from({ year: 2001, monthCode: "M08", calendar }, options); +const common201901 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M01", calendar }, options); +const common201904 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M04", calendar }, options); +const leap202004 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04", calendar }, options); +const leap202004L = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04L", calendar }, options); +const common202104 = Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common201901.subtract(years1), + 2020, 1, "M01", "add 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.subtract(years1), + 1967, 3, "M03", "Adding 1 year to leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.subtract(years1, options); +}, "Adding 1 year to leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap193807L.subtract(years1), + 1939, 7, "M07", "Adding 1 year to leap month M07L on day 30 constrains to M07 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap193807L.subtract(years1, options); +}, "Adding 1 year to leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(years1, options), + 2020, 4, "M04", "Adding 1 year to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(years1, options), + 2021, 4, "M04", "Adding 1 year to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M04L", calendar }, options).subtract(new Temporal.Duration(-8), options), + 2020, 5, "M04L", "Adding years to go from one M04L to the next M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(years1, options), + 2001, 9, "M08", "Adding 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(-2), options), + 2021, 4, "M04", "Adding 2 years to common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201901.subtract(years1n), + 2018, 1, "M01", "Subtracting 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.subtract(years1n), + 1965, 3, "M03", "Subtracting 1 year from leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.subtract(years1n, options); +}, "Subtracting 1 year from leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap195205L.subtract(years1n), + 1951, 5, "M05", "Subtracting 1 year from leap month M05L on day 30 constrains to M05 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap195205L.subtract(years1n, options); +}, "Subtracting 1 year from leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(years1n, options), + 2020, 4, "M04", "Subtracting 1 year from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(years1n, options), + 2019, 4, "M04", "Subtracting 1 year from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(new Temporal.Duration(8), options), + 2012, 5, "M04L", "Subtracting years to go from one M04L to the previous M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(years1n, options), + 2000, 8, "M08", "Subtracting 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(2), options), + 2019, 4, "M04", "Subtracting 2 years from common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months12 = new Temporal.Duration(0, -12); +const months12n = new Temporal.Duration(0, 12); +const months13 = new Temporal.Duration(0, -13); +const months13n = new Temporal.Duration(0, 13); + +const leap202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); +const leap202006 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1947, monthCode: "M02L", calendar }, options).subtract(months1), + 1947, 4, "M03", "add 1 month, starting at start of leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1955, monthCode: "M03L", calendar }, options).subtract(months1), + 1955, 5, "M04", "add 1 month, starting at start of leap month with 30 days", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(months1), + 2020, 4, "M04", "adding 1 month to M03 in leap year lands in M04 (not M04L)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(new Temporal.Duration(0, -2)), + 2020, 5, "M04L", "adding 2 months to M03 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(new Temporal.Duration(0, -3)), + 2020, 6, "M05", "adding 3 months to M03 in leap year lands in M05 (not M06)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(months12), + 2020, 4, "M04", "Adding 12 months to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(months13), + 2020, 5, "M04L", "Adding 13 months to common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months12), + 2021, 3, "M03", "Adding 12 months to leap-year M04 lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months13), + 2021, 4, "M04", "Adding 13 months to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months12), + 2021, 4, "M04", "Adding 12 months to M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(new Temporal.Duration(-1, -12), options), + 2002, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(new Temporal.Duration(-2, -13), options), + 2004, 9, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(0, -24)), + 2021, 3, "M03", "Adding 24 months to common-year M04 crossing leap year with M04L, lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(0, -25)), + 2021, 4, "M04", "Adding 25 months to common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(months1n), + 2020, 6, "M05", "Subtracting 1 month from M06 in leap year lands in M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(new Temporal.Duration(0, 2)), + 2020, 5, "M04L", "Subtracting 2 months from M06 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(new Temporal.Duration(0, 3)), + 2020, 4, "M04", "Subtracting 3 months from M06 in leap year lands in M04 (not M03)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M05", calendar }, options).subtract(months1n), + 2020, 5, "M04L", "Subtracting 1 month from M05 in leap year lands in M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months1n), + 2020, 4, "M04", "Subtracting 1 month from M04L in calendar lands in M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(months12n), + 2020, 5, "M04L", "Subtracting 12 months from common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(months13n), + 2020, 4, "M04", "Subtracting 13 months from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months12n), + 2019, 4, "M04", "Subtracting 12 months from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months12n), + 2019, 5, "M05", "Subtracting 12 months from M04L lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months13n), + 2019, 4, "M04", "Subtracting 13 months from M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(new Temporal.Duration(1, 12), options), + 1999, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(new Temporal.Duration(2, 13), options), + 1997, 8, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(0, 24)), + 2019, 5, "M05", "Subtracting 24 months from common-year M04 crossing leap year with M04L, lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(0, 25)), + 2019, 4, "M04", "Subtracting 25 months from common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-dangi.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-dangi.js new file mode 100644 index 0000000000000..fbe1d292ee214 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-dangi.js @@ -0,0 +1,323 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Arithmetic around leap months in the dangi calendar +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "dangi"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); + +const leap193807L = Temporal.PlainYearMonth.from({ year: 1938, monthCode: "M07L", calendar }, options); +const leap195205L = Temporal.PlainYearMonth.from({ year: 1952, monthCode: "M05L", calendar }, options); +const leap196603L = Temporal.PlainYearMonth.from({ year: 1966, monthCode: "M03L", calendar }, options); +const common200008 = Temporal.PlainYearMonth.from({ year: 2000, monthCode: "M08", calendar }, options); +const common200108 = Temporal.PlainYearMonth.from({ year: 2001, monthCode: "M08", calendar }, options); +const common201901 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M01", calendar }, options); +const common201904 = Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M04", calendar }, options); +const leap202004 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04", calendar }, options); +const leap202004L = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M04L", calendar }, options); +const common202104 = Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common201901.subtract(years1), + 2020, 1, "M01", "add 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.subtract(years1), + 1967, 3, "M03", "Adding 1 year to leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.subtract(years1, options); +}, "Adding 1 year to leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap193807L.subtract(years1), + 1939, 7, "M07", "Adding 1 year to leap month M07L on day 30 constrains to M07 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap193807L.subtract(years1, options); +}, "Adding 1 year to leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(years1, options), + 2020, 4, "M04", "Adding 1 year to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(years1, options), + 2021, 4, "M04", "Adding 1 year to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M03L", calendar }, options).subtract(new Temporal.Duration(19), options), + 1993, 4, "M03L", "Subtracting years to go from one M03L to the previous M03L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(years1, options), + 2001, 9, "M08", "Adding 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(-2), options), + 2021, 4, "M04", "Adding 2 years to common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201901.subtract(years1n), + 2018, 1, "M01", "Subtracting 1 year from non-leap day", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap196603L.subtract(years1n), + 1965, 3, "M03", "Subtracting 1 year from leap month M03L lands in common-year M03 with overflow constrain", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap196603L.subtract(years1n, options); +}, "Subtracting 1 year from leap month rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap195205L.subtract(years1n), + 1951, 5, "M05", "Subtracting 1 year from leap month M05L on day 30 constrains to M05 day 29", + undefined, undefined, null +); + +assert.throws(RangeError, function () { + leap195205L.subtract(years1n, options); +}, "Subtracting 1 year from leap month day 30 rejects"); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(years1n, options), + 2020, 4, "M04", "Subtracting 1 year from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(years1n, options), + 2019, 4, "M04", "Subtracting 1 year from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2012, monthCode: "M03L", calendar }, options).subtract(new Temporal.Duration(19), options), + 1993, 4, "M03L", "Subtracting years to go from one M03L to the previous M03L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(years1n, options), + 2000, 8, "M08", "Subtracting 1 year crossing leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(2), options), + 2019, 4, "M04", "Subtracting 2 years from common-year M04 crossing leap year lands in common-year M04", + undefined, undefined, null +); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months12 = new Temporal.Duration(0, -12); +const months12n = new Temporal.Duration(0, 12); +const months13 = new Temporal.Duration(0, -13); +const months13n = new Temporal.Duration(0, 13); + +const leap202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); +const leap202006 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1947, monthCode: "M02L", calendar }, options).subtract(months1), + 1947, 4, "M03", "add 1 month, starting at start of leap month", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1955, monthCode: "M03L", calendar }, options).subtract(months1), + 1955, 5, "M04", "add 1 month, starting at start of leap month with 30 days", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(months1), + 2020, 4, "M04", "adding 1 month to M03 in leap year lands in M04 (not M04L)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(new Temporal.Duration(0, -2)), + 2020, 5, "M04L", "adding 2 months to M03 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202003.subtract(new Temporal.Duration(0, -3)), + 2020, 6, "M05", "adding 3 months to M03 in leap year lands in M05 (not M06)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(months12), + 2020, 4, "M04", "Adding 12 months to common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(months13), + 2020, 5, "M04L", "Adding 13 months to common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months12), + 2021, 3, "M03", "Adding 12 months to leap-year M04 lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months13), + 2021, 4, "M04", "Adding 13 months to leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months12), + 2021, 4, "M04", "Adding 12 months to M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(new Temporal.Duration(-1, -12), options), + 2002, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(new Temporal.Duration(-2, -13), options), + 2004, 9, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(0, -24)), + 2021, 3, "M03", "Adding 24 months to common-year M04 crossing leap year with M04L, lands in common-year M03", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common201904.subtract(new Temporal.Duration(0, -25)), + 2021, 4, "M04", "Adding 25 months to common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(months1n), + 2020, 6, "M05", "Subtracting 1 month from M06 in leap year lands in M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(new Temporal.Duration(0, 2)), + 2020, 5, "M04L", "Subtracting 2 months from M06 in leap year lands in M04L (leap month)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202006.subtract(new Temporal.Duration(0, 3)), + 2020, 4, "M04", "Subtracting 3 months from M06 in leap year lands in M04 (not M03)", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M05", calendar }, options).subtract(months1n), + 2020, 5, "M04L", "Subtracting 1 month from M05 in leap year lands in M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months1n), + 2020, 4, "M04", "Subtracting 1 month from M04L in calendar lands in M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(months12n), + 2020, 5, "M04L", "Subtracting 12 months from common-year M04 lands in leap-year M04L", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(months13n), + 2020, 4, "M04", "Subtracting 13 months from common-year M04 lands in leap-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004.subtract(months12n), + 2019, 4, "M04", "Subtracting 12 months from leap-year M04 lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months12n), + 2019, 5, "M05", "Subtracting 12 months from M04L lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + leap202004L.subtract(months13n), + 2019, 4, "M04", "Subtracting 13 months from M04L lands in common-year M04", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200108.subtract(new Temporal.Duration(1, 12), options), + 1999, 8, "M08", "Adding 1y 12mo crossing leap month in the year part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common200008.subtract(new Temporal.Duration(2, 13), options), + 1997, 8, "M08", "Adding 1y 13mo crossing leap month in the months part", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(0, 24)), + 2019, 5, "M05", "Subtracting 24 months from common-year M04 crossing leap year with M04L, lands in common-year M05", + undefined, undefined, null +); + +TemporalHelpers.assertPlainYearMonth( + common202104.subtract(new Temporal.Duration(0, 25)), + 2019, 4, "M04", "Subtracting 25 months from common-year M04 crossing leap year with M04L, lands in common-year M04", + undefined, undefined, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-hebrew.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-hebrew.js new file mode 100644 index 0000000000000..8bdc79df5a552 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-hebrew.js @@ -0,0 +1,342 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Arithmetic around leap months in the hebrew calendar +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years2 = new Temporal.Duration(-2); +const years2n = new Temporal.Duration(2); + +const leap1AdarI = Temporal.PlainYearMonth.from({ year: 5782, monthCode: "M05L", calendar }, options); +const leap1AdarII = Temporal.PlainYearMonth.from({ year: 5782, monthCode: "M06", calendar }, options); +const common1Adar = Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M06", calendar }, options); +const common = Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M08", calendar }, options); +const leap2AdarI = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M05L", calendar }, options); +const leap2AdarII = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M06", calendar }, options); +const common2Adar = Temporal.PlainYearMonth.from({ year: 5785, monthCode: "M06", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(years1, options), + 5784, 7, "M06", "Adding 1 year to common-year Adar (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(years2, options), + 5785, 6, "M06", "Adding 2 years to common-year Adar (M06) crossing leap year lands in common-year Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(years1), + 5785, 6, "M06", "Adding 1 year to Adar I (M05L) lands in common-year Adar (M06) with constrain", + "am", 5785, null +); + +assert.throws(RangeError, function () { + leap2AdarI.subtract(years1, options); +}, "Adding 1 year to Adar I (M05L) rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(years1, options), + 5785, 6, "M06", "Adding 1 year to Adar II (M06) lands in common-year Adar (M06) even with reject", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common.subtract(years1, options), + 5784, 9, "M08", "Adding 1 year across Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarI.subtract(years2, options), + 5784, 6, "M05L", "Adding 2 years to leap-year Adar I (M05L) lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarII.subtract(years2, options), + 5784, 7, "M06", "Adding 2 years to leap-year Adar II (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(years1n, options), + 5784, 7, "M06", "Subtracting 1 year from common-year Adar (M06) lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(years2n, options), + 5783, 6, "M06", "Subtracting 2 years from common-year Adar (M06) crossing leap year lands in common-year Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(years1n), + 5783, 6, "M06", "Subtracting 1 year from Adar I (M05L) lands in common-year Adar (M06) with constrain", + "am", 5783, null +); + +assert.throws(RangeError, function () { + leap2AdarI.subtract(years1n, options); +}, "Subtracting 1 year from Adar I (M05L) rejects"); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(years1n, options), + 5783, 6, "M06", "Subtracting 1 year from Adar II (M06) lands in common-year Adar (M06) even with reject", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common.subtract(years2n, options), + 5781, 8, "M08", "Subtracting 2 years across Adar I (M05L)", + "am", 5781, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(years2n, options), + 5782, 6, "M05L", "Subtracting 2 years from leap-year Adar I (M05L) lands in leap-year Adar I (M05L)", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(years2n, options), + 5782, 7, "M06", "Subtracting 2 years from leap-year Adar II (M06) lands in leap-year Adar II (M06)", + "am", 5782, null +); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months2 = new Temporal.Duration(0, -2); +const months2n = new Temporal.Duration(0, 2); +const months12 = new Temporal.Duration(0, -12); +const months12n = new Temporal.Duration(0, 12); +const months13 = new Temporal.Duration(0, -13); +const months13n = new Temporal.Duration(0, 13); +const months24 = new Temporal.Duration(0, -24); +const months24n = new Temporal.Duration(0, 24); + +const date1 = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M04", calendar }, options); +const date3 = Temporal.PlainYearMonth.from({ year: 5784, monthCode: "M07", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date1.subtract(months1, options), + 5784, 5, "M05", "Adding 1 month to M04 in leap year lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.subtract(months2, options), + 5784, 6, "M05L", "Adding 2 months to M04 in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.subtract(new Temporal.Duration(0, -3), options), + 5784, 7, "M06", "Adding 3 months to M04 in leap year lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months1, options), + 5784, 7, "M06", "Adding 1 month to M05L (Adar I) lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M04", calendar }, options).subtract(months2, options), + 5783, 6, "M06", "Adding 2 months to M04 in non-leap year lands in M06 (no M05L)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(months12, options), + 5784, 6, "M05L", "Adding 12 months to common-year Adar lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(months13, options), + 5784, 7, "M06", "Adding 13 months to common-year Adar lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months12, options), + 5785, 5, "M05", "Adding 12 months to leap-year Adar I lands in Shevat (M05)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months13, options), + 5785, 6, "M06", "Adding 13 months to leap-year Adar I lands in Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(months12, options), + 5785, 6, "M06", "Adding 12 months to leap-year Adar II lands in Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common.subtract(months13, options), + 5784, 9, "M08", "Adding 13 months across Adar I (M05L) lands in same month code", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common.subtract(new Temporal.Duration(-1, -12), options), + 5785, 8, "M08", "Adding 1y 12mo across Adar I (M05L) in the years part lands in same month code", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.subtract(new Temporal.Duration(-2, -13), options), + 5787, 8, "M07", "Adding 2y 13mo across Adar I (M05L) in the months part lands in same month code", + "am", 5787, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(months24, options), + 5785, 5, "M05", "Adding 24 months to common-year Adar crossing a leap year lands in common-year Shevat (M05)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + common1Adar.subtract(new Temporal.Duration(0, -25), options), + 5785, 6, "M06", "Adding 25 months to common-year Adar crossing a leap year lands in common-year Adar (M06)", + "am", 5785, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarI.subtract(months24, options), + 5784, 5, "M05", "Adding 24 months to leap-year Adar I lands in leap-year Shevat (M05)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap1AdarII.subtract(months24, options), + 5784, 6, "M05L", "Adding 24 months to leap-year Adar II lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.subtract(months1n, options), + 5784, 7, "M06", "Subtracting 1 month from M07 in leap year lands in M06 (Adar II)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.subtract(months2n, options), + 5784, 6, "M05L", "Subtracting 2 months from M07 in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + date3.subtract(new Temporal.Duration(0, 3), options), + 5784, 5, "M05", "Subtracting 3 months from M07 in leap year lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(months1n, options), + 5784, 6, "M05L", "Subtracting 1 month from M06 (Adar II) in leap year lands in M05L (Adar I)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months1n, options), + 5784, 5, "M05", "Subtracting 1 month from M05L (Adar I) lands in M05 (Shevat)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 5783, monthCode: "M07", calendar }).subtract(months2n, options), + 5783, 5, "M05", "Subtracting 2 months from M07 in non-leap year lands in M05 (no M05L)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(months12n, options), + 5784, 7, "M06", "Subtracting 12 months from common-year Adar lands in leap-year Adar II (M06)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(months13n, options), + 5784, 6, "M05L", "Subtracting 13 months from common-year Adar lands in leap-year Adar I (M05L)", + "am", 5784, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months12n, options), + 5783, 6, "M06", "Subtracting 12 months from leap-year Adar I lands in Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months13n, options), + 5783, 5, "M05", "Subtracting 13 months from leap-year Adar I lands in Shevat (M05)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(months12n, options), + 5783, 7, "M07", "Subtracting 12 months from leap-year Adar II lands in Nisan (M07)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(months24n, options), + 5783, 7, "M07", "Subtracting 24 months from common-year Adar crossing a leap year lands in common-year Nisan (M07)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.subtract(new Temporal.Duration(2, 12), options), + 5781, 4, "M04", "Subtracting 2y 12mo across Adar I (M05L) in the years part lands in same month code", + "am", 5781, null +); + +TemporalHelpers.assertPlainYearMonth( + date1.subtract(new Temporal.Duration(1, 13), options), + 5782, 4, "M04", "Subtracting 1y 13mo across Adar I (M05L) in the months part lands in same month code", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + common2Adar.subtract(new Temporal.Duration(0, 25), options), + 5783, 6, "M06", "Subtracting 25 months from common-year Adar crossing a leap year lands in common-year Adar (M06)", + "am", 5783, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarI.subtract(months24n, options), + 5782, 7, "M06", "Subtracting 24 months from leap-year Adar I lands in leap-year Adar (M06)", + "am", 5782, null +); + +TemporalHelpers.assertPlainYearMonth( + leap2AdarII.subtract(months24n, options), + 5782, 8, "M07", "Subtracting 24 months from leap-year Adar II lands in leap-year Nisan (M07)", + "am", 5782, null +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-buddhist.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-buddhist.js new file mode 100644 index 0000000000000..727c112c8ccf2 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-buddhist.js @@ -0,0 +1,100 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (buddhist calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "buddhist"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +const date256302 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date256302.subtract(years1, options), + 2564, 2, "M02", "add 1y to Feb", + "be", 2564); +TemporalHelpers.assertPlainYearMonth( + date256302.subtract(years4, options), + 2567, 2, "M02", "add 4y to Feb", + "be", 2567); + +TemporalHelpers.assertPlainYearMonth( + date256302.subtract(years1n, options), + 2562, 2, "M02", "subtract 1y from Feb", + "be", 2562); +TemporalHelpers.assertPlainYearMonth( + date256302.subtract(years4n, options), + 2559, 2, "M02", "subtract 4y from Feb", + "be", 2559); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months5 = new Temporal.Duration(0, -5); +const months11n = new Temporal.Duration(0, 11); +const years1months2 = new Temporal.Duration(-1, -2); +const years1months2n = new Temporal.Duration(1, 2); + +const date256301 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M01", calendar }, options); +const date256303 = Temporal.PlainYearMonth.from({ year: 2563, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date256301.subtract(months1, options), + 2563, 2, "M02", "add 1mo to Jan in leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M09", calendar }, options).subtract(months5), + 2565, 2, "M02", "add 5mo with result in the next year", + "be", 2565); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2562, monthCode: "M09", calendar }, options).subtract(months5), + 2563, 2, "M02", "add 5mo with result in the next leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M12", calendar }, options).subtract(years1months2), + 2566, 2, "M02", "add 1y 2mo with result in the next year", + "be", 2566); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2565, monthCode: "M12", calendar }, options).subtract(years1months2), + 2567, 2, "M02", "add 1y 2mo with result in the next leap year", + "be", 2567); + +TemporalHelpers.assertPlainYearMonth( + date256303.subtract(months1n, options), + 2563, 2, "M02", "subtract 1mo from Mar in leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + date256301.subtract(months11n, options), + 2562, 2, "M02", "subtract 11mo with result in the previous year", + "be", 2562); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M01", calendar }, options).subtract(months11n, options), + 2563, 2, "M02", "add 11mo with result in the previous leap year", + "be", 2563); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2565, monthCode: "M04", calendar }, options).subtract(years1months2n), + 2564, 2, "M02", "add 1y 2mo with result in the previous year", + "be", 2564); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2564, monthCode: "M04", calendar }, options).subtract(years1months2n), + 2563, 2, "M02", "add 1y 2mo with result in the previous leap year", + "be", 2563); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-coptic.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-coptic.js new file mode 100644 index 0000000000000..d542cd47636d3 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-coptic.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (coptic calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "coptic"; +const options = { overflow: "reject" }; + +const leapDay = Temporal.PlainYearMonth.from({ year: 1739, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1, options), + 1740, 13, "M13", "Adding 1 year to epagomenal month", + "am", 1740, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1n, options), + 1738, 13, "M13", "Subtracting 1 year from epagomenal month", + "am", 1738, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4, options), + 1743, 13, "M13", "Adding 4 years to epagomenal month", + "am", 1743, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4n, options), + 1735, 13, "M13", "Subtracting 4 years from epagomenal month", + "am", 1735, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethioaa.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethioaa.js new file mode 100644 index 0000000000000..1461e8f94f353 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethioaa.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (ethioaa calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "ethioaa"; +const options = { overflow: "reject" }; + +const leapDay = Temporal.PlainYearMonth.from({ year: 7515, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1, options), + 7516, 13, "M13", "Adding 1 year to epagomenal month", + "aa", 7516, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1n, options), + 7514, 13, "M13", "Subtracting 1 year from epagomenal month", + "aa", 7514, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4, options), + 7519, 13, "M13", "Adding 4 years to epagomenal month", + "aa", 7519, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4n, options), + 7511, 13, "M13", "Subtracting 4 years from epagomenal month", + "aa", 7511, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethiopic.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethiopic.js new file mode 100644 index 0000000000000..0477352923fcb --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethiopic.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (ethiopic calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "ethiopic"; +const options = { overflow: "reject" }; + +const leapDay = Temporal.PlainYearMonth.from({ year: 2015, monthCode: "M13", calendar }, options); + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1, options), + 2016, 13, "M13", "Adding 1 year to leap day", + "am", 2016, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1n, options), + 2014, 13, "M13", "Subtracting 1 year from leap day", + "am", 2014, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4, options), + 2019, 13, "M13", "Adding 4 years to leap day", + "am", 2019, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4n, options), + 2011, 13, "M13", "Subtracting 4 years from leap day", + "am", 2011, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-gregory.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-gregory.js new file mode 100644 index 0000000000000..a7cd7dfa46153 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-gregory.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (gregory calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "gregory"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +const date202002 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years1, options), + 2021, 2, "M02", "add 1y to leap day", + "ce", 2021); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years4, options), + 2024, 2, "M02", "add 4y to leap day", + "ce", 2024); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years1n, options), + 2019, 2, "M02", "subtract 1y from leap day", + "ce", 2019); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years4n, options), + 2016, 2, "M02", "subtract 4y from leap day", + "ce", 2016); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months5 = new Temporal.Duration(0, -5); +const months11n = new Temporal.Duration(0, 11); +const years1months2 = new Temporal.Duration(-1, -2); +const years1months2n = new Temporal.Duration(1, 2); + +const date202001 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M01", calendar }, options); +const date202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202001.subtract(months1, options), + 2020, 2, "M02", "add 1mo to in leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M09", calendar }, options).subtract(months5), + 2022, 2, "M02", "add 5mo with result in the next year", + "ce", 2022); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M09", calendar }, options).subtract(months5), + 2020, 2, "M02", "add 5mo with result in the next leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M12", calendar }, options).subtract(years1months2), + 2023, 2, "M02", "add 1y 2mo with result in the next year", + "ce", 2023); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M12", calendar }, options).subtract(years1months2), + 2024, 2, "M02", "add 1y 2mo with result in the next leap year", + "ce", 2024); + +TemporalHelpers.assertPlainYearMonth( + date202003.subtract(months1n, options), + 2020, 2, "M02", "subtract 1mo from Mar in leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + date202001.subtract(months11n), + 2019, 2, "M02", "subtract 11mo with result in the previous year", + "ce", 2019); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01", calendar }, options).subtract(months11n), + 2020, 2, "M02", "add 11mo with result in the previous leap year", + "ce", 2020); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M04", calendar }, options).subtract(years1months2n), + 2021, 2, "M02", "add 1y 2mo with result in the previous year", + "ce", 2021); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options).subtract(years1months2n), + 2020, 2, "M02", "add 1y 2mo with result in the previous leap year", + "ce", 2020); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-indian.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-indian.js new file mode 100644 index 0000000000000..d85d764a1c8a6 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-indian.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (indian calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "indian"; +const options = { overflow: "reject" }; + +const leapDay = Temporal.PlainYearMonth.from({ year: 1946, monthCode: "M01", calendar }, options); + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1, options), + 1947, 1, "M01", "Adding 1 year to Chaitra", + "shaka", 1947, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years1n, options), + 1945, 1, "M01", "Subtracting 1 year from Chaitra", + "shaka", 1945, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4, options), + 1950, 1, "M01", "Adding 4 years to Chaitra", + "shaka", 1950, null); + +TemporalHelpers.assertPlainYearMonth( + leapDay.subtract(years4n, options), + 1942, 1, "M01", "Subtracting 4 years from Chaitra", + "shaka", 1942, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-civil.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-civil.js new file mode 100644 index 0000000000000..5a3e5d340f87e --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-civil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (islamic-civil calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "islamic-civil"; +const options = { overflow: "reject" }; + +// Month 12 (Dhu al-Hijjah) has 29 days in common years and 30 in leap years. +// AH 1442, 1445, and 1447 are leap years. + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years2 = new Temporal.Duration(-2); +const years3n = new Temporal.Duration(3); + +const date144512 = Temporal.PlainYearMonth.from({ year: 1445, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years1, options), + 1446, 12, "M12", "add 1y in leap year", + "ah", 1446, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years2, options), + 1447, 12, "M12", "add 2y landing in next leap year", + "ah", 1447, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years1n, options), + 1444, 12, "M12", "subtract 1y in leap year", + "ah", 1444, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years3n, options), + 1442, 12, "M12", "subtract 3y landing in previous leap year", + "ah", 1442, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-tbla.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-tbla.js new file mode 100644 index 0000000000000..19b2975b6f6cd --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-tbla.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (islamic-tbla calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "islamic-tbla"; +const options = { overflow: "reject" }; + +// Month 12 (Dhu al-Hijjah) has 29 days in common years and 30 in leap years. +// AH 1442, 1445, and 1447 are leap years. + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years2 = new Temporal.Duration(-2); +const years3n = new Temporal.Duration(3); + +const date144512 = Temporal.PlainYearMonth.from({ year: 1445, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years1, options), + 1446, 12, "M12", "add 1y in leap year", + "ah", 1446, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years2, options), + 1447, 12, "M12", "add 2y landing in next leap year", + "ah", 1447, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years1n, options), + 1444, 12, "M12", "subtract 1y in leap year", + "ah", 1444, null); + +TemporalHelpers.assertPlainYearMonth( + date144512.subtract(years3n, options), + 1442, 12, "M12", "subtract 3y landing in previous leap year", + "ah", 1442, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-japanese.js new file mode 100644 index 0000000000000..00280b1dfefae --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-japanese.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (japanese calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "japanese"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +const date202002 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years1, options), + 2021, 2, "M02", "add 1y to Feb", + "reiwa", 3); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years4, options), + 2024, 2, "M02", "add 4y to Feb", + "reiwa", 6); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years1n, options), + 2019, 2, "M02", "subtract 1y from Feb", + "heisei", 31); + +TemporalHelpers.assertPlainYearMonth( + date202002.subtract(years4n, options), + 2016, 2, "M02", "subtract 4y from Feb", + "heisei", 28); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months5 = new Temporal.Duration(0, -5); +const months11n = new Temporal.Duration(0, 11); +const years1months2 = new Temporal.Duration(-1, -2); +const years1months2n = new Temporal.Duration(1, 2); + +const date202001 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M01", calendar }, options); +const date202003 = Temporal.PlainYearMonth.from({ year: 2020, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date202001.subtract(months1, options), + 2020, 2, "M02", "add 1mo to Jan", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M09", calendar }, options).subtract(months5), + 2022, 2, "M02", "add 5mo with result in the next year", + "reiwa", 4); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M09", calendar }, options).subtract(months5), + 2020, 2, "M02", "add 5mo with result in the next leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M12", calendar }, options).subtract(years1months2), + 2023, 2, "M02", "add 1y 2mo with result in the next year", + "reiwa", 5); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M12", calendar }, options).subtract(years1months2), + 2024, 2, "M02", "add 1y 2mo with result in the next leap year", + "reiwa", 6); + +TemporalHelpers.assertPlainYearMonth( + date202003.subtract(months1n, options), + 2020, 2, "M02", "subtract 1mo from Mar", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + date202001.subtract(months11n, options), + 2019, 2, "M02", "subtract 11mo with result in the previous year", + "heisei", 31); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01", calendar }, options).subtract(months11n, options), + 2020, 2, "M02", "add 11mo with result in the previous leap year", + "reiwa", 2); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2022, monthCode: "M04", calendar }, options).subtract(years1months2n, options), + 2021, 2, "M02", "add 1y 2mo with result in the previous year", + "reiwa", 3); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M04", calendar }, options).subtract(years1months2n, options), + 2020, 2, "M02", "add 1y 2mo with result in the previous leap year", + "reiwa", 2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-persian.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-persian.js new file mode 100644 index 0000000000000..4cdd1cb080ea6 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-persian.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (Persian calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "persian"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +const date136212 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M12", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date136212.subtract(years1, options), + 1363, 12, "M12", "add 1y in leap year", + "ap", 1363, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.subtract(years4, options), + 1366, 12, "M12", "add 4y in leap year", + "ap", 1366, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.subtract(years1n, options), + 1361, 12, "M12", "subtract 1y in leap year", + "ap", 1361, null); + +TemporalHelpers.assertPlainYearMonth( + date136212.subtract(years4n, options), + 1358, 12, "M12", "subtract 4y in leap year", + "ap", 1358, null); + +// Months + +const months1n = new Temporal.Duration(0, 1); +const months6 = new Temporal.Duration(0, -6); +const months11n = new Temporal.Duration(0, 11); +const years1months2 = new Temporal.Duration(-1, -2); +const years1months2n = new Temporal.Duration(1, 2); + +const date136206 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M06", calendar }, options); +const date136211 = Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M11", calendar }, options); +const date136301 = Temporal.PlainYearMonth.from({ year: 1363, monthCode: "M01", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date136206.subtract(months6, options), + 1362, 12, "M12", "add 6mo to Shahrivar in leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1362, monthCode: "M10", calendar }, options).subtract(years1months2, options), + 1363, 12, "M12", "add 1y 2mo with result in the next year", + "ap", 1363, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1361, monthCode: "M10", calendar }, options).subtract(years1months2), + 1362, 12, "M12", "add 1y 2mo with result in the next leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + date136301.subtract(months1n, options), + 1362, 12, "M12", "subtract 1mo from Farvardin in leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + date136211.subtract(months11n, options), + 1361, 12, "M12", "subtract 11mo with result in the previous year", + "ap", 1361, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1363, monthCode: "M11", calendar }, options).subtract(months11n, options), + 1362, 12, "M12", "subtract 11mo with result in the previous leap year", + "ap", 1362, null); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1364, monthCode: "M02", calendar }, options).subtract(years1months2n, options), + 1362, 12, "M12", "add 1y 2mo with result in the previous year", + "ap", 1362, null); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 1365, monthCode: "M02", calendar }, options).subtract(years1months2n, options), + 1363, 12, "M12", "add 1y 2mo with result in the previous leap year", + "ap", 1363, null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-roc.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-roc.js new file mode 100644 index 0000000000000..3231e5bbabfad --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-roc.js @@ -0,0 +1,102 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L., and the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Check various basic calculations involving leap years (roc calendar) +features: [Temporal, Intl.Era-monthcode] +includes: [temporalHelpers.js] +---*/ + +const calendar = "roc"; +const options = { overflow: "reject" }; + +// Years + +const years1 = new Temporal.Duration(-1); +const years1n = new Temporal.Duration(1); +const years4 = new Temporal.Duration(-4); +const years4n = new Temporal.Duration(4); + +const date10902 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M02", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date10902.subtract(years1), + 110, 2, "M02", "add 1y in leap year", + "roc", 110); + +TemporalHelpers.assertPlainYearMonth( + date10902.subtract(years4, options), + 113, 2, "M02", "add 4y in leap year", + "roc", 113); + +TemporalHelpers.assertPlainYearMonth( + date10902.subtract(years1n), + 108, 2, "M02", "subtract 1y in leap year", + "roc", 108); + +TemporalHelpers.assertPlainYearMonth( + date10902.subtract(years4n, options), + 105, 2, "M02", "subtract 4y in leap year", + "roc", 105); + +// Months + +const months1 = new Temporal.Duration(0, -1); +const months1n = new Temporal.Duration(0, 1); +const months5 = new Temporal.Duration(0, -5); +const months11n = new Temporal.Duration(0, 11); +const years1months2 = new Temporal.Duration(-1, -2); +const years1months2n = new Temporal.Duration(1, 2); + +const date10901 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M01", calendar }, options); +const date10903 = Temporal.PlainYearMonth.from({ year: 109, monthCode: "M03", calendar }, options); + +TemporalHelpers.assertPlainYearMonth( + date10901.subtract(months1, options), + 109, 2, "M02", "add 1mo to Jan in leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M09", calendar }, options).subtract(months5, options), + 111, 2, "M02", "add 5mo with result in the next year", + "roc", 111); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 108, monthCode: "M09", calendar }, options).subtract(months5, options), + 109, 2, "M02", "add 5mo with result in the next leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M12", calendar }, options).subtract(years1months2, options), + 112, 2, "M02", "add 1y 2mo with result in the next year", + "roc", 112); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 111, monthCode: "M12", calendar }, options).subtract(years1months2, options), + 113, 2, "M02", "add 1y 2mo with result in the next leap year", + "roc", 113); + +TemporalHelpers.assertPlainYearMonth( + date10903.subtract(months1n, options), + 109, 2, "M02", "subtract 1mo from Mar in leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + date10901.subtract(months11n, options), + 108, 2, "M02", "subtract 11mo with result in the previous year", + "roc", 108); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M01", calendar }, options).subtract(months11n, options), + 109, 2, "M02", "add 11mo with result in the previous leap year", + "roc", 109); + +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 111, monthCode: "M04", calendar }, options).subtract(years1months2n, options), + 110, 2, "M02", "add 1y 2mo with result in the previous year", + "roc", 110); +TemporalHelpers.assertPlainYearMonth( + Temporal.PlainYearMonth.from({ year: 110, monthCode: "M04", calendar }, options).subtract(years1months2n, options), + 109, 2, "M02", "add 1y 2mo with result in the previous leap year", + "roc", 109); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/subtract/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..27c11157fda19 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.tolocalestring +description: Using timeStyle, even if dateStyle is present, should throw +features: [Temporal] +---*/ + +const item = new Temporal.PlainYearMonth(2026, 1, "gregory", 1); + +assert.throws(TypeError, function() { + item.toLocaleString("en-u-ca-gregory", { dateStyle: "full", timeStyle: "full" }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toString/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toString/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toString/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/toString/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-japanese.js index bb88d72cb3f0a..e9a2cc44feb32 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-japanese.js @@ -14,7 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.PlainYearMonth.from({ era: "bce", eraYear: 1, monthCode: "M06", calendar }, options); const ce1 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1, monthCode: "M06", calendar }, options); -const meiji5 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 5, monthCode: "M01", calendar }, options); +const ce1872 = Temporal.PlainYearMonth.from({ era: "ce", eraYear: 1872, monthCode: "M12", calendar }, options); +const meiji6 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 6, monthCode: "M01", calendar}, options); +const meiji7 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 7, monthCode: "M01", calendar }, options); const meiji45 = Temporal.PlainYearMonth.from({ era: "meiji", eraYear: 45, monthCode: "M05", calendar }, options); const taisho1 = Temporal.PlainYearMonth.from({ era: "taisho", eraYear: 1, monthCode: "M08", calendar }, options); const taisho6 = Temporal.PlainYearMonth.from({ era: "taisho", eraYear: 6, monthCode: "M03", calendar }, options); @@ -82,16 +84,16 @@ const tests = [ [-63, 0, "-63y backwards from Taisho 6 March to Showa 55 March"], [0, -756, "-756mo backwards from Taisho 6 March to Showa 55 March"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [45, 2, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, "542mo from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [43, 2, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, "518mo from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [-45, -2, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [-43, -2, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -104,6 +106,17 @@ const tests = [ [0, -3, "-3mo backwards from Meiji 45 May to Taisho 1 August"], [0, -3, "-3mo backwards from Meiji 45 May to Taisho 1 August"], ], + // Last pre-solar-calendar CE month to first solar-calendar month of Meiji era + [ + ce1872, meiji6, + [0, 1, "from month before solar Meiji era to first month"], + [0, 1, "from month before solar Meiji era to first month"], + ], + [ + meiji6, ce1872, + [0, -1, "backwards from month before solar Meiji era to first month"], + [0, -1, "backwards from month before solar Meiji era to first month"], + ], // CE-BCE boundary [ bce1, ce1, diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/until/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/with/shell.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/with/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/with/shell.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/with/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/year/arithmetic-year.js b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/year/arithmetic-year.js index 2a29ddf2c0048..11b8e52b7156a 100644 --- a/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/year/arithmetic-year.js +++ b/js/src/tests/test262/intl402/Temporal/PlainYearMonth/prototype/year/arithmetic-year.js @@ -103,7 +103,7 @@ const tests = { [{ era: "bce", eraYear: 2, monthCode: "M06" }, -1], [{ era: "bce", eraYear: 1, monthCode: "M12" }, 0], [{ era: "ce", eraYear: 1, monthCode: "M07" }, 1], - [{ era: "meiji", eraYear: 1, monthCode: "M12" }, 1868], + [{ era: "meiji", eraYear: 6, monthCode: "M12" }, 1873], [{ era: "taisho", eraYear: 1, monthCode: "M12" }, 1912], [{ era: "showa", eraYear: 1, monthCode: "M12" }, 1926], [{ era: "heisei", eraYear: 1, monthCode: "M12" }, 1989], diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/canonicalize-calendar.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/canonicalize-calendar.js index bd0265256a526..e44f7b1600ceb 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/canonicalize-calendar.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/canonicalize-calendar.js @@ -1,5 +1,5 @@ // |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// Copyright (C) 2025 Igalia, S.L. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,7 +8,12 @@ description: Calendar ID is canonicalized features: [Temporal] ---*/ -const result = new Temporal.ZonedDateTime(1719923640_000_000_000n, "UTC", "islamicc"); +var result = new Temporal.ZonedDateTime(1719923640_000_000_000n, "UTC", "islamicc"); assert.sameValue(result.calendarId, "islamic-civil", "calendar ID is canonicalized"); +// May need to be removed in the future. +// See https://github.com/tc39/ecma402/issues/285 +result = new Temporal.ZonedDateTime(1719923640_000_000_000n, "UTC", "ethiopic-amete-alem"); +assert.sameValue(result.calendarId, "ethioaa", "calendar ID is canonicalized"); + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era-with-era-year.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era-with-era-year.js new file mode 100644 index 0000000000000..9a5962dd16f38 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era-with-era-year.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown if era is invalid for this calendar with year absent and eraYear present +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.ZonedDateTime.from({ month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era.js new file mode 100644 index 0000000000000..5705946a4092b --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/calendar-invalid-era.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown if era is invalid for this calendar +features: [Temporal] +---*/ + +const calendarsWithEras = [ + "buddhist", + "coptic", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +]; + +const calendarsWithoutEras = [ + "chinese", + "dangi", +]; + +calendarsWithEras.forEach((calendar) => { + // "xyz" is not a valid era in any supported calendar + assert.throws(RangeError, + () => Temporal.ZonedDateTime.from({ year: 2025, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", era: "xyz", eraYear: 2025, calendar }), + `xyz is not a valid era in calendar ${calendar}`); +}); + +calendarsWithoutEras.forEach((calendar) => { + // era is ignored + const result = Temporal.ZonedDateTime.from({ year: 2025, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", era: "xyz", eraYear: 2025, calendar }); + assert.sameValue(result instanceof Temporal.ZonedDateTime, true, `era should be ignored for calendar ${calendar}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js index 14c5abbe0864d..d7417332f2123 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js @@ -116,6 +116,24 @@ TemporalHelpers.assertPlainDateTime( 1868, 10, "M10", 22, 12, 34, 0, 0, 0, 0, "Meiji 1 resolves to CE 1868 before era start date", "ce", 1868); +const meiji1AfterStart = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +TemporalHelpers.assertPlainDateTime( + meiji1AfterStart.toPlainDateTime(), + 1868, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "Meiji 1 still resolves to CE 1868 after era start date", + "ce", 1868); + +const meiji5 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M12", day: 31, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +TemporalHelpers.assertPlainDateTime( + meiji5.toPlainDateTime(), + 1872, 12, "M12", 31, 12, 34, 0, 0, 0, 0, "Meiji 5 resolves to CE 1872", + "ce", 1872); + +const ce1873 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1873, monthCode: "M01", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +TemporalHelpers.assertPlainDateTime( + ce1873.toPlainDateTime(), + 1873, 1, "M01", 1, 12, 34, 0, 0, 0, 0, "CE 1873 resolves to Meiji 6", + "meiji", 6); + const meiji0 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 0, monthCode: "M10", day: 23, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertPlainDateTime( meiji0.toPlainDateTime(), @@ -128,12 +146,6 @@ TemporalHelpers.assertPlainDateTime( 1866, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "Meiji -1 resolves to CE 1866", "ce", 1866); -const ce1868AfterStart = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 23, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); -TemporalHelpers.assertPlainDateTime( - ce1868AfterStart.toPlainDateTime(), - 1868, 10, "M10", 23, 12, 34, 0, 0, 0, 0, "CE 1868 resolves to Meiji 1 after era start date", - "meiji", 1); - const ce0 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 0, monthCode: "M01", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertPlainDateTime( ce0.toPlainDateTime(), diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-japanese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-japanese.js index 6c08865b3df07..90a18063e238a 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/era-japanese.js @@ -16,7 +16,7 @@ const reiwa = Temporal.ZonedDateTime.from({ era: "reiwa", eraYear: 2, month: 1, const heisei = Temporal.ZonedDateTime.from({ era: "heisei", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); const showa = Temporal.ZonedDateTime.from({ era: "showa", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); const taisho = Temporal.ZonedDateTime.from({ era: "taisho", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); -const meiji = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 2, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); +const meiji = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 6, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); const ce = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1000, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); const bce = Temporal.ZonedDateTime.from({ era: "bce", eraYear: 1, month: 1, day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); @@ -28,7 +28,7 @@ TemporalHelpers.assertPlainDateTime(showa.toPlainDateTime(), 1927, 1, "M01", 1, TemporalHelpers.assertPlainDateTime(taisho.toPlainDateTime(), 1913, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${taisho}`, "taisho", 2); -TemporalHelpers.assertPlainDateTime(meiji.toPlainDateTime(), 1869, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${meiji}`, "meiji", 2); +TemporalHelpers.assertPlainDateTime(meiji.toPlainDateTime(), 1873, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${meiji}`, "meiji", 6); TemporalHelpers.assertPlainDateTime(ce.toPlainDateTime(), 1000, 1, "M01", 1, 12, 34, 0, 0, 0, 0, `${ce} (CE)`, "ce", 1000); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/invalid-month-codes-hebrew.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/invalid-month-codes-hebrew.js index 620ecef94531d..a9b6226c29d66 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/invalid-month-codes-hebrew.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/from/invalid-month-codes-hebrew.js @@ -10,10 +10,16 @@ features: [Temporal, Intl.Era-monthcode] const calendar = "hebrew"; +// 5779 is a leap year assert.throws(RangeError, () => { Temporal.ZonedDateTime.from({ year: 5779, monthCode: "M13", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); }, "M13 should not be a valid month code"); +// 5781 is a common year +assert.throws(RangeError, () => { + Temporal.ZonedDateTime.from({ year: 5781, monthCode: "M13", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }); +}, "M13 should not be a valid month code"); + // Invalid leap months: e.g. M02L for (var i = 1; i <= 12; i++) { if (i === 5) diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-africa.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-africa.js deleted file mode 100644 index 15e10077587d5..0000000000000 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-africa.js +++ /dev/null @@ -1,56 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 André Bargull. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.zoneddatetime -description: ZonedDateTime constructor accepts link names as time zone ID input -features: [Temporal] ----*/ - -const testCases = [ - "Africa/Accra", // Link Africa/Abidjan Africa/Accra # Ghana - "Africa/Bamako", // Link Africa/Abidjan Africa/Bamako # Mali - "Africa/Banjul", // Link Africa/Abidjan Africa/Banjul # The Gambia - "Africa/Conakry", // Link Africa/Abidjan Africa/Conakry # Guinea - "Africa/Dakar", // Link Africa/Abidjan Africa/Dakar # Senegal - "Africa/Freetown", // Link Africa/Abidjan Africa/Freetown # Sierra Leone - "Africa/Lome", // Link Africa/Abidjan Africa/Lome # Togo - "Africa/Nouakchott", // Link Africa/Abidjan Africa/Nouakchott # Mauritania - "Africa/Ouagadougou", // Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso - "Atlantic/St_Helena", // Link Africa/Abidjan Atlantic/St_Helena # St Helena - "Africa/Addis_Ababa", // Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia - "Africa/Asmara", // Link Africa/Nairobi Africa/Asmara # Eritrea - "Africa/Dar_es_Salaam", // Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania - "Africa/Djibouti", // Link Africa/Nairobi Africa/Djibouti - "Africa/Kampala", // Link Africa/Nairobi Africa/Kampala # Uganda - "Africa/Mogadishu", // Link Africa/Nairobi Africa/Mogadishu # Somalia - "Indian/Antananarivo", // Link Africa/Nairobi Indian/Antananarivo # Madagascar - "Indian/Comoro", // Link Africa/Nairobi Indian/Comoro - "Indian/Mayotte", // Link Africa/Nairobi Indian/Mayotte - "Africa/Blantyre", // Link Africa/Maputo Africa/Blantyre # Malawi - "Africa/Bujumbura", // Link Africa/Maputo Africa/Bujumbura # Burundi - "Africa/Gaborone", // Link Africa/Maputo Africa/Gaborone # Botswana - "Africa/Harare", // Link Africa/Maputo Africa/Harare # Zimbabwe - "Africa/Kigali", // Link Africa/Maputo Africa/Kigali # Rwanda - "Africa/Lubumbashi", // Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo - "Africa/Lusaka", // Link Africa/Maputo Africa/Lusaka # Zambia - "Africa/Bangui", // Link Africa/Lagos Africa/Bangui # Central African Republic - "Africa/Brazzaville", // Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo - "Africa/Douala", // Link Africa/Lagos Africa/Douala # Cameroon - "Africa/Kinshasa", // Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) - "Africa/Libreville", // Link Africa/Lagos Africa/Libreville # Gabon - "Africa/Luanda", // Link Africa/Lagos Africa/Luanda # Angola - "Africa/Malabo", // Link Africa/Lagos Africa/Malabo # Equatorial Guinea - "Africa/Niamey", // Link Africa/Lagos Africa/Niamey # Niger - "Africa/Porto-Novo", // Link Africa/Lagos Africa/Porto-Novo # Benin - "Africa/Maseru", // Link Africa/Johannesburg Africa/Maseru # Lesotho - "Africa/Mbabane", // Link Africa/Johannesburg Africa/Mbabane # Eswatini -]; - -for (let id of testCases) { - const instance = new Temporal.ZonedDateTime(0n, id); - assert.sameValue(instance.timeZoneId, id); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-asia.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-asia.js deleted file mode 100644 index a9bd238841059..0000000000000 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-asia.js +++ /dev/null @@ -1,27 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 André Bargull. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.zoneddatetime -description: ZonedDateTime constructor accepts link names as time zone ID input -features: [Temporal, canonical-tz] ----*/ - -const testCases = [ - "Europe/Nicosia", // Link Asia/Nicosia Europe/Nicosia - "Asia/Bahrain", // Link Asia/Qatar Asia/Bahrain - "Antarctica/Syowa", // Link Asia/Riyadh Antarctica/Syowa - "Asia/Aden", // Link Asia/Riyadh Asia/Aden # Yemen - "Asia/Kuwait", // Link Asia/Riyadh Asia/Kuwait - "Asia/Phnom_Penh", // Link Asia/Bangkok Asia/Phnom_Penh # Cambodia - "Asia/Vientiane", // Link Asia/Bangkok Asia/Vientiane # Laos - "Asia/Muscat", // Link Asia/Dubai Asia/Muscat # Oman -]; - -for (let id of testCases) { - const instance = new Temporal.ZonedDateTime(0n, id); - assert.sameValue(instance.timeZoneId, id); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-australasia.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-australasia.js deleted file mode 100644 index e543fe2ee7ed9..0000000000000 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-australasia.js +++ /dev/null @@ -1,23 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 André Bargull. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.zoneddatetime -description: ZonedDateTime constructor accepts link names as time zone ID input -features: [Temporal] ----*/ - -const testCases = [ - "Pacific/Saipan", // Link Pacific/Guam Pacific/Saipan # N Mariana Is - "Antarctica/McMurdo", // Link Pacific/Auckland Antarctica/McMurdo - "Antarctica/DumontDUrville", // Link Pacific/Port_Moresby Antarctica/DumontDUrville - "Pacific/Midway", // Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands -]; - -for (let id of testCases) { - const instance = new Temporal.ZonedDateTime(0n, id); - assert.sameValue(instance.timeZoneId, id); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-europe.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-europe.js deleted file mode 100644 index 66d40c1b56d08..0000000000000 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-europe.js +++ /dev/null @@ -1,35 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 André Bargull. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.zoneddatetime -description: ZonedDateTime constructor accepts link names as time zone ID input -features: [Temporal, canonical-tz] ----*/ - -const testCases = [ - "Europe/Jersey", // Link Europe/London Europe/Jersey - "Europe/Guernsey", // Link Europe/London Europe/Guernsey - "Europe/Isle_of_Man", // Link Europe/London Europe/Isle_of_Man - "Europe/Mariehamn", // Link Europe/Helsinki Europe/Mariehamn - "Europe/Busingen", // Link Europe/Zurich Europe/Busingen - "Europe/Vatican", // Link Europe/Rome Europe/Vatican - "Europe/San_Marino", // Link Europe/Rome Europe/San_Marino - "Europe/Vaduz", // Link Europe/Zurich Europe/Vaduz - "Arctic/Longyearbyen", // Link Europe/Oslo Arctic/Longyearbyen - "Europe/Ljubljana", // Link Europe/Belgrade Europe/Ljubljana # Slovenia - "Europe/Podgorica", // Link Europe/Belgrade Europe/Podgorica # Montenegro - "Europe/Sarajevo", // Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina - "Europe/Skopje", // Link Europe/Belgrade Europe/Skopje # North Macedonia - "Europe/Zagreb", // Link Europe/Belgrade Europe/Zagreb # Croatia - "Europe/Bratislava", // Link Europe/Prague Europe/Bratislava - "Asia/Istanbul", // Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. -]; - -for (let id of testCases) { - const instance = new Temporal.ZonedDateTime(0n, id); - assert.sameValue(instance.timeZoneId, id); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-northamerica.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-northamerica.js deleted file mode 100644 index 208a25e084571..0000000000000 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links-northamerica.js +++ /dev/null @@ -1,42 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2022 André Bargull. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-temporal.zoneddatetime -description: ZonedDateTime constructor accepts link names as time zone ID input -features: [Temporal, canonical-tz] ----*/ - -const testCases = [ - "America/Creston", // Link America/Phoenix America/Creston - "America/Nassau", // Link America/Toronto America/Nassau - "America/Atikokan", // Link America/Panama America/Atikokan - "America/Cayman", // Link America/Panama America/Cayman - "America/Anguilla", // Link America/Puerto_Rico America/Anguilla - "America/Antigua", // Link America/Puerto_Rico America/Antigua - "America/Aruba", // Link America/Puerto_Rico America/Aruba - "America/Curacao", // Link America/Puerto_Rico America/Curacao - "America/Blanc-Sablon", // Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) - "America/Dominica", // Link America/Puerto_Rico America/Dominica - "America/Grenada", // Link America/Puerto_Rico America/Grenada - "America/Guadeloupe", // Link America/Puerto_Rico America/Guadeloupe - "America/Kralendijk", // Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands - "America/Lower_Princes", // Link America/Puerto_Rico America/Lower_Princes # Sint Maarten - "America/Marigot", // Link America/Puerto_Rico America/Marigot # St Martin (French part) - "America/Montserrat", // Link America/Puerto_Rico America/Montserrat - "America/Port_of_Spain", // Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago - "America/St_Barthelemy", // Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy - "America/St_Kitts", // Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis - "America/St_Lucia", // Link America/Puerto_Rico America/St_Lucia - "America/St_Thomas", // Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) - "America/St_Vincent", // Link America/Puerto_Rico America/St_Vincent - "America/Tortola", // Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) -]; - -for (let id of testCases) { - const instance = new Temporal.ZonedDateTime(0n, id); - assert.sameValue(instance.timeZoneId, id); -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links.js new file mode 100644 index 0000000000000..e8950f792c9c1 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/links.js @@ -0,0 +1,337 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: ZonedDateTime constructor accepts link names as time zone ID input +features: [Temporal, canonical-tz] +---*/ + +const asiaTestCases = { + "Europe/Nicosia": "Asia/Nicosia", + "Asia/Ashkhabad": "Asia/Ashgabat", + "Asia/Calcutta": "Asia/Kolkata", + "Asia/Choibalsan": "Asia/Ulaanbaatar", + "Asia/Chongqing": "Asia/Shanghai", + "Asia/Chungking": "Asia/Shanghai", + "Asia/Dacca": "Asia/Dhaka", + "Asia/Harbin": "Asia/Shanghai", + "Asia/Istanbul": "Europe/Istanbul", + "Asia/Kashgar": "Asia/Urumqi", + "Asia/Katmandu": "Asia/Kathmandu", + "Asia/Macao": "Asia/Macau", + "Asia/Rangoon": "Asia/Yangon", + "Asia/Saigon": "Asia/Ho_Chi_Minh", + "Asia/Tel_Aviv": "Asia/Jerusalem", + "Asia/Thimbu": "Asia/Thimphu", + "Asia/Ujung_Pandang": "Asia/Makassar", + "Asia/Ulan_Bator": "Asia/Ulaanbaatar", +}; + +const asiaHistoricalTestCases = [ + "Antarctica/Syowa", // Link Asia/Riyadh Antarctica/Syowa + "Asia/Aden", // Link Asia/Riyadh Asia/Aden # Yemen + "Asia/Bahrain", // Link Asia/Qatar Asia/Bahrain + "Asia/Kuwait", // Link Asia/Riyadh Asia/Kuwait + "Asia/Phnom_Penh", // Link Asia/Bangkok Asia/Phnom_Penh # Cambodia + "Asia/Vientiane", // Link Asia/Bangkok Asia/Vientiane # Laos + "Asia/Muscat", // Link Asia/Dubai Asia/Muscat # Oman +]; + +const africaTestCases = { + "Africa/Asmera": "Africa/Asmara", + "Africa/Timbuktu": "Africa/Bamako", +}; + +const africaHistoricalTestCases = [ + "Africa/Accra", // Link Africa/Abidjan Africa/Accra # Ghana + "Africa/Bamako", // Link Africa/Abidjan Africa/Bamako # Mali + "Africa/Banjul", // Link Africa/Abidjan Africa/Banjul # The Gambia + "Africa/Conakry", // Link Africa/Abidjan Africa/Conakry # Guinea + "Africa/Dakar", // Link Africa/Abidjan Africa/Dakar # Senegal + "Africa/Freetown", // Link Africa/Abidjan Africa/Freetown # Sierra Leone + "Africa/Lome", // Link Africa/Abidjan Africa/Lome # Togo + "Africa/Nouakchott", // Link Africa/Abidjan Africa/Nouakchott # Mauritania + "Africa/Ouagadougou", // Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso + "Atlantic/St_Helena", // Link Africa/Abidjan Atlantic/St_Helena # St Helena + "Africa/Addis_Ababa", // Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia + "Africa/Asmara", // Link Africa/Nairobi Africa/Asmara # Eritrea + "Africa/Dar_es_Salaam", // Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania + "Africa/Djibouti", // Link Africa/Nairobi Africa/Djibouti + "Africa/Kampala", // Link Africa/Nairobi Africa/Kampala # Uganda + "Africa/Mogadishu", // Link Africa/Nairobi Africa/Mogadishu # Somalia + "Indian/Antananarivo", // Link Africa/Nairobi Indian/Antananarivo # Madagascar + "Indian/Comoro", // Link Africa/Nairobi Indian/Comoro + "Indian/Mayotte", // Link Africa/Nairobi Indian/Mayotte + "Africa/Blantyre", // Link Africa/Maputo Africa/Blantyre # Malawi + "Africa/Bujumbura", // Link Africa/Maputo Africa/Bujumbura # Burundi + "Africa/Gaborone", // Link Africa/Maputo Africa/Gaborone # Botswana + "Africa/Harare", // Link Africa/Maputo Africa/Harare # Zimbabwe + "Africa/Kigali", // Link Africa/Maputo Africa/Kigali # Rwanda + "Africa/Lubumbashi", // Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo + "Africa/Lusaka", // Link Africa/Maputo Africa/Lusaka # Zambia + "Africa/Bangui", // Link Africa/Lagos Africa/Bangui # Central African Republic + "Africa/Brazzaville", // Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo + "Africa/Douala", // Link Africa/Lagos Africa/Douala # Cameroon + "Africa/Kinshasa", // Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) + "Africa/Libreville", // Link Africa/Lagos Africa/Libreville # Gabon + "Africa/Luanda", // Link Africa/Lagos Africa/Luanda # Angola + "Africa/Malabo", // Link Africa/Lagos Africa/Malabo # Equatorial Guinea + "Africa/Niamey", // Link Africa/Lagos Africa/Niamey # Niger + "Africa/Porto-Novo", // Link Africa/Lagos Africa/Porto-Novo # Benin + "Africa/Maseru", // Link Africa/Johannesburg Africa/Maseru # Lesotho + "Africa/Mbabane", // Link Africa/Johannesburg Africa/Mbabane # Eswatini +]; + +const australasiaTestCases = { + "Antarctica/South_Pole": "Antarctica/McMurdo", + "Australia/ACT": "Australia/Sydney", + "Australia/Canberra": "Australia/Sydney", + "Australia/Currie": "Australia/Hobart", + "Australia/LHI": "Australia/Lord_Howe", + "Australia/NSW": "Australia/Sydney", + "Australia/North": "Australia/Darwin", + "Australia/Queensland": "Australia/Brisbane", + "Australia/South": "Australia/Adelaide", + "Australia/Tasmania": "Australia/Hobart", + "Australia/Victoria": "Australia/Melbourne", + "Australia/West": "Australia/Perth", + "Australia/Yancowinna": "Australia/Broken_Hill", + "Pacific/Enderbury": "Pacific/Kanton", + "Pacific/Johnston": "Pacific/Honolulu", + "Pacific/Ponape": "Pacific/Pohnpei", + "Pacific/Samoa": "Pacific/Pago_Pago", + "Pacific/Truk": "Pacific/Chuuk", + "Pacific/Yap": "Pacific/Chuuk", +}; + +const australasiaHistoricalTestCases = [ + "Pacific/Saipan", // Link Pacific/Guam Pacific/Saipan # N Mariana Is + "Antarctica/McMurdo", // Link Pacific/Auckland Antarctica/McMurdo + "Antarctica/DumontDUrville", // Link Pacific/Port_Moresby Antarctica/DumontDUrville + "Pacific/Midway", // Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands +]; + +const europeTestCases = { + "Europe/Belfast": "Europe/London", + "Europe/Kiev": "Europe/Kyiv", + "Europe/Nicosia": "Asia/Nicosia", + "Europe/Tiraspol": "Europe/Chisinau", + "Europe/Uzhgorod": "Europe/Kyiv", + "Europe/Zaporozhye": "Europe/Kyiv", +}; + +const europeHistoricalTestCases = [ + "Europe/Jersey", // Link Europe/London Europe/Jersey + "Europe/Guernsey", // Link Europe/London Europe/Guernsey + "Europe/Isle_of_Man", // Link Europe/London Europe/Isle_of_Man + "Europe/Mariehamn", // Link Europe/Helsinki Europe/Mariehamn + "Europe/Busingen", // Link Europe/Zurich Europe/Busingen + "Europe/Vatican", // Link Europe/Rome Europe/Vatican + "Europe/San_Marino", // Link Europe/Rome Europe/San_Marino + "Europe/Vaduz", // Link Europe/Zurich Europe/Vaduz + "Arctic/Longyearbyen", // Link Europe/Oslo Arctic/Longyearbyen + "Europe/Ljubljana", // Link Europe/Belgrade Europe/Ljubljana # Slovenia + "Europe/Podgorica", // Link Europe/Belgrade Europe/Podgorica # Montenegro + "Europe/Sarajevo", // Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina + "Europe/Skopje", // Link Europe/Belgrade Europe/Skopje # North Macedonia + "Europe/Zagreb", // Link Europe/Belgrade Europe/Zagreb # Croatia + "Europe/Bratislava", // Link Europe/Prague Europe/Bratislava + "Asia/Istanbul", // Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. +]; + +const northAmericaTestCases = { + "America/Argentina/ComodRivadavia": "America/Argentina/Catamarca", + "America/Atka": "America/Adak", + "America/Buenos_Aires": "America/Argentina/Buenos_Aires", + "America/Catamarca": "America/Argentina/Catamarca", + "America/Coral_Harbour": "America/Atikokan", + "America/Cordoba": "America/Argentina/Cordoba", + "America/Ensenada": "America/Tijuana", + "America/Fort_Wayne": "America/Indiana/Indianapolis", + "America/Godthab": "America/Nuuk", + "America/Indianapolis": "America/Indiana/Indianapolis", + "America/Jujuy": "America/Argentina/Jujuy", + "America/Knox_IN": "America/Indiana/Knox", + "America/Louisville": "America/Kentucky/Louisville", + "America/Mendoza": "America/Argentina/Mendoza", + "America/Montreal": "America/Toronto", + "America/Nipigon": "America/Toronto", + "America/Pangnirtung": "America/Iqaluit", + "America/Porto_Acre": "America/Rio_Branco", + "America/Rainy_River": "America/Winnipeg", + "America/Rosario": "America/Argentina/Cordoba", + "America/Santa_Isabel": "America/Tijuana", + "America/Shiprock": "America/Denver", + "America/Thunder_Bay": "America/Toronto", + "America/Virgin": "America/St_Thomas", + "America/Yellowknife": "America/Edmonton", + "US/Alaska": "America/Anchorage", + "US/Aleutian": "America/Adak", + "US/Arizona": "America/Phoenix", + "US/Central": "America/Chicago", + "US/East-Indiana": "America/Indiana/Indianapolis", + "US/Eastern": "America/New_York", + "US/Hawaii": "Pacific/Honolulu", + "US/Indiana-Starke": "America/Indiana/Knox", + "US/Michigan": "America/Detroit", + "US/Mountain": "America/Denver", + "US/Pacific": "America/Los_Angeles", + "US/Samoa": "Pacific/Pago_Pago", +}; + +const northAmericaHistoricalTestCases = [ + "America/Creston", // Link America/Phoenix America/Creston + "America/Nassau", // Link America/Toronto America/Nassau + "America/Atikokan", // Link America/Panama America/Atikokan + "America/Cayman", // Link America/Panama America/Cayman + "America/Anguilla", // Link America/Puerto_Rico America/Anguilla + "America/Antigua", // Link America/Puerto_Rico America/Antigua + "America/Aruba", // Link America/Puerto_Rico America/Aruba + "America/Curacao", // Link America/Puerto_Rico America/Curacao + "America/Blanc-Sablon", // Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) + "America/Dominica", // Link America/Puerto_Rico America/Dominica + "America/Grenada", // Link America/Puerto_Rico America/Grenada + "America/Guadeloupe", // Link America/Puerto_Rico America/Guadeloupe + "America/Kralendijk", // Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands + "America/Lower_Princes", // Link America/Puerto_Rico America/Lower_Princes # Sint Maarten + "America/Marigot", // Link America/Puerto_Rico America/Marigot # St Martin (French part) + "America/Montserrat", // Link America/Puerto_Rico America/Montserrat + "America/Port_of_Spain", // Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago + "America/St_Barthelemy", // Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy + "America/St_Kitts", // Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis + "America/St_Lucia", // Link America/Puerto_Rico America/St_Lucia + "America/St_Thomas", // Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) + "America/St_Vincent", // Link America/Puerto_Rico America/St_Vincent + "America/Tortola", // Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) +]; + +const otherTestCases = { + "Atlantic/Faeroe": "Atlantic/Faroe", + "Atlantic/Jan_Mayen": "Arctic/Longyearbyen", + "Brazil/Acre": "America/Rio_Branco", + "Brazil/DeNoronha": "America/Noronha", + "Brazil/East": "America/Sao_Paulo", + "Brazil/West": "America/Manaus", + "CET": "Europe/Brussels", + "CST6CDT": "America/Chicago", + "Canada/Atlantic": "America/Halifax", + "Canada/Central": "America/Winnipeg", + "Canada/Eastern": "America/Toronto", + "Canada/Mountain": "America/Edmonton", + "Canada/Newfoundland": "America/St_Johns", + "Canada/Pacific": "America/Vancouver", + "Canada/Saskatchewan": "America/Regina", + "Canada/Yukon": "America/Whitehorse", + "Chile/Continental": "America/Santiago", + "Chile/EasterIsland": "Pacific/Easter", + "Cuba": "America/Havana", + "EET": "Europe/Athens", + "EST": "America/Panama", + "EST5EDT": "America/New_York", + "Egypt": "Africa/Cairo", + "Eire": "Europe/Dublin", + "Etc/GMT": "UTC", + "Etc/GMT+0": "UTC", + "Etc/GMT-0": "UTC", + "Etc/GMT0": "UTC", + "Etc/Greenwich": "UTC", + "Etc/UCT": "UTC", + "Etc/UTC": "UTC", + "Etc/Universal": "UTC", + "Etc/Zulu": "UTC", + "GB": "Europe/London", + "GB-Eire": "Europe/London", + "GMT": "UTC", + "GMT+0": "UTC", + "GMT-0": "UTC", + "GMT0": "UTC", + "Greenwich": "UTC", + "HST": "Pacific/Honolulu", + "Hongkong": "Asia/Hong_Kong", + "Iceland": "Atlantic/Reykjavik", + "Iran": "Asia/Tehran", + "Israel": "Asia/Jerusalem", + "Jamaica": "America/Jamaica", + "Japan": "Asia/Tokyo", + "Kwajalein": "Pacific/Kwajalein", + "Libya": "Africa/Tripoli", + "MET": "Europe/Brussels", + "MST": "America/Phoenix", + "MST7MDT": "America/Denver", + "Mexico/BajaNorte": "America/Tijuana", + "Mexico/BajaSur": "America/Mazatlan", + "Mexico/General": "America/Mexico_City", + "NZ": "Pacific/Auckland", + "NZ-CHAT": "Pacific/Chatham", + "Navajo": "America/Denver", + "PRC": "Asia/Shanghai", + "PST8PDT": "America/Los_Angeles", + "Poland": "Europe/Warsaw", + "Portugal": "Europe/Lisbon", + "ROC": "Asia/Taipei", + "ROK": "Asia/Seoul", + "Singapore": "Asia/Singapore", + "Turkey": "Europe/Istanbul", + "UCT": "UTC", + "Universal": "UTC", + "W-SU": "Europe/Moscow", + "WET": "Europe/Lisbon", + "Zulu": "UTC", +}; + + +let epochNanoseconds = [ + new Temporal.PlainDate(1900, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(1950, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(1960, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(1970, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(1980, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(1990, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(2000, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(2010, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(2020, 1, 1).toZonedDateTime("UTC").epochNanoseconds, + new Temporal.PlainDate(2030, 1, 1).toZonedDateTime("UTC").epochNanoseconds, +]; + +for (const testCases of [asiaHistoricalTestCases, + africaHistoricalTestCases, + australasiaHistoricalTestCases, + europeHistoricalTestCases, + northAmericaHistoricalTestCases]) { + for (let link of testCases) { + const instanceLink = new Temporal.ZonedDateTime(0n, link); + assert.sameValue(instanceLink.timeZoneId, link, `creating ZonedDateTime for ${link}`); + } +} + +for (const testCases of [asiaTestCases, + africaTestCases, + australasiaTestCases, + europeTestCases, + northAmericaTestCases, + otherTestCases]) { + for (let [link, zone] of Object.entries(testCases)) { + const instanceLink = new Temporal.ZonedDateTime(0n, link); + assert.sameValue(instanceLink.timeZoneId, link, `creating ZonedDateTime for ${link}`); + + const instanceZone = new Temporal.ZonedDateTime(0n, zone); + assert.sameValue(instanceLink.equals(instanceZone), true, `link=${link}, zone=${zone}`); + + assert.sameValue( + instanceLink.offsetNanoseconds, + instanceZone.offsetNanoseconds, + `offsetNanoseconds; link=${link}, zone=${zone}`); + + for (let epochNs of epochNanoseconds) { + assert.sameValue( + new Temporal.ZonedDateTime(epochNs, link).offsetNanoseconds, + new Temporal.ZonedDateTime(epochNs, zone).offsetNanoseconds, + `link=${link}, zone=${zone}, epochNs=${epochNs}` + ); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/add/offset-before-1883.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/add/offset-before-1883.js new file mode 100644 index 0000000000000..2db76eed449d7 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/add/offset-before-1883.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Addition resulting in a pre-1883 date returns the correct offset +features: [Temporal] +---*/ + +const instance = Temporal.ZonedDateTime.from("2001-09-08T18:46:40-07:00[America/Vancouver]"); +const expectedOffset = "-08:12:28"; // Offset before introduction of standard time zones + +assert.sameValue(instance.add(Temporal.Duration.from("-P5432Y1837M")).offset, expectedOffset, `add -P5432Y1837M, offset should be ${expectedOffset}`); +assert.sameValue(instance.add(Temporal.Duration.from("-P5432Y1836M")).offset, expectedOffset, `add -P5432Y1836M, offset should be ${expectedOffset}`); +assert.sameValue(instance.add(Temporal.Duration.from("-P5432Y1835M")).offset, expectedOffset, `add -P5432Y1835M, offset should be ${expectedOffset}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/era-boundary-japanese.js index c54df3d5a9af8..d4e522216b947 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.ZonedDateTime.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const ce1 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); -const ce1868 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); -const meiji1 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, timeZone: "UTC", calendar}, options); -const meiji5 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +const ce1872 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +const meiji6 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar}, options); +const meiji7 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const meiji45 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const taisho1 = Temporal.ZonedDateTime.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const taisho6 = Temporal.ZonedDateTime.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], [0, 7, 0, 10, "7mo 10d from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], [0, 2, 0, 21, "2mo 21d from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js index e0b7fe948666e..475b049abb26b 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -4, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2025, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js index 6b541c0a926bc..7fdb31366d9a9 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), 0, -3, 0, -29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "months" }), + start1.since(end, { largestUnit: "months" }), 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "years" }), + start1.since(end, { largestUnit: "years" }), -2, -4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "months" }), + start2.since(end, { largestUnit: "months" }), 0, -28, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).since(end, { largestUnit: "years" }), + start2.since(end, { largestUnit: "years" }), -2, -3, 0, -29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 1987, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 1979, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 1979, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -98, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -7, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 1956, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 1955, monthCode: "M03L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 1955, monthCode: "M03L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..e318ed115b152 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M08", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M07", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M07", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), + 0, 0, 0, -29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M12", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M09", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).since(end, { largestUnit }), + 0, -2, 0, -29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5786, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M11", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M11", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -2, -5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -29, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + -2, -4, 0, -29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M03", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -11, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 5785, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M05L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M05L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "months" }), + 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.since(end, { largestUnit: "years" }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "months" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.since(end, { largestUnit: "years" }), + 0, -12, 0, -29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/subtract/offset-before-1883.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/subtract/offset-before-1883.js new file mode 100644 index 0000000000000..e627cb0a15615 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/subtract/offset-before-1883.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Subtraction resulting in a pre-1883 date returns the correct offset +features: [Temporal] +---*/ + +const instance = Temporal.ZonedDateTime.from("2001-09-08T18:46:40-07:00[America/Vancouver]"); +const expectedOffset = "-08:12:28"; // Offset before introduction of standard time zones + +assert.sameValue(instance.subtract(Temporal.Duration.from("P5432Y1837M")).offset, expectedOffset, `subtract -P5432Y1837M, offset should be ${expectedOffset}`); +assert.sameValue(instance.subtract(Temporal.Duration.from("P5432Y1836M")).offset, expectedOffset, `subtract -P5432Y1836M, offset should be ${expectedOffset}`); +assert.sameValue(instance.subtract(Temporal.Duration.from("P5432Y1835M")).offset, expectedOffset, `subtract -P5432Y1835M, offset should be ${expectedOffset}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/datestyle-and-timestyle.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/datestyle-and-timestyle.js new file mode 100644 index 0000000000000..dad3ddfd33a0b --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/datestyle-and-timestyle.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Using both dateStyle and timeStyle should not throw +features: [Temporal] +---*/ + +const item = new Temporal.ZonedDateTime(0n, "UTC"); +var result = item.toLocaleString("en", { dateStyle: "full", timeStyle: "full" }); +assert.sameValue(result.includes(":00"), true, "using both dateStyle and timeStyle should not throw"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/hourcycle.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/hourcycle.js new file mode 100644 index 0000000000000..d92ec00ec0978 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/hourcycle.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Hour cycle should be correctly set when defaults are used +features: [Temporal] +---*/ + +const output0 = "00:00:00"; +const output11 = "0:00:00"; +const output12 = "12:00:00"; +const output24 = "24:00:00"; + +const item = new Temporal.ZonedDateTime(0n, "UTC"); +var result = item.toLocaleString("en", { hour12: false }); +assert.sameValue(result.includes(output0), true, `output for hour12: false should include ${output0}`); + +result = item.toLocaleString("en", { hour12: true }); +assert.sameValue(result.includes(output12), true, `output for hour12: true should include ${output12}`); + +result = item.toLocaleString("en", { hourCycle: "h23" }); +assert.sameValue(result.includes(output0), true, `output for hourCycle: h23 should include ${output0}`); + +result = item.toLocaleString("en", { hourCycle: "h24" }); +assert.sameValue(result.includes(output24), true, `output for hourCycle: h24 should include ${output24}`); + +result = item.toLocaleString("en", { hourCycle: "h11" }); +assert.sameValue(result.includes(output11), true, `output for hourCycle: h11 should include ${output11}`); + +result = item.toLocaleString("en", { hourCycle: "h12" }); +assert.sameValue(result.includes(output12), true, `output for hourCycle: h12 should include ${output12}`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/offset-time-zones.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/offset-time-zones.js new file mode 100644 index 0000000000000..defc623bcd0a7 --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toLocaleString/offset-time-zones.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Tests that a ZonedDateTime with an offset time zone can be formatted with toLocaleString +locale: [en] +features: [Temporal] +---*/ + +var zdt = new Temporal.ZonedDateTime(0n, "+00:00"); +var result = zdt.toLocaleString("en"); +assert(result.includes("GMT") && !result.includes("+") && !result.includes("-")); + +zdt = new Temporal.ZonedDateTime(0n, "+01:00"); +result = zdt.toLocaleString("en"); +assert(result.includes("GMT+1")); + +zdt = new Temporal.ZonedDateTime(0n, "-01:00"); +result = zdt.toLocaleString("en"); +assert(result.includes("GMT-1")); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toString/timezone.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toString/timezone.js new file mode 100644 index 0000000000000..29fa8be1f55ec --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/toString/timezone.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2026 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + toString returns correct output for all time zone identifiers from + Intl.supportedValuesOf. +features: [Temporal, Intl-enumeration] +---*/ + +function timeZoneId(zdt) { + let str = zdt.toString(); + let m = str.match(/(?<=\[)[\w\/_+-]+(?=\])/); + assert.sameValue(m !== null, true, str); + return m[0]; +} + +for (let id of Intl.supportedValuesOf("timeZone")) { + let instance = new Temporal.ZonedDateTime(0n, id); + + assert.sameValue(timeZoneId(instance), id); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/era-boundary-japanese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/era-boundary-japanese.js index c3dad3d6c5140..253701fd40593 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/era-boundary-japanese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/era-boundary-japanese.js @@ -14,9 +14,9 @@ const options = { overflow: "reject" }; const bce1 = Temporal.ZonedDateTime.from({ era: "bce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const ce1 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1, monthCode: "M06", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); -const ce1868 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1868, monthCode: "M10", day: 22, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); -const meiji1 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 1, monthCode: "M10", day: 23, hour: 12, minute: 34, timeZone: "UTC", calendar}, options); -const meiji5 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 5, monthCode: "M01", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +const ce1872 = Temporal.ZonedDateTime.from({ era: "ce", eraYear: 1872, monthCode: "M12", day: 31, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); +const meiji6 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 6, monthCode: "M01", day: 1, hour: 12, minute: 34, timeZone: "UTC", calendar}, options); +const meiji7 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 7, monthCode: "M01", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const meiji45 = Temporal.ZonedDateTime.from({ era: "meiji", eraYear: 45, monthCode: "M05", day: 19, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const taisho1 = Temporal.ZonedDateTime.from({ era: "taisho", eraYear: 1, monthCode: "M08", day: 9, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); const taisho6 = Temporal.ZonedDateTime.from({ era: "taisho", eraYear: 6, monthCode: "M03", day: 15, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); @@ -97,18 +97,16 @@ const tests = [ [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], [0, -7, 0, -10, "-7mo -10d backwards from Taisho 15 July 20 to Showa 1 December 30"], ], - // From Meiji 5 (1872) to Taisho 6 (1917) - crossing era boundary - // Note that contemporarily January 15 1872 would have been Meiji 4 in the - // pre-1873 lunisolar calendar, but the spec-mandated behaviour is proleptic + // From Meiji 7 (1874) to Taisho 6 (1917) - crossing era boundary [ - meiji5, taisho6, - [45, 2, 0, 0, "45y 2mo from Meiji 5 January to Taisho 6 March"], - [0, 542, 0, 0, "542mo from Meiji 5 January to Taisho 6 March"], + meiji7, taisho6, + [43, 2, 0, 0, "43y 2mo from Meiji 7 January to Taisho 6 March"], + [0, 518, 0, 0, "518mo from Meiji 7 January to Taisho 6 March"], ], [ - taisho6, meiji5, - [-45, -2, 0, 0, "-45y -2mo backwards from Meiji 5 January to Taisho 6 March"], - [0, -542, 0, 0, "-542mo backwards from Meiji 5 January to Taisho 6 March"], + taisho6, meiji7, + [-43, -2, 0, 0, "-43y -2mo backwards from Meiji 7 January to Taisho 6 March"], + [0, -518, 0, 0, "-518mo backwards from Meiji 7 January to Taisho 6 March"], ], // Within same year but different eras [ @@ -121,16 +119,16 @@ const tests = [ [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], [0, -2, 0, -21, "-2mo -21d backwards from Meiji 45 May 19 to Taisho 1 August 9"], ], - // Last pre-Meiji day to first day of Meiji era + // Last pre-solar-calendar CE day to first solar-calendar day of Meiji era [ - ce1868, meiji1, - [0, 0, 0, 1, "from day before Meiji era to first day"], - [0, 0, 0, 1, "from day before Meiji era to first day"], + ce1872, meiji6, + [0, 0, 0, 1, "from day before solar Meiji era to first day"], + [0, 0, 0, 1, "from day before solar Meiji era to first day"], ], [ - meiji1, ce1868, - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], - [0, 0, 0, -1, "backwards from day before Meiji era to first day"], + meiji6, ce1872, + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], + [0, 0, 0, -1, "backwards from day before solar Meiji era to first day"], ], // CE-BCE boundary [ diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js index 1172096e85b9d..82223ae7f2310 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "chinese"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "chinese"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "chinese"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M09-29 (29d) is 5 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 4, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M09-29 (29d) is 4 months 29 days, not 5 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "chinese"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2025, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2025-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2025-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2017, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2017-M06L-29 to 2018-M06-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2017-M06L-30 to 2018-M06-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js index 12e6ea7d556f5..83790876c5fdc 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js @@ -10,18 +10,19 @@ features: [Temporal, Intl.Era-monthcode] ---*/ const calendar = "dangi"; +const options = { overflow: "reject" }; // Difference between end of 30-day month to end of following 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M06-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M06-29 is 29 days, not one month (${largestUnit})` ); @@ -30,15 +31,15 @@ const calendar = "dangi"; // Difference between end of 30-day M04 to end of 29-day M04L { - const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, `M04-29 (30d) to M04L-29 (29d) is one month (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2020, monthCode: "M04", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, `M04-30 to M04L-29 (29d) is 29 days, not one month (${largestUnit})` ); @@ -48,15 +49,15 @@ const calendar = "dangi"; // Difference between end of 30-day month to end of not-immediately-following // 29-day month { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); for (const largestUnit of ["years", "months"]) { TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, `M05-29 (30d) to M09-29 (29d) is 4 months (${largestUnit})` ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit }), + Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), 0, 3, 0, 29, 0, 0, 0, 0, 0, 0, `M05-30 to M09-29 (29d) is 3 months 29 days, not 4 months (${largestUnit})` ); @@ -66,27 +67,112 @@ const calendar = "dangi"; // Difference between end of 30-day month in one year to 29-day month in later // year { - const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }); + const end = Temporal.ZonedDateTime.from({ year: 2023, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "months" }), + start1.until(end, { largestUnit: "months" }), 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 29 days" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "years" }), + start1.until(end, { largestUnit: "years" }), 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, "2021-M05-29 (30d) to 2023-M09-29 (29d) is 2 years, 4 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "months" }), + start2.until(end, { largestUnit: "months" }), 0, 28, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 28 months, 29 days, not 29 months" ); TemporalHelpers.assertDuration( - Temporal.ZonedDateTime.from({ year: 2021, monthCode: "M05", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }).until(end, { largestUnit: "years" }), + start2.until(end, { largestUnit: "years" }), 2, 3, 0, 29, 0, 0, 0, 0, 0, 0, "2021-M05-30 to 2023-M09-29 (29d) is 2 years, 3 months, 29 days, not 2 years 4 months" ); } +// Difference between end of 30-day common month and end of the same month with +// 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 2019, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 2018, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "2018-M02-29 to 2019-M02-29 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "2018-M02-30 to 2019-M02-29 is 11 months 29 days, not 1 year" + ); +} + +// Difference between end of 30-day leap month and end of the same leap month +// with 29 days in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 1987, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 1979, monthCode: "M06L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 1979, monthCode: "M06L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 99 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "1979-M06L-29 to 1987-M06L-29 is 8 years" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 98, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 98 months 29 days, not 98 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 7, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "1979-M06L-30 to 1987-M06L-29 is 7 years 12 months 29 days, not 8 years" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 1956, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 1955, monthCode: "M03L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 1955, monthCode: "M03L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "1955-M03L-29 to 1956-M03-29 is 12 months, not 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "1955-M03L-30 to 1956-M03-29 is 11 months 29 days, not 1 year" + ); +} + reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js new file mode 100644 index 0000000000000..22dcb1eb6480d --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests balancing of days to months at end of month (Hebrew calendar) +includes: [temporalHelpers.js] +features: [Temporal, Intl.Era-monthcode] +---*/ + +const calendar = "hebrew"; +const options = { overflow: "reject" }; + +// 5784 is a leap year. + +// 30-day months: 01, 05, 05L, 07, 09, 11 +// 29-day months: 04, 06, 08, 10, 12 +// +// Cheshvan and Kislev (02, 03) have 29 or 30 days, independent of leap years. +// Deficient - Cheshvan and Kislev have 29 days +// Regular - Cheshvan has 29 days, Kislev 30 +// Complete - Cheshvan and Kislev have 30 days +// +// Some recent years of each type: +// 5778 - regular common year +// 5779 - complete leap year +// 5781 - deficient common year +// 5782 - regular leap year +// 5783 - complete common year +// 5784 - deficient leap year + +// Difference between end of longer month to end of following shorter month +{ + const end = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M08", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M07", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + `Nisan 29th to Iyar 29th is one month (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M07", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), + 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, + `Nisan 30th to Iyar 29th is 29 days, not one month (${largestUnit})` + ); + } +} + +// Difference between end of longer month to end of not-immediately-following +// shorter month +{ + const end = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M12", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + for (const largestUnit of ["years", "months"]) { + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M09", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, + `Sivan 29th to Elul 29th is 3 months (${largestUnit})` + ); + TemporalHelpers.assertDuration( + Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M09", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options).until(end, { largestUnit }), + 0, 2, 0, 29, 0, 0, 0, 0, 0, 0, + `Sivan 30th to Elul 29th is 2 months 29 days, not 3 months (${largestUnit})` + ); + } +} + +// Difference between end of longer month in one year to shorter month in +// later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5786, monthCode: "M04", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M11", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M11", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 30 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, + "Av 29th 5783 to Tevet 29th 5786 is 2 years, 5 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 29, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 29 months, 29 days, not 30 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 2, 4, 0, 29, 0, 0, 0, 0, 0, 0, + "Av 30th 5783 to Tevet 29th 5786 is 2 years, 4 months, 29 days, not 2 years 5 months" + ); +} + +// Difference between 30 Kislev and day 29 of 29-day Kislev in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M02", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M02", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Kislev 5783 to 29th Kislev 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Kislev 5783 to 29th Kislev 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Difference between 30 Cheshvan and day 29 of 29-day Cheshvan in a later year +{ + const end = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M03", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5783, monthCode: "M03", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 12 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 12 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 11, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Cheshvan 5783 to 29th Cheshvan 5784 (deficient year) is 11 months 29 days, not 1 year" + ); +} + +// Case where both the month and day are not constrained +{ + const end = Temporal.ZonedDateTime.from({ year: 5785, monthCode: "M06", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start1 = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M05L", day: 29, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + const start2 = Temporal.ZonedDateTime.from({ year: 5784, monthCode: "M05L", day: 30, hour: 12, minute: 34, timeZone: "UTC", calendar }, options); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "months" }), + 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 13 months" + ); + TemporalHelpers.assertDuration( + start1.until(end, { largestUnit: "years" }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "29th Adar I 5784 to 29th Adar 5785 is 1 year" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "months" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 13 months" + ); + TemporalHelpers.assertDuration( + start2.until(end, { largestUnit: "years" }), + 0, 12, 0, 29, 0, 0, 0, 0, 0, 0, + "30th Adar I 5784 to 29th Adar 5785 is 12 months 29 days, not 1 year" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js new file mode 100644 index 0000000000000..ad81da6327c5c --- /dev/null +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/withCalendar/calendar-time-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: A time string is valid input for Calendar +features: [Temporal] +---*/ + +const instance = Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, hour: 12, minute: 34, timeZone: "UTC"}); + +const calendars = [ + "buddhist", + "chinese", + "coptic", + "dangi", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic-civil", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", +] + +calendars.forEach((cal) => { + const str = `T11:30[u-ca=${cal}]`; + const result = instance.withCalendar(str); + assert.sameValue(result.calendarId, cal, `Calendar created from string "${str}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/year/arithmetic-year.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/year/arithmetic-year.js index 5cc4bf3de918b..82aa96bbafa93 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/year/arithmetic-year.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/prototype/year/arithmetic-year.js @@ -103,7 +103,7 @@ const tests = { [{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1], [{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0], [{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1], - [{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868], + [{ era: "meiji", eraYear: 6, monthCode: "M12", day: 31 }, 1873], [{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912], [{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926], [{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989], diff --git a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/shell.js index 73e5d07407594..8d4cef92ca9ea 100644 --- a/js/src/tests/test262/intl402/Temporal/ZonedDateTime/shell.js +++ b/js/src/tests/test262/intl402/Temporal/ZonedDateTime/shell.js @@ -263,6 +263,25 @@ var TemporalHelpers = { assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); }, + /* + * assertPlainDatesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDates are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDatesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDate, `${prefix}expected value should be a Temporal.PlainDate`); + assert(actual instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.calendarId, + expected.calendarId, + `${prefix}calendar same value:` + ); + }, + /* * assertPlainDateTimesEqual(actual, expected[, description]): * diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-get.js new file mode 100644 index 0000000000000..4dd24d1dd6468 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/then-exported.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of 'then' when it is an exported name, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep-then_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "then"; + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-property-define.js new file mode 100644 index 0000000000000..ffb13b6a4464f --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-exported-then-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/then-exported.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of 'then' when it is an exported name, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep-then_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "then"; + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-get.js new file mode 100644 index 0000000000000..a1bf1a99d7f3e --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/then-not-exported.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of 'then' when it is not an exported name, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "then"; + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-property-define.js new file mode 100644 index 0000000000000..89a3b7d8dbc79 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-not-exported-then-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/then-not-exported.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of 'then' when it is not an exported name, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "then"; + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-exported.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-exported.js new file mode 100644 index 0000000000000..4c8ccbdbc86ae --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-exported.js @@ -0,0 +1,37 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-set-exported.case +// - src/import-defer/ignore/ignore.template +/*--- +description: _ [[Set]] exported called as super access (does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + [[Set]] ( P, V, Receiver ) + 1. return false. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +class A { constructor() { return ns; } }; +class B extends A { + constructor() { + super(); + super.exported = 14; + } +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-not-exported.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-not-exported.js new file mode 100644 index 0000000000000..bf311498096c5 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-super-property-set-not-exported.js @@ -0,0 +1,37 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-set-not-exported.case +// - src/import-defer/ignore/ignore.template +/*--- +description: _ [[Set]] not exported called as super access (does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + [[Set]] ( P, V, Receiver ) + 1. return false. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +class A { constructor() { return ns; } }; +class B extends A { + constructor() { + super(); + super.notExported = 14; + } +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-get.js new file mode 100644 index 0000000000000..f2e93adcfdda7 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/symbol-other.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of a symbol that is not a property of the namespace object, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = Symbol(); + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-property-define.js new file mode 100644 index 0000000000000..7556d28805040 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-other-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/symbol-other.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of a symbol that is not a property of the namespace object, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = Symbol(); + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-get.js new file mode 100644 index 0000000000000..61e8a21e44da6 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/symbol-toStringTag.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of Symbol.toStringTag, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = Symbol.toStringTag; + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-property-define.js new file mode 100644 index 0000000000000..2e60c11996c2c --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/ignore-symbol-toStringTag-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/symbol-toStringTag.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of Symbol.toStringTag, does not trigger execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = Symbol.toStringTag; + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert.sameValue(globalThis.evaluations.length, 0, "It does not trigger evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-get.js new file mode 100644 index 0000000000000..ede1faf9dd516 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/string-exported.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of a string that is an exported name, triggers execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "exported"; + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert(globalThis.evaluations.length > 0, "It triggers evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-property-define.js new file mode 100644 index 0000000000000..98a2b032ee95f --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-exported-string-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/string-exported.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of a string that is an exported name, triggers execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "exported"; + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert(globalThis.evaluations.length > 0, "It triggers evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-get.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-get.js new file mode 100644 index 0000000000000..f65fdcd4ebf74 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-get.js @@ -0,0 +1,51 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-get.case +// - src/import-defer/trigger-on-possible-export/string-not-exported.template +/*--- +description: _ [[Get]] when namespace is aliased by super (of a string that is not an exported name, triggers execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[Get]] ( _P_, _Receiver_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryGet(_O_, _P_, _Receiver_). + 1. Let _exports_ be ? GetModuleExportsList(_O_). + 1. ... + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "notExported"; + +let obj = { + superGet(key) { + return super[key]; + } +} + +Object.setPrototypeOf(obj, ns); +obj.superGet(key); + +assert(globalThis.evaluations.length > 0, "It triggers evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-property-define.js b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-property-define.js new file mode 100644 index 0000000000000..c27e1ec880129 --- /dev/null +++ b/js/src/tests/test262/language/import/import-defer/evaluation-triggers/trigger-not-exported-string-super-property-define.js @@ -0,0 +1,63 @@ +// |reftest| skip module -- import-defer is not supported +// This file was procedurally generated from the following sources: +// - src/import-defer/super-property-define.case +// - src/import-defer/trigger-on-possible-export/string-not-exported.template +/*--- +description: _ [[DefineOwnProperty]] called from class field definition (of a string that is not an exported name, triggers execution) +esid: sec-module-namespace-exotic-objects +features: [import-defer] +flags: [generated, module] +info: | + IsSymbolLikeNamespaceKey ( _P_, _O_ ) + 1. If _P_ is a Symbol, return *true*. + 1. If _ns_.[[Deferred]] is *true* and _P_ is "then", return *true*. + 1. Return *false*. + + GetModuleExportsList ( _O_ ) + 1. If _O_.[[Deferred]] is *true*, then + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception. + 1. Perform ? EvaluateSync(_m_). + 1. Return _O_.[[Exports]]. + + + [[DefineOwnProperty]] ( _P_, _Desc_ ) + 1. If IsSymbolLikeNamespaceKey(_P_, _O_), return ! OrdinaryDefineOwnProperty(_O_, _Desc_). + 1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). + 1. NOTE: If _O_.[[Deferred]] is *true*, the step above will ensure that the module is evaluated. + 1. ... + + DefineField ( receiver, fieldRecord ) + 1. Let fieldName be fieldRecord.[[Name]]. + 1. Let initializer be fieldRecord.[[Initializer]]. + 1. ... + 1. If fieldName is a Private Name, then + a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue). + 1. Else, + 1. Assert: fieldName is a property key. + a. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). + 1. Return unused. + +---*/ + + +import "./setup_FIXTURE.js"; + +import defer * as ns from "./dep_FIXTURE.js"; + +assert.sameValue(globalThis.evaluations.length, 0, "import defer does not trigger evaluation"); + +var key = "notExported"; + +class A { constructor() { return ns; } }; +class B extends A { + [key] = 10; +}; + +try { + new B(); +} catch (_) {} + +assert(globalThis.evaluations.length > 0, "It triggers evaluation"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-getter.js b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-getter.js new file mode 100644 index 0000000000000..ab1fd4a5962a1 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-getter.js @@ -0,0 +1,90 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.asyncDispose] getter +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + get [Symbol.asyncDispose]() { + return async function () { + this.disposed = true; + }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.js b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.js new file mode 100644 index 0000000000000..b3e600051a228 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.js @@ -0,0 +1,55 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + assert.sameValue(this, resource); + } + }; + + { + await using _ = resource; + } +}); diff --git a/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-not-async.js b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-not-async.js new file mode 100644 index 0000000000000..0cd011bea7631 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/Symbol.asyncDispose-method-not-async.js @@ -0,0 +1,59 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Disposal succeeds even if [Symbol.disposeAsync] does not return a Promise. +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/Symbol.dispose-getter.js b/js/src/tests/test262/language/statements/await-using/Symbol.dispose-getter.js new file mode 100644 index 0000000000000..d9f82e6e426ad --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/Symbol.dispose-getter.js @@ -0,0 +1,90 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.dispose] getter +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.js b/js/src/tests/test262/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.js new file mode 100644 index 0000000000000..408db309db9d3 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.js @@ -0,0 +1,55 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var resource = { + disposed: false, + [Symbol.dispose]() { + assert.sameValue(this, resource); + } + }; + + { + await using _ = resource; + } +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js b/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js new file mode 100644 index 0000000000000..78ed4fcaffc77 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js @@ -0,0 +1,48 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + `await using` allows a non-Promise return value from `[Symbol.asyncDispose]()` +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + [Symbol.asyncDispose]() { + } + }; + + { + await using _ = resource; + } +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js b/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js new file mode 100644 index 0000000000000..601d1154cc334 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js @@ -0,0 +1,53 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + `await using` allows non-native `Promise`-like return value from `[Symbol.asyncDispose]()` +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + [Symbol.asyncDispose]() { + return { + then(resolve) { + resolve(); + } + }; + } + }; + + { + await using _ = resource; + } +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-allows-null-initializer.js b/js/src/tests/test262/language/statements/await-using/await-using-allows-null-initializer.js new file mode 100644 index 0000000000000..b74f6db39789e --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-allows-null-initializer.js @@ -0,0 +1,47 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows null in initializer of 'await using' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + ... + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await using x = null; +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-allows-undefined-initializer.js b/js/src/tests/test262/language/statements/await-using/await-using-allows-undefined-initializer.js new file mode 100644 index 0000000000000..dad0c49eccb98 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-allows-undefined-initializer.js @@ -0,0 +1,47 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows undefined in initializer of 'await using' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + ... + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + using x = undefined; +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js b/js/src/tests/test262/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js new file mode 100644 index 0000000000000..3167d5769d030 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js @@ -0,0 +1,103 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: An 'await using' does not imply an Await occurs if the statement is not evaluated +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + ... + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var BREAK_EARLY = true; + var isRunningInSameMicrotask = true; + var wasStartedInSameMicrotask = false; + var didEvaluatePrecedingBlockStatementsInSameMicrotask = false; + var wasRunningInSameMicrotask = false; + + async function f() { + wasStartedInSameMicrotask = isRunningInSameMicrotask; + outer: { + didEvaluatePrecedingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + if (BREAK_EARLY) break outer; + await using _ = null; + } + wasRunningInSameMicrotask = isRunningInSameMicrotask; + } + + var p = f(); + isRunningInSameMicrotask = false; + await p; + + assert.sameValue(wasStartedInSameMicrotask, true, 'Expected async function containing `await using` to start in the same microtask'); + assert.sameValue(didEvaluatePrecedingBlockStatementsInSameMicrotask, true, 'Expected block statements preceding `await using` to be evaluated in the same microtask'); + assert.sameValue(wasRunningInSameMicrotask, true, 'Expected statements following the block containing unevaluated `await using` to evaluate in the same microtask'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/await-using-implies-await-if-evaluated.js b/js/src/tests/test262/language/statements/await-using/await-using-implies-await-if-evaluated.js new file mode 100644 index 0000000000000..b737eccf59212 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/await-using-implies-await-if-evaluated.js @@ -0,0 +1,104 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: An 'await using' implies an Await occurs if the statement is evaluated, even if all initializers are 'null' or 'undefined' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + ... + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var isRunningInSameMicrotask = true; + var wasStartedInSameMicrotask = false; + var didEvaluatePrecedingBlockStatementsInSameMicrotask = false; + var didEvaluateFollowingBlockStatementsInSameMicrotask = false; + var wasRunningInSameMicrotask = false; + + async function f() { + wasStartedInSameMicrotask = isRunningInSameMicrotask; + { + didEvaluatePrecedingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + await using _ = null; + didEvaluateFollowingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + } + wasRunningInSameMicrotask = isRunningInSameMicrotask; + } + + var p = f(); + isRunningInSameMicrotask = false; + await p; + + assert.sameValue(wasStartedInSameMicrotask, true, 'Expected async function containing `await using` to start in the same microtask'); + assert.sameValue(didEvaluatePrecedingBlockStatementsInSameMicrotask, true, 'Expected block statements preceding `await using` to be evaluated in the same microtask'); + assert.sameValue(didEvaluateFollowingBlockStatementsInSameMicrotask, true, 'Expected block statements following `await using` to be evaluated in the same microtask'); + assert.sameValue(wasRunningInSameMicrotask, false, 'Expected statements following the block containing evaluated `await using` to evaluate in a different microtask'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/block-local-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/await-using/block-local-closure-get-before-initialization.js new file mode 100644 index 0000000000000..27b7e11d56bad --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/block-local-closure-get-before-initialization.js @@ -0,0 +1,23 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..1563bf2603ef8 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + await using x = x + 1; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..f9be4280e888e --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + x; await using x = null; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/fn-name-arrow.js b/js/src/tests/test262/language/statements/await-using/fn-name-arrow.js new file mode 100644 index 0000000000000..b6bf07a2a3c66 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/fn-name-arrow.js @@ -0,0 +1,29 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ArrowFunction) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function () { + await using arrow = () => {}; + + assert.sameValue(arrow.name, 'arrow'); + verifyNotEnumerable(arrow, 'name'); + verifyNotWritable(arrow, 'name'); + verifyConfigurable(arrow, 'name'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/fn-name-class.js b/js/src/tests/test262/language/statements/await-using/fn-name-class.js new file mode 100644 index 0000000000000..7e666c1e59056 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/fn-name-class.js @@ -0,0 +1,32 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ClassExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [class, explicit-resource-management] +---*/ + +asyncTest(async function () { + await using xCls = class x { static async [Symbol.asyncDispose]() {} }; + await using cls = class { static async [Symbol.asyncDispose]() {} }; + await using xCls2 = class { static name() {} static async [Symbol.asyncDispose]() {} }; + + assert.notSameValue(xCls.name, 'xCls'); + assert.notSameValue(xCls2.name, 'xCls2'); + + assert.sameValue(cls.name, 'cls'); + verifyNotEnumerable(cls, 'name'); + verifyNotWritable(cls, 'name'); + verifyConfigurable(cls, 'name'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/fn-name-cover.js b/js/src/tests/test262/language/statements/await-using/fn-name-cover.js new file mode 100644 index 0000000000000..ae39299875132 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/fn-name-cover.js @@ -0,0 +1,33 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Assignment of function `name` attribute (CoverParenthesizedExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function () { + await using xCover = (0, function() {}); + await using cover = (function() {}); + + assert(xCover.name !== 'xCover'); + + assert.sameValue(cover.name, 'cover'); + verifyNotEnumerable(cover, 'name'); + verifyNotWritable(cover, 'name'); + verifyConfigurable(cover, 'name'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/fn-name-fn.js b/js/src/tests/test262/language/statements/await-using/fn-name-fn.js new file mode 100644 index 0000000000000..306a467bcd884 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/fn-name-fn.js @@ -0,0 +1,32 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (FunctionExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function() { + await using xFn = function x() {}; + await using fn = function() {}; + + assert(xFn.name !== 'xFn'); + + assert.sameValue(fn.name, 'fn'); + verifyNotEnumerable(fn, 'name'); + verifyNotWritable(fn, 'name'); + verifyConfigurable(fn, 'name'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/fn-name-gen.js b/js/src/tests/test262/language/statements/await-using/fn-name-gen.js new file mode 100644 index 0000000000000..b6a3cbc145982 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/fn-name-gen.js @@ -0,0 +1,32 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (GeneratorExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [generators,explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function() { + await using xGen = function* x() {}; + await using gen = function*() {}; + + assert(xGen.name !== 'xGen'); + + assert.sameValue(gen.name, 'gen'); + verifyNotEnumerable(gen, 'name'); + verifyNotWritable(gen, 'name'); + verifyConfigurable(gen, 'name'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/function-local-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/await-using/function-local-closure-get-before-initialization.js new file mode 100644 index 0000000000000..bcc3f345ec63e --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/function-local-closure-get-before-initialization.js @@ -0,0 +1,24 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..5dbf2807ae2d1 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,18 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + await using x = x + 1; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..541915d0e1d76 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + x; await using x = null; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js new file mode 100644 index 0000000000000..764b279ef3b32 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js @@ -0,0 +1,89 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.asyncDispose]` method once, when initialized. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + asyncDisposeReadCount: 0, + get [Symbol.asyncDispose]() { + this.asyncDisposeReadCount++; + return async function() { }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.asyncDisposeReadCount, 1, 'Expected [Symbol.asyncDispose] to have been read only once'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js new file mode 100644 index 0000000000000..076aeac24b1e0 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js @@ -0,0 +1,93 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Reads `[Symbol.dispose]` method if `[Symbol.asyncDispose]` is null +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return null; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose', 'Symbol.dispose'], 'Expected [Symbol.dispose] to have been read after [Symbol.asyncDispose] returns null'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js new file mode 100644 index 0000000000000..951649d8827d6 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js @@ -0,0 +1,93 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Reads `[Symbol.dispose]` method if `[Symbol.asyncDispose]` is undefined. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return undefined; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose', 'Symbol.dispose'], 'Expected [Symbol.dispose] to have been read after [Symbol.asyncDispose] returns undefined'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js new file mode 100644 index 0000000000000..55677eb4d0c02 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js @@ -0,0 +1,89 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.dispose]` method once, when initialized. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposeReadCount, 1, 'Expected [Symbol.dispose] to have been read only once'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js b/js/src/tests/test262/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js new file mode 100644 index 0000000000000..5e1006b6aab3f --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js @@ -0,0 +1,93 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Does not read `[Symbol.dispose]` if `[Symbol.asyncDispose]` is neither null or undefined. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return async function() {}; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose'], 'Expected [Symbol.dispose] to not have been read'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/global-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/await-using/global-closure-get-before-initialization.js new file mode 100644 index 0000000000000..ffd61a91443b6 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/global-closure-get-before-initialization.js @@ -0,0 +1,23 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..11edac0d47752 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,20 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + await using x = x + 1; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..d0e51235e8dae --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/global-use-before-initialization-in-prior-statement.js @@ -0,0 +1,20 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global use before initialization in prior statement. + (TDZ, Temporal Dead Zone) + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + await assert.throwsAsync(ReferenceError, async function () { + x; await using x = null; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js new file mode 100644 index 0000000000000..e40d0529336df --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js @@ -0,0 +1,106 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + await using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js new file mode 100644 index 0000000000000..617b355675bf7 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,108 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + await using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js new file mode 100644 index 0000000000000..bc4c9ae3dadc1 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js @@ -0,0 +1,59 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js new file mode 100644 index 0000000000000..5a09b4684f16e --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js @@ -0,0 +1,85 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var wasDisposedBeforeBody = false; + var wasDisposedBeforeIteration = false; + var wasDisposedAfterIteration = false; + + function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; + } + + for (await using _ of g()) { + wasDisposedBeforeBody = resource.disposed; + } + + assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); + assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); + assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js new file mode 100644 index 0000000000000..c165c4cd6bda2 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js @@ -0,0 +1,63 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var i = 0; + var wasDisposedInForStatement; + for (await using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; + } + + assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 0000000000000..7e13240414bfc --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.js @@ -0,0 +1,69 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + var i = 0; + for (await using _1 = resource, _2 = getResource(); i < 1; i++) { + } + }, 'for'); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js new file mode 100644 index 0000000000000..14e233ab46fb8 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js @@ -0,0 +1,63 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + await using _1 = resource, _2 = getResource(); + }); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js new file mode 100644 index 0000000000000..5f1a25ce4f6a2 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js @@ -0,0 +1,106 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + await using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js new file mode 100644 index 0000000000000..e6bcd1f1e4b8a --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,108 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + await using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js new file mode 100644 index 0000000000000..40092eb4304be --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js @@ -0,0 +1,59 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js new file mode 100644 index 0000000000000..18e970e3b4fa7 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js @@ -0,0 +1,85 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var wasDisposedBeforeBody = false; + var wasDisposedBeforeIteration = false; + var wasDisposedAfterIteration = false; + + function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; + } + + for (await using _ of g()) { + wasDisposedBeforeBody = resource.disposed; + } + + assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); + assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); + assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js new file mode 100644 index 0000000000000..c165c4cd6bda2 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js @@ -0,0 +1,63 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var i = 0; + var wasDisposedInForStatement; + for (await using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; + } + + assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 0000000000000..912c710adc571 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.js @@ -0,0 +1,69 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + var i = 0; + for (await using _1 = resource, _2 = getResource(); i < 1; i++) { + } + }, 'for'); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js new file mode 100644 index 0000000000000..e8bcc3c62cb28 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js @@ -0,0 +1,63 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + await using _1 = resource, _2 = getResource(); + }); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/multiple-resources-disposed-in-reverse-order.js b/js/src/tests/test262/language/statements/await-using/multiple-resources-disposed-in-reverse-order.js new file mode 100644 index 0000000000000..d5d040f1056ac --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/multiple-resources-disposed-in-reverse-order.js @@ -0,0 +1,60 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: Multiple resources are disposed in the reverse of the order in which they were added +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var disposed = []; + var resource1 = { async [Symbol.asyncDispose]() { disposed.push(this); } }; + var resource2 = { [Symbol.dispose]() { disposed.push(this); } }; + var resource3 = { async [Symbol.asyncDispose]() { disposed.push(this); } }; + + { + await using _1 = resource1, _2 = resource2; + await using _3 = resource3; + } + + assert.sameValue(disposed[0], resource3); + assert.sameValue(disposed[1], resource2); + assert.sameValue(disposed[2], resource1); +}); diff --git a/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js b/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js new file mode 100644 index 0000000000000..ac78446f7f576 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js @@ -0,0 +1,74 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple lexical bindings + in a single 'await using' declaration +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var disposed = []; + var resource1 = { + async [Symbol.asyncDispose]() { + disposed.push(this); + } + }; + var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } + }; + { + await using r1 = resource1, r2 = resource2; + } + assert.sameValue(2, disposed.length); + assert.sameValue(disposed[0], resource2, 'Expected resource2 to be the first disposed resource'); + assert.sameValue(disposed[1], resource1, 'Expected resource1 to be the second disposed resource'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js b/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js new file mode 100644 index 0000000000000..d2f319e3a9170 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js @@ -0,0 +1,69 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple subsequent 'await using' + declarations in the same block scope +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var disposed = []; + var resource1 = { + async [Symbol.asyncDispose]() { + disposed.push(this); + } + }; + var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } + }; + { + await using r1 = resource1; + await using r2 = resource2; + } + assert.sameValue(2, disposed.length); + assert.sameValue(disposed[0], resource2, 'Expected resource2 to be the first disposed resource'); + assert.sameValue(disposed[1], resource1, 'Expected resource1 to be the second disposed resource'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js b/js/src/tests/test262/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js new file mode 100644 index 0000000000000..a5205a65ba5d6 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js @@ -0,0 +1,17 @@ +// |reftest| error:SyntaxError +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-initializebinding-n-v +description: > + Redeclaration error within strict mode function inside non-strict code. +negative: + phase: parse + type: SyntaxError +flags: [noStrict, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +(async function() { 'use strict'; { await using f = null; var f; } }) + diff --git a/js/src/tests/test262/language/statements/await-using/shell.js b/js/src/tests/test262/language/statements/await-using/shell.js index 0222d37d0d23d..953d0a2cea3c5 100644 --- a/js/src/tests/test262/language/statements/await-using/shell.js +++ b/js/src/tests/test262/language/statements/await-using/shell.js @@ -105,3 +105,405 @@ assert.throwsAsync = function (expectedErrorConstructor, func, message) { )); }); }; + +// file: deepEqual.js +// Copyright 2019 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Compare two values structurally +defines: [assert.deepEqual] +---*/ + +assert.deepEqual = function(actual, expected, message) { + var format = assert.deepEqual.format; + var mustBeTrue = assert.deepEqual._compare(actual, expected); + + // format can be slow when `actual` or `expected` are large objects, like for + // example the global object, so only call it when the assertion will fail. + if (mustBeTrue !== true) { + message = `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`; + } + + assert(mustBeTrue, message); +}; + +(function() { +let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +let join = arr => arr.join(', '); +function stringFromTemplate(strings, subs) { + let parts = strings.map((str, i) => `${i === 0 ? '' : subs[i - 1]}${str}`); + return parts.join(''); +} +function escapeKey(key) { + if (typeof key === 'symbol') return `[${String(key)}]`; + if (/^[a-zA-Z0-9_$]+$/.test(key)) return key; + return assert._formatIdentityFreeValue(key); +} + +assert.deepEqual.format = function(value, seen) { + let basic = assert._formatIdentityFreeValue(value); + if (basic) return basic; + switch (value === null ? 'null' : typeof value) { + case 'string': + case 'bigint': + case 'number': + case 'boolean': + case 'undefined': + case 'null': + assert(false, 'values without identity should use basic formatting'); + break; + case 'symbol': + case 'function': + case 'object': + break; + default: + return typeof value; + } + + if (!seen) { + seen = { + counter: 0, + map: new Map() + }; + } + let usage = seen.map.get(value); + if (usage) { + usage.used = true; + return `ref #${usage.id}`; + } + usage = { id: ++seen.counter, used: false }; + seen.map.set(value, usage); + + // Properly communicating multiple references requires deferred rendering of + // all identity-bearing values until the outermost format call finishes, + // because the current value can also in appear in a not-yet-visited part of + // the object graph (which, when visited, will update the usage object). + // + // To preserve readability of the desired output formatting, we accomplish + // this deferral using tagged template literals. + // Evaluation closes over the usage object and returns a function that accepts + // "mapper" arguments for rendering the corresponding substitution values and + // returns an object with only a toString method which will itself be invoked + // when trying to use the result as a string in assert.deepEqual. + // + // For convenience, any absent mapper is presumed to be `String`, and the + // function itself has a toString method that self-invokes with no mappers + // (allowing returning the function directly when every mapper is `String`). + function lazyResult(strings, ...subs) { + function acceptMappers(...mappers) { + function toString() { + let renderings = subs.map((sub, i) => (mappers[i] || String)(sub)); + let rendered = stringFromTemplate(strings, renderings); + if (usage.used) rendered += ` as #${usage.id}`; + return rendered; + } + + return { toString }; + } + + acceptMappers.toString = () => String(acceptMappers()); + return acceptMappers; + } + + let format = assert.deepEqual.format; + function lazyString(strings, ...subs) { + return { toString: () => stringFromTemplate(strings, subs) }; + } + + if (typeof value === 'function') { + return lazyResult`function${value.name ? ` ${String(value.name)}` : ''}`; + } + if (typeof value !== 'object') { + // probably a symbol + return lazyResult`${value}`; + } + if (Array.isArray ? Array.isArray(value) : value instanceof Array) { + return lazyResult`[${value.map(value => format(value, seen))}]`(join); + } + if (value instanceof Date) { + return lazyResult`Date(${format(value.toISOString(), seen)})`; + } + if (value instanceof Error) { + return lazyResult`error ${value.name || 'Error'}(${format(value.message, seen)})`; + } + if (value instanceof RegExp) { + return lazyResult`${value}`; + } + if (typeof Map !== "undefined" && value instanceof Map) { + let contents = Array.from(value).map(pair => lazyString`${format(pair[0], seen)} => ${format(pair[1], seen)}`); + return lazyResult`Map {${contents}}`(join); + } + if (typeof Set !== "undefined" && value instanceof Set) { + let contents = Array.from(value).map(value => format(value, seen)); + return lazyResult`Set {${contents}}`(join); + } + + let tag = Symbol.toStringTag && Symbol.toStringTag in value + ? value[Symbol.toStringTag] + : Object.getPrototypeOf(value) === null ? '[Object: null prototype]' : 'Object'; + let keys = Reflect.ownKeys(value).filter(key => getOwnPropertyDescriptor(value, key).enumerable); + let contents = keys.map(key => lazyString`${escapeKey(key)}: ${format(value[key], seen)}`); + return lazyResult`${tag ? `${tag} ` : ''}{${contents}}`(String, join); +}; +})(); + +assert.deepEqual._compare = (function () { + var EQUAL = 1; + var NOT_EQUAL = -1; + var UNKNOWN = 0; + + function deepEqual(a, b) { + return compareEquality(a, b) === EQUAL; + } + + function compareEquality(a, b, cache) { + return compareIf(a, b, isOptional, compareOptionality) + || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality) + || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache) + || NOT_EQUAL; + } + + function compareIf(a, b, test, compare, cache) { + return !test(a) + ? !test(b) ? UNKNOWN : NOT_EQUAL + : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache); + } + + function tryCompareStrictEquality(a, b) { + return a === b ? EQUAL : UNKNOWN; + } + + function tryCompareTypeOfEquality(a, b) { + return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN; + } + + function tryCompareToStringTagEquality(a, b) { + var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined; + var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined; + return aTag !== bTag ? NOT_EQUAL : UNKNOWN; + } + + function isOptional(value) { + return value === undefined + || value === null; + } + + function compareOptionality(a, b) { + return tryCompareStrictEquality(a, b) + || NOT_EQUAL; + } + + function isPrimitiveEquatable(value) { + switch (typeof value) { + case 'string': + case 'number': + case 'bigint': + case 'boolean': + case 'symbol': + return true; + default: + return isBoxed(value); + } + } + + function comparePrimitiveEquality(a, b) { + if (isBoxed(a)) a = a.valueOf(); + if (isBoxed(b)) b = b.valueOf(); + return tryCompareStrictEquality(a, b) + || tryCompareTypeOfEquality(a, b) + || compareIf(a, b, isNaNEquatable, compareNaNEquality) + || NOT_EQUAL; + } + + function isNaNEquatable(value) { + return typeof value === 'number'; + } + + function compareNaNEquality(a, b) { + return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL; + } + + function isObjectEquatable(value) { + return typeof value === 'object' || typeof value === 'function'; + } + + function compareObjectEquality(a, b, cache) { + if (!cache) cache = new Map(); + return getCache(cache, a, b) + || setCache(cache, a, b, EQUAL) // consider equal for now + || cacheComparison(a, b, tryCompareStrictEquality, cache) + || cacheComparison(a, b, tryCompareToStringTagEquality, cache) + || compareIf(a, b, isValueOfEquatable, compareValueOfEquality) + || compareIf(a, b, isToStringEquatable, compareToStringEquality) + || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache) + || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache) + || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || cacheComparison(a, b, fail, cache); + } + + function isBoxed(value) { + return value instanceof String + || value instanceof Number + || value instanceof Boolean + || typeof Symbol === 'function' && value instanceof Symbol + || typeof BigInt === 'function' && value instanceof BigInt; + } + + function isValueOfEquatable(value) { + return value instanceof Date; + } + + function compareValueOfEquality(a, b) { + return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isToStringEquatable(value) { + return value instanceof RegExp; + } + + function compareToStringEquality(a, b) { + return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isArrayLikeEquatable(value) { + return (Array.isArray ? Array.isArray(value) : value instanceof Array) + || (typeof Uint8Array === 'function' && value instanceof Uint8Array) + || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray) + || (typeof Uint16Array === 'function' && value instanceof Uint16Array) + || (typeof Uint32Array === 'function' && value instanceof Uint32Array) + || (typeof Int8Array === 'function' && value instanceof Int8Array) + || (typeof Int16Array === 'function' && value instanceof Int16Array) + || (typeof Int32Array === 'function' && value instanceof Int32Array) + || (typeof Float32Array === 'function' && value instanceof Float32Array) + || (typeof Float64Array === 'function' && value instanceof Float64Array) + || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array) + || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array); + } + + function compareArrayLikeEquality(a, b, cache) { + if (a.length !== b.length) return NOT_EQUAL; + for (var i = 0; i < a.length; i++) { + if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + return EQUAL; + } + + function isStructurallyEquatable(value) { + return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference + || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference + || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference + || typeof Map === 'function' && value instanceof Map // comparable via @@iterator + || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator + } + + function compareStructuralEquality(a, b, cache) { + var aKeys = []; + for (var key in a) aKeys.push(key); + + var bKeys = []; + for (var key in b) bKeys.push(key); + + if (aKeys.length !== bKeys.length) { + return NOT_EQUAL; + } + + aKeys.sort(); + bKeys.sort(); + + for (var i = 0; i < aKeys.length; i++) { + var aKey = aKeys[i]; + var bKey = bKeys[i]; + if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + + return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || EQUAL; + } + + function isIterableEquatable(value) { + return typeof Symbol === 'function' + && typeof value[Symbol.iterator] === 'function'; + } + + function compareIteratorEquality(a, b, cache) { + if (typeof Map === 'function' && a instanceof Map && b instanceof Map || + typeof Set === 'function' && a instanceof Set && b instanceof Set) { + if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size + } + + var ar, br; + while (true) { + ar = a.next(); + br = b.next(); + if (ar.done) { + if (br.done) return EQUAL; + if (b.return) b.return(); + return NOT_EQUAL; + } + if (br.done) { + if (a.return) a.return(); + return NOT_EQUAL; + } + if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) { + if (a.return) a.return(); + if (b.return) b.return(); + return NOT_EQUAL; + } + } + } + + function compareIterableEquality(a, b, cache) { + return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache); + } + + function cacheComparison(a, b, compare, cache) { + var result = compare(a, b, cache); + if (cache && (result === EQUAL || result === NOT_EQUAL)) { + setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result)); + } + return result; + } + + function fail() { + return NOT_EQUAL; + } + + function setCache(cache, left, right, result) { + var otherCache; + + otherCache = cache.get(left); + if (!otherCache) cache.set(left, otherCache = new Map()); + otherCache.set(right, result); + + otherCache = cache.get(right); + if (!otherCache) cache.set(right, otherCache = new Map()); + otherCache.set(left, result); + } + + function getCache(cache, left, right) { + var otherCache; + var result; + + otherCache = cache.get(left); + result = otherCache && otherCache.get(right); + if (result) return result; + + otherCache = cache.get(right); + result = otherCache && otherCache.get(left); + if (result) return result; + + return UNKNOWN; + } + + return deepEqual; +})(); diff --git a/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-caseclause.js b/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-caseclause.js new file mode 100644 index 0000000000000..10982b5bf84c1 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-caseclause.js @@ -0,0 +1,27 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:SyntaxError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2025 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-const-using-and-await-using-declarations-static-semantics-early-errors +description: Disallowed in switch statement +info: | + - It is a Syntax Error if the goal symbol is |Script| and |AwaitUsingDeclaration| is not contained, either directly or indirectly, within a |Block|, |ForStatement|, |ForInOfStatement|, |FunctionBody|, |GeneratorBody|, |AsyncGeneratorBody|, |AsyncFunctionBody|, or |ClassStaticBlockBody|. + - It is a Syntax Error if |AwaitUsingDeclaration| is contained directly within the |StatementList| of either a |CaseClause| or |DefaultClause|. + +negative: + phase: parse + type: SyntaxError + +features: [explicit-resource-management] +---*/ + +async function f() { + switch (0) { + case 0: + await using _ = null; + break; + } +} + +$DONOTEVALUATE(); diff --git a/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-defaultclause.js b/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-defaultclause.js new file mode 100644 index 0000000000000..38c70e8c6e3d5 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/syntax/await-using-invalid-switchstatement-defaultclause.js @@ -0,0 +1,27 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:SyntaxError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2025 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-const-using-and-await-using-declarations-static-semantics-early-errors +description: Disallowed in switch statement +info: | + - It is a Syntax Error if the goal symbol is |Script| and |AwaitUsingDeclaration| is not contained, either directly or indirectly, within a |Block|, |ForStatement|, |ForInOfStatement|, |FunctionBody|, |GeneratorBody|, |AsyncGeneratorBody|, |AsyncFunctionBody|, or |ClassStaticBlockBody|. + - It is a Syntax Error if |AwaitUsingDeclaration| is contained directly within the |StatementList| of either a |CaseClause| or |DefaultClause|. + +negative: + phase: parse + type: SyntaxError + +features: [explicit-resource-management] +---*/ + +async function f() { + switch (0) { + default: + await using _ = null; + break; + } +} + +$DONOTEVALUATE(); diff --git a/js/src/tests/test262/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js b/js/src/tests/test262/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 0000000000000..b70e11fd7cff0 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js @@ -0,0 +1,57 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Rethrows an error as-is if it is the only error thrown during evaluation of subsequent statements following 'await using' + or from disposal. +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + class MyError extends Error {} + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { throw new MyError(); } }; + await using _2 = { [Symbol.dispose]() { } }; + }); + + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { } }; + await using _2 = { [Symbol.dispose]() { throw new MyError(); } }; + }); + + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { } }; + await using _2 = { [Symbol.dispose]() { } }; + throw new MyError(); + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.js new file mode 100644 index 0000000000000..35ecdadbe259a --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.js @@ -0,0 +1,79 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is null and Symbol.dispose is not present +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.asyncDispose]: null }; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.js new file mode 100644 index 0000000000000..e7432ae4b3e7f --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.js @@ -0,0 +1,79 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is undefined and Symbol.dispose is not present +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.asyncDispose]: undefined }; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js new file mode 100644 index 0000000000000..33de7669d52ae --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js @@ -0,0 +1,100 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is not callable +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + 4. Return func. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: true }; + }, 'true'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: false }; + }, 'false'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: 1 }; + }, 'number'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: 'object' }; + }, 'string'); + + var s = Symbol(); + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: s }; + }, 'symbol'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.js new file mode 100644 index 0000000000000..145db59e36050 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.js @@ -0,0 +1,79 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is null +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: null }; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.js new file mode 100644 index 0000000000000..d7f513cabd4cd --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.js @@ -0,0 +1,79 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is undefined +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: undefined }; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js new file mode 100644 index 0000000000000..f68b49aadd10f --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js @@ -0,0 +1,100 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is not callable +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + 4. Return func. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: true }; + }, 'true'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: false }; + }, 'false'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: 1 }; + }, 'number'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: 'object' }; + }, 'string'); + + var s = Symbol(); + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: s }; + }, 'symbol'); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js new file mode 100644 index 0000000000000..836564abf6d93 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js @@ -0,0 +1,80 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value is missing both the Symbol.asyncDispose and Symbol.dispose properties +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = {}; + }); +}); diff --git a/js/src/tests/test262/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js b/js/src/tests/test262/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 0000000000000..28d6b649c8707 --- /dev/null +++ b/js/src/tests/test262/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js @@ -0,0 +1,58 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Throws a nested hierarchy of SuppressedErrors if multiple errors were thrown during evaluation of subsequent statements following 'await using' + and/or from disposal. +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + class MyError extends Error {} + const error1 = new MyError(); + const error2 = new MyError(); + const error3 = new MyError(); + + try { + await using _1 = { async [Symbol.asyncDispose]() { throw error1; } }; + await using _2 = { [Symbol.dispose]() { throw error2; } }; + throw error3; + } + catch (e) { + assert(e instanceof SuppressedError, "Expected an SuppressedError to have been thrown"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "Expected the outermost suppressed error to have been a SuppressedError"); + assert.sameValue(e.suppressed.error, error2, "Expected the innermost suppressing error to have been 'error2'"); + assert.sameValue(e.suppressed.suppressed, error3, "Expected the innermost suppressed error to have been 'error3'"); + } +}); diff --git a/js/src/tests/test262/language/statements/using/Symbol.dispose-getter.js b/js/src/tests/test262/language/statements/using/Symbol.dispose-getter.js new file mode 100644 index 0000000000000..760a9cf2ff600 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/Symbol.dispose-getter.js @@ -0,0 +1,90 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.dispose] getter +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + 4. Return func. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/Symbol.dispose-method-called-with-correct-this.js b/js/src/tests/test262/language/statements/using/Symbol.dispose-method-called-with-correct-this.js new file mode 100644 index 0000000000000..3ff9f349bbda5 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/Symbol.dispose-method-called-with-correct-this.js @@ -0,0 +1,53 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + ... + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + assert.sameValue(this, resource); + } +}; + +{ + using _ = resource; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/block-local-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/using/block-local-closure-get-before-initialization.js new file mode 100644 index 0000000000000..c888653f9ff8e --- /dev/null +++ b/js/src/tests/test262/language/statements/using/block-local-closure-get-before-initialization.js @@ -0,0 +1,23 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ +{ + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +} + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..482557741c25c --- /dev/null +++ b/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + { + using x = x + 1; + } +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..edf86bd42ef70 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/block-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + { + x; using x = null; + } +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/cptn-value.js b/js/src/tests/test262/language/statements/using/cptn-value.js new file mode 100644 index 0000000000000..487446518d41f --- /dev/null +++ b/js/src/tests/test262/language/statements/using/cptn-value.js @@ -0,0 +1,29 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Returns an empty completion +info: | + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + +features: [explicit-resource-management] +---*/ + +assert.sameValue( + eval('{using test262id1 = null;}'), undefined, 'Single declaration' +); +assert.sameValue( + eval('{using test262id2 = null, test262id3 = null;}'), + undefined, + 'Multiple declarations' +); + +assert.sameValue(eval('4; {using test262id5 = null;}'), 4); +assert.sameValue(eval('6; {using test262id7 = null, test262id8 = null;}'), 6); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/fn-name-arrow.js b/js/src/tests/test262/language/statements/using/fn-name-arrow.js new file mode 100644 index 0000000000000..01f794863a459 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/fn-name-arrow.js @@ -0,0 +1,29 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ArrowFunction) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using arrow = () => {}; + + assert.sameValue(arrow.name, 'arrow'); + verifyNotEnumerable(arrow, 'name'); + verifyNotWritable(arrow, 'name'); + verifyConfigurable(arrow, 'name'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/fn-name-class.js b/js/src/tests/test262/language/statements/using/fn-name-class.js new file mode 100644 index 0000000000000..0a2f1b9234976 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/fn-name-class.js @@ -0,0 +1,31 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ClassExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [class, explicit-resource-management] +---*/ +{ + using xCls = class x { static [Symbol.dispose]() {} }; + using cls = class { static [Symbol.dispose]() {} }; + using xCls2 = class { static name() {} static [Symbol.dispose]() {} }; + + assert.notSameValue(xCls.name, 'xCls'); + assert.notSameValue(xCls2.name, 'xCls2'); + + assert.sameValue(cls.name, 'cls'); + verifyNotEnumerable(cls, 'name'); + verifyNotWritable(cls, 'name'); + verifyConfigurable(cls, 'name'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/fn-name-cover.js b/js/src/tests/test262/language/statements/using/fn-name-cover.js new file mode 100644 index 0000000000000..8467cefe2109b --- /dev/null +++ b/js/src/tests/test262/language/statements/using/fn-name-cover.js @@ -0,0 +1,33 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Assignment of function `name` attribute (CoverParenthesizedExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xCover = (0, function() {}); + using cover = (function() {}); + + assert(xCover.name !== 'xCover'); + + assert.sameValue(cover.name, 'cover'); + verifyNotEnumerable(cover, 'name'); + verifyNotWritable(cover, 'name'); + verifyConfigurable(cover, 'name'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/fn-name-fn.js b/js/src/tests/test262/language/statements/using/fn-name-fn.js new file mode 100644 index 0000000000000..900234eb592dd --- /dev/null +++ b/js/src/tests/test262/language/statements/using/fn-name-fn.js @@ -0,0 +1,32 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (FunctionExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xFn = function x() {}; + using fn = function() {}; + + assert(xFn.name !== 'xFn'); + + assert.sameValue(fn.name, 'fn'); + verifyNotEnumerable(fn, 'name'); + verifyNotWritable(fn, 'name'); + verifyConfigurable(fn, 'name'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/fn-name-gen.js b/js/src/tests/test262/language/statements/using/fn-name-gen.js new file mode 100644 index 0000000000000..0a595daf1e93d --- /dev/null +++ b/js/src/tests/test262/language/statements/using/fn-name-gen.js @@ -0,0 +1,32 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (GeneratorExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [generators,explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xGen = function* x() {}; + using gen = function*() {}; + + assert(xGen.name !== 'xGen'); + + assert.sameValue(gen.name, 'gen'); + verifyNotEnumerable(gen, 'name'); + verifyNotWritable(gen, 'name'); + verifyConfigurable(gen, 'name'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/function-local-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/using/function-local-closure-get-before-initialization.js new file mode 100644 index 0000000000000..d6a8dd6d87260 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/function-local-closure-get-before-initialization.js @@ -0,0 +1,23 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +(function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +}()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..a5aaed594fe5b --- /dev/null +++ b/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + (function() { + using x = x + 1; + }()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..1919982e06fe9 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/function-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,19 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + (function() { + x; using x = null; + }()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/gets-initializer-Symbol.dispose-property-once.js b/js/src/tests/test262/language/statements/using/gets-initializer-Symbol.dispose-property-once.js new file mode 100644 index 0000000000000..8ca84216cab50 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/gets-initializer-Symbol.dispose-property-once.js @@ -0,0 +1,87 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.dispose]` method once, when initialized. +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposeReadCount, 1, 'Expected [Symbol.dispose] to have been read only once'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/global-closure-get-before-initialization.js b/js/src/tests/test262/language/statements/using/global-closure-get-before-initialization.js new file mode 100644 index 0000000000000..8fb03c28fbece --- /dev/null +++ b/js/src/tests/test262/language/statements/using/global-closure-get-before-initialization.js @@ -0,0 +1,23 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: global closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +{ + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-declaration-statement.js b/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-declaration-statement.js new file mode 100644 index 0000000000000..e6922e1e8abcd --- /dev/null +++ b/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,18 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:ReferenceError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: global use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +features: [explicit-resource-management] +---*/ + +{ + using x = x + 1; +} diff --git a/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-prior-statement.js b/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-prior-statement.js new file mode 100644 index 0000000000000..193eb55da92dc --- /dev/null +++ b/js/src/tests/test262/language/statements/using/global-use-before-initialization-in-prior-statement.js @@ -0,0 +1,18 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:ReferenceError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: global use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +features: [explicit-resource-management] +---*/ + +{ + x; using x = null; +} diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js new file mode 100644 index 0000000000000..d51bf6eb4666c --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js @@ -0,0 +1,106 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js new file mode 100644 index 0000000000000..053c6c32e6e4d --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,108 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) async -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-block.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-block.js new file mode 100644 index 0000000000000..c0c7006693e8b --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-block.js @@ -0,0 +1,57 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs new file mode 100644 index 0000000000000..391643bce0ba9 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs @@ -0,0 +1,80 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +var wasDisposedBeforeBody = false; +var wasDisposedBeforeIteration = false; +var wasDisposedAfterIteration = false; + +function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; +} + +for (using _ of g()) { + wasDisposedBeforeBody = resource.disposed; +} + +assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); +assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); +assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-forstatement.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-forstatement.js new file mode 100644 index 0000000000000..e127acecfa569 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-forstatement.js @@ -0,0 +1,61 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +var i = 0; +var wasDisposedInForStatement; +for (using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; +} + +assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-functionbody.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-functionbody.js new file mode 100644 index 0000000000000..93b98705f1038 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-functionbody.js @@ -0,0 +1,58 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-evaluatefunctionbody +description: Initialized value is disposed at end of FunctionBody +info: | + FunctionBody : FunctionStatementList + + ... + 3. Let result be Completion(Evaluation of FunctionStatementList). + 4. Let env be the running execution context's LexicalEnvironment. + 5. Return ? DisposeResources(env.[[DisposeCapability]], result). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function f() { + using _ = resource; +} + +f(); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-generatorbody.js b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-generatorbody.js new file mode 100644 index 0000000000000..180dade448773 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-at-end-of-generatorbody.js @@ -0,0 +1,93 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-generatorstart +description: Initialized value is disposed at end of GeneratorBody +info: | + GeneratorStart ( generator, generatorBody ) + + 1. Assert: The value of generator.[[GeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be generatorBody(). + e. Assert: If we return here, the generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[GeneratorState]] to completed. + h. NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with acGenerator can be discarded at this point. + i. Let env be genContext's LexicalEnvironment. + j. If env is not undefined, then + i. Assert: env is a Declarative Environment Record. + i. Set result to DisposeResources(env.[[DisposeCapability]], result). + k. If result.[[Type]] is normal, then + i. Let resultValue be undefined. + l. Else if result.[[Type]] is return, then + i. Let resultValue be result.[[Value]]. + m. Else, + i. Assert: result.[[Type]] is throw. + ii. Return ? result. + n. Return CreateIterResultObject(resultValue, true). + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[GeneratorContext]] to genContext. + 7. Set generator.[[GeneratorState]] to suspendedStart. + 8. Return unused. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function * f() { + using _ = resource; + yield; +} + +var g = f(); +var wasDisposedBeforeGeneratorStarted = resource.disposed; +g.next(); +var wasDisposedWhileSuspended = resource.disposed; +assert.sameValue(g.next().done, true); +var isDisposedAfterGeneratorCompleted = resource.disposed; + +assert.sameValue(wasDisposedBeforeGeneratorStarted, false, 'Expected resource to not have been disposed prior to generator start'); +assert.sameValue(wasDisposedWhileSuspended, false, 'Expected resource to not have been disposed while generator is suspended'); +assert.sameValue(isDisposedAfterGeneratorCompleted, true, 'Expected resource to have been disposed after generator completed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js b/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 0000000000000..f2524b4bf4a3d --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js @@ -0,0 +1,67 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function getResource() { + throw new Error(); +} + +assert.throws(Error, function () { + var i = 0; + for (using _1 = resource, _2 = getResource(); i < 1; i++) { + } +}, 'for'); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js b/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js new file mode 100644 index 0000000000000..403956dc3c4d1 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js @@ -0,0 +1,61 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function getResource() { + throw new Error(); +} + +assert.throws(Error, function () { + using _1 = resource, _2 = getResource(); +}); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/multiple-resources-disposed-in-reverse-order.js b/js/src/tests/test262/language/statements/using/multiple-resources-disposed-in-reverse-order.js new file mode 100644 index 0000000000000..54ab55abc99f6 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/multiple-resources-disposed-in-reverse-order.js @@ -0,0 +1,58 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: Multiple resources are disposed in the reverse of the order in which they were added +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { [Symbol.dispose]() { disposed.push(this); } }; +var resource2 = { [Symbol.dispose]() { disposed.push(this); } }; +var resource3 = { [Symbol.dispose]() { disposed.push(this); } }; + +{ + using _1 = resource1, _2 = resource2; + using _3 = resource3; +} + +assert.sameValue(disposed[0], resource3); +assert.sameValue(disposed[1], resource2); +assert.sameValue(disposed[2], resource1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js b/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js new file mode 100644 index 0000000000000..31ccef59ca622 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js @@ -0,0 +1,72 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple lexical bindings + in a single 'using' declaration +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +{ + using r1 = resource1, r2 = resource2; +} +assert.sameValue(2, disposed.length); +assert.sameValue(disposed[0], resource2, 'Expected resource2 to be the first disposed resource'); +assert.sameValue(disposed[1], resource1, 'Expected resource1 to be the second disposed resource'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js b/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js new file mode 100644 index 0000000000000..1f45986776a98 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js @@ -0,0 +1,67 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple subsequent 'using' + declarations in the same block scope +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +{ + using r1 = resource1; + using r2 = resource2; +} +assert.sameValue(2, disposed.length); +assert.sameValue(disposed[0], resource2, 'Expected resource2 to be the first disposed resource'); +assert.sameValue(disposed[1], resource1, 'Expected resource1 to be the second disposed resource'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js b/js/src/tests/test262/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js new file mode 100644 index 0000000000000..29d19f663aaf4 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js @@ -0,0 +1,17 @@ +// |reftest| error:SyntaxError +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-initializebinding-n-v +description: > + Redeclaration error within strict mode function inside non-strict code. +negative: + phase: parse + type: SyntaxError +flags: [noStrict, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +(function() { 'use strict'; { using f = null; var f; } }) + diff --git a/js/src/tests/test262/language/statements/using/shell.js b/js/src/tests/test262/language/statements/using/shell.js index e69de29bb2d1d..0222d37d0d23d 100644 --- a/js/src/tests/test262/language/statements/using/shell.js +++ b/js/src/tests/test262/language/statements/using/shell.js @@ -0,0 +1,107 @@ +// GENERATED, DO NOT EDIT +// file: asyncHelpers.js +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + A collection of assertion and wrapper functions for testing asynchronous built-ins. +defines: [asyncTest, assert.throwsAsync] +---*/ + +/** + * Defines the **sole** asynchronous test of a file. + * @see {@link ../docs/rfcs/async-helpers.md} for background. + * + * @param {Function} testFunc a callback whose returned promise indicates test results + * (fulfillment for success, rejection for failure) + * @returns {void} + */ +function asyncTest(testFunc) { + if (!Object.prototype.hasOwnProperty.call(globalThis, "$DONE")) { + throw new Test262Error("asyncTest called without async flag"); + } + if (typeof testFunc !== "function") { + $DONE(new Test262Error("asyncTest called with non-function argument")); + return; + } + try { + testFunc().then( + function () { + $DONE(); + }, + function (error) { + $DONE(error); + } + ); + } catch (syncError) { + $DONE(syncError); + } +} + +/** + * Asserts that a callback asynchronously throws an instance of a particular + * error (i.e., returns a promise whose rejection value is an object referencing + * the constructor). + * + * @param {Function} expectedErrorConstructor the expected constructor of the + * rejection value + * @param {Function} func the callback + * @param {string} [message] the prefix to use for failure messages + * @returns {Promise} fulfills if the expected error is thrown, + * otherwise rejects + */ +assert.throwsAsync = function (expectedErrorConstructor, func, message) { + return new Promise(function (resolve) { + var fail = function (detail) { + if (message === undefined) { + throw new Test262Error(detail); + } + throw new Test262Error(message + " " + detail); + }; + if (typeof expectedErrorConstructor !== "function") { + fail("assert.throwsAsync called with an argument that is not an error constructor"); + } + if (typeof func !== "function") { + fail("assert.throwsAsync called with an argument that is not a function"); + } + var expectedName = expectedErrorConstructor.name; + var expectation = "Expected a " + expectedName + " to be thrown asynchronously"; + var res; + try { + res = func(); + } catch (thrown) { + fail(expectation + " but the function threw synchronously"); + } + if (res === null || typeof res !== "object" || typeof res.then !== "function") { + fail(expectation + " but result was not a thenable"); + } + var onResFulfilled, onResRejected; + var resSettlementP = new Promise(function (onFulfilled, onRejected) { + onResFulfilled = onFulfilled; + onResRejected = onRejected; + }); + try { + res.then(onResFulfilled, onResRejected) + } catch (thrown) { + fail(expectation + " but .then threw synchronously"); + } + resolve(resSettlementP.then( + function () { + fail(expectation + " but no exception was thrown at all"); + }, + function (thrown) { + var actualName; + if (thrown === null || typeof thrown !== "object") { + fail(expectation + " but thrown value was not an object"); + } else if (thrown.constructor !== expectedErrorConstructor) { + actualName = thrown.constructor.name; + if (expectedName === actualName) { + fail(expectation + + " but got a different error constructor with the same name"); + } + fail(expectation + " but got a " + actualName); + } + } + )); + }); +}; diff --git a/js/src/tests/test262/language/statements/using/static-init-await-binding-invalid.js b/js/src/tests/test262/language/statements/using/static-init-await-binding-invalid.js new file mode 100644 index 0000000000000..90bded496de92 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/static-init-await-binding-invalid.js @@ -0,0 +1,28 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:SyntaxError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-class-definitions-static-semantics-early-errors +description: BindingIdentifier may not be `await` within class static blocks +info: | + BindingIdentifier : Identifier + + [...] + - It is a Syntax Error if the code matched by this production is nested, + directly or indirectly (but not crossing function or static initialization + block boundaries), within a ClassStaticBlock and the StringValue of + Identifier is "await". +negative: + phase: parse + type: SyntaxError +features: [class-static-block, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +class C { + static { + using await = null; + } +} diff --git a/js/src/tests/test262/language/statements/using/static-init-await-binding-valid.js b/js/src/tests/test262/language/statements/using/static-init-await-binding-valid.js new file mode 100644 index 0000000000000..12a1137371b8b --- /dev/null +++ b/js/src/tests/test262/language/statements/using/static-init-await-binding-valid.js @@ -0,0 +1,22 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-class-definitions-static-semantics-early-errors +description: The `await` keyword is interpreted as an identifier within arrow function bodies +info: | + ClassStaticBlockBody : ClassStaticBlockStatementList + + [...] + - It is a Syntax Error if ContainsAwait of ClassStaticBlockStatementList is true. +features: [class-static-block, explicit-resource-management] +---*/ + +class C { + static { + (() => { using await = null; }); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-caseclause.js b/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-caseclause.js new file mode 100644 index 0000000000000..c1f3dbda6ced9 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-caseclause.js @@ -0,0 +1,25 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:SyntaxError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2025 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-const-using-and-await-using-declarations-static-semantics-early-errors +description: Disallowed in switch statement +info: | + - It is a Syntax Error if the goal symbol is |Script| and |UsingDeclaration| is not contained, either directly or indirectly, within a |Block|, |ForStatement|, |ForInOfStatement|, |FunctionBody|, |GeneratorBody|, |AsyncGeneratorBody|, |AsyncFunctionBody|, or |ClassStaticBlockBody|. + - It is a Syntax Error if |UsingDeclaration| is contained directly within the |StatementList| of either a |CaseClause| or |DefaultClause|. + +negative: + phase: parse + type: SyntaxError + +features: [explicit-resource-management] +---*/ + +switch (0) { + case 0: + using _ = null; + break; +} + +$DONOTEVALUATE(); diff --git a/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-defaultclause.js b/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-defaultclause.js new file mode 100644 index 0000000000000..57036391ef07c --- /dev/null +++ b/js/src/tests/test262/language/statements/using/syntax/using-invalid-switchstatement-defaultclause.js @@ -0,0 +1,25 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) error:SyntaxError -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2025 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-const-using-and-await-using-declarations-static-semantics-early-errors +description: Disallowed in switch statement +info: | + - It is a Syntax Error if the goal symbol is |Script| and |UsingDeclaration| is not contained, either directly or indirectly, within a |Block|, |ForStatement|, |ForInOfStatement|, |FunctionBody|, |GeneratorBody|, |AsyncGeneratorBody|, |AsyncFunctionBody|, or |ClassStaticBlockBody|. + - It is a Syntax Error if |UsingDeclaration| is contained directly within the |StatementList| of either a |CaseClause| or |DefaultClause|. + +negative: + phase: parse + type: SyntaxError + +features: [explicit-resource-management] +---*/ + +switch (0) { + default: + using _ = null; + break; +} + +$DONOTEVALUATE(); diff --git a/js/src/tests/test262/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js b/js/src/tests/test262/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 0000000000000..0bdf15aa50b54 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js @@ -0,0 +1,55 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Rethrows an error as-is if it is the only error thrown during evaluation of subsequent statements following 'using' + or from disposal. +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +class MyError extends Error {} +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { throw new MyError(); } }; + using _2 = { [Symbol.dispose]() { } }; +}); + +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { } }; + using _2 = { [Symbol.dispose]() { throw new MyError(); } }; +}); + +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { } }; + using _2 = { [Symbol.dispose]() { } }; + throw new MyError(); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.js b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.js new file mode 100644 index 0000000000000..86645f73de04a --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.js @@ -0,0 +1,75 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is null +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function () { + using x = { [Symbol.dispose]: null }; +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.js b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.js new file mode 100644 index 0000000000000..69910d04f38a9 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.js @@ -0,0 +1,75 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is undefined +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function () { + using x = { [Symbol.dispose]: undefined }; +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js new file mode 100644 index 0000000000000..ebd612e952d97 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js @@ -0,0 +1,96 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is not callable +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined. + ii. Set method to undefined. + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + 4. Return func. + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: true }; +}, 'true'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: false }; +}, 'false'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: 1 }; +}, 'number'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: 'object' }; +}, 'string'); + +var s = Symbol(); +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: s }; +}, 'symbol'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js b/js/src/tests/test262/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js new file mode 100644 index 0000000000000..0c2d8b29aaa88 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js @@ -0,0 +1,68 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value is missing Symbol.dispose property +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + b. Let resource be ? CreateDisposableResource(V, hint). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function () { + using x = {}; +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/throws-suppressederror-if-multiple-errors-during-disposal.js b/js/src/tests/test262/language/statements/using/throws-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 0000000000000..ccc76721154b8 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/throws-suppressederror-if-multiple-errors-during-disposal.js @@ -0,0 +1,56 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Throws a nested hierarchy of SuppressedErrors if multiple errors were thrown during evaluation of subsequent statements following 'using' + and/or from disposal. +info: | + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +class MyError extends Error {} +const error1 = new MyError(); +const error2 = new MyError(); +const error3 = new MyError(); + +try { + using _1 = { [Symbol.dispose]() { throw error1; } }; + using _2 = { [Symbol.dispose]() { throw error2; } }; + throw error3; +} +catch (e) { + assert(e instanceof SuppressedError, "Expected an SuppressedError to have been thrown"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "Expected the outermost suppressed error to have been a SuppressedError"); + assert.sameValue(e.suppressed.error, error2, "Expected the innermost suppressing error to have been 'error2'"); + assert.sameValue(e.suppressed.suppressed, error3, "Expected the innermost suppressed error to have been 'error3'"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/using-allows-null-initializer.js b/js/src/tests/test262/language/statements/using/using-allows-null-initializer.js new file mode 100644 index 0000000000000..a5fc324ee01a0 --- /dev/null +++ b/js/src/tests/test262/language/statements/using/using-allows-null-initializer.js @@ -0,0 +1,47 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows null in initializer of 'using' +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + ... + ... + +features: [explicit-resource-management] +---*/ + +{ + using x = null; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/language/statements/using/using-allows-undefined-initializer.js b/js/src/tests/test262/language/statements/using/using-allows-undefined-initializer.js new file mode 100644 index 0000000000000..cd4851cf2abbe --- /dev/null +++ b/js/src/tests/test262/language/statements/using/using-allows-undefined-initializer.js @@ -0,0 +1,47 @@ +// |reftest| shell-option(--enable-explicit-resource-management) skip-if(!(this.hasOwnProperty('getBuildConfiguration')&&getBuildConfiguration('explicit-resource-management'))||!xulRuntime.shell) -- explicit-resource-management is not enabled unconditionally, requires shell-options +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows undefined in initializer of 'using' +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused. + ... + ... + +features: [explicit-resource-management] +---*/ + +{ + using x = undefined; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-date-add.js b/js/src/tests/test262/staging/Temporal/v8/calendar-date-add.js deleted file mode 100644 index 6a5ebb4079204..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-date-add.js +++ /dev/null @@ -1,90 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-date-add test - in V8's mjsunit test calendar-date-add.js -features: [Temporal] ----*/ - -let p1y = new Temporal.Duration(1); -let p4y = new Temporal.Duration(4); -let p5m = new Temporal.Duration(0, 5); -let p1y2m = new Temporal.Duration(1, 2); -let p1y4d = new Temporal.Duration(1, 0, 0, 4); -let p1y2m4d = new Temporal.Duration(1, 2, 0, 4); -let p10d = new Temporal.Duration(0, 0, 0, 10); -let p1w = new Temporal.Duration(0, 0, 1); -let p6w = new Temporal.Duration(0, 0, 6); -let p2w3d = new Temporal.Duration(0, 0, 2, 3); -let p1y2w = new Temporal.Duration(1, 0, 2); -let p2m3w = new Temporal.Duration(0, 2, 3); -assert.sameValue(Temporal.PlainDate.from('2020-02-29').add(p1y).toJSON(), '2021-02-28'); -assert.sameValue(Temporal.PlainDate.from('2020-02-29').add(p4y).toJSON(), '2024-02-29'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p1y).toJSON(), '2022-07-16'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p5m).toJSON(), '2021-12-16'); -assert.sameValue(Temporal.PlainDate.from('2021-08-16').add(p5m).toJSON(), '2022-01-16'); -assert.sameValue(Temporal.PlainDate.from('2021-10-31').add(p5m).toJSON(), '2022-03-31'); -assert.sameValue(Temporal.PlainDate.from('2021-09-30').add(p5m).toJSON(), '2022-02-28'); -assert.sameValue(Temporal.PlainDate.from('2019-09-30').add(p5m).toJSON(), '2020-02-29'); -assert.sameValue(Temporal.PlainDate.from('2019-10-01').add(p5m).toJSON(), '2020-03-01'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p1y2m).toJSON(), '2022-09-16'); -assert.sameValue(Temporal.PlainDate.from('2021-11-30').add(p1y2m).toJSON(), '2023-01-30'); -assert.sameValue(Temporal.PlainDate.from('2021-12-31').add(p1y2m).toJSON(), '2023-02-28'); -assert.sameValue(Temporal.PlainDate.from('2022-12-31').add(p1y2m).toJSON(), '2024-02-29'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p1y4d).toJSON(), '2022-07-20'); -assert.sameValue(Temporal.PlainDate.from('2021-02-27').add(p1y4d).toJSON(), '2022-03-03'); -assert.sameValue(Temporal.PlainDate.from('2023-02-27').add(p1y4d).toJSON(), '2024-03-02'); -assert.sameValue(Temporal.PlainDate.from('2021-12-30').add(p1y4d).toJSON(), '2023-01-03'); -assert.sameValue(Temporal.PlainDate.from('2021-07-30').add(p1y4d).toJSON(), '2022-08-03'); -assert.sameValue(Temporal.PlainDate.from('2021-06-30').add(p1y4d).toJSON(), '2022-07-04'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p1y2m4d).toJSON(), '2022-09-20'); -assert.sameValue(Temporal.PlainDate.from('2021-02-27').add(p1y2m4d).toJSON(), '2022-05-01'); -assert.sameValue(Temporal.PlainDate.from('2021-02-26').add(p1y2m4d).toJSON(), '2022-04-30'); -assert.sameValue(Temporal.PlainDate.from('2023-02-26').add(p1y2m4d).toJSON(), '2024-04-30'); -assert.sameValue(Temporal.PlainDate.from('2021-12-30').add(p1y2m4d).toJSON(), '2023-03-04'); -assert.sameValue(Temporal.PlainDate.from('2021-07-30').add(p1y2m4d).toJSON(), '2022-10-04'); -assert.sameValue(Temporal.PlainDate.from('2021-06-30').add(p1y2m4d).toJSON(), '2022-09-03'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').add(p10d).toJSON(), '2021-07-26'); -assert.sameValue(Temporal.PlainDate.from('2021-07-26').add(p10d).toJSON(), '2021-08-05'); -assert.sameValue(Temporal.PlainDate.from('2021-12-26').add(p10d).toJSON(), '2022-01-05'); -assert.sameValue(Temporal.PlainDate.from('2020-02-26').add(p10d).toJSON(), '2020-03-07'); -assert.sameValue(Temporal.PlainDate.from('2021-02-26').add(p10d).toJSON(), '2021-03-08'); -assert.sameValue(Temporal.PlainDate.from('2020-02-19').add(p10d).toJSON(), '2020-02-29'); -assert.sameValue(Temporal.PlainDate.from('2021-02-19').add(p10d).toJSON(), '2021-03-01'); -assert.sameValue(Temporal.PlainDate.from('2021-02-19').add(p1w).toJSON(), '2021-02-26'); -assert.sameValue(Temporal.PlainDate.from('2021-02-27').add(p1w).toJSON(), '2021-03-06'); -assert.sameValue(Temporal.PlainDate.from('2020-02-27').add(p1w).toJSON(), '2020-03-05'); -assert.sameValue(Temporal.PlainDate.from('2021-12-24').add(p1w).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from('2021-12-27').add(p1w).toJSON(), '2022-01-03'); -assert.sameValue(Temporal.PlainDate.from('2021-01-27').add(p1w).toJSON(), '2021-02-03'); -assert.sameValue(Temporal.PlainDate.from('2021-06-27').add(p1w).toJSON(), '2021-07-04'); -assert.sameValue(Temporal.PlainDate.from('2021-07-27').add(p1w).toJSON(), '2021-08-03'); -assert.sameValue(Temporal.PlainDate.from('2021-02-19').add(p6w).toJSON(), '2021-04-02'); -assert.sameValue(Temporal.PlainDate.from('2021-02-27').add(p6w).toJSON(), '2021-04-10'); -assert.sameValue(Temporal.PlainDate.from('2020-02-27').add(p6w).toJSON(), '2020-04-09'); -assert.sameValue(Temporal.PlainDate.from('2021-12-24').add(p6w).toJSON(), '2022-02-04'); -assert.sameValue(Temporal.PlainDate.from('2021-12-27').add(p6w).toJSON(), '2022-02-07'); -assert.sameValue(Temporal.PlainDate.from('2021-01-27').add(p6w).toJSON(), '2021-03-10'); -assert.sameValue(Temporal.PlainDate.from('2021-06-27').add(p6w).toJSON(), '2021-08-08'); -assert.sameValue(Temporal.PlainDate.from('2021-07-27').add(p6w).toJSON(), '2021-09-07'); -assert.sameValue(Temporal.PlainDate.from('2020-02-29').add(p2w3d).toJSON(), '2020-03-17'); -assert.sameValue(Temporal.PlainDate.from('2020-02-28').add(p2w3d).toJSON(), '2020-03-16'); -assert.sameValue(Temporal.PlainDate.from('2021-02-28').add(p2w3d).toJSON(), '2021-03-17'); -assert.sameValue(Temporal.PlainDate.from('2020-12-28').add(p2w3d).toJSON(), '2021-01-14'); -assert.sameValue(Temporal.PlainDate.from('2020-02-29').add(p1y2w).toJSON(), '2021-03-14'); -assert.sameValue(Temporal.PlainDate.from('2020-02-28').add(p1y2w).toJSON(), '2021-03-14'); -assert.sameValue(Temporal.PlainDate.from('2021-02-28').add(p1y2w).toJSON(), '2022-03-14'); -assert.sameValue(Temporal.PlainDate.from('2020-12-28').add(p1y2w).toJSON(), '2022-01-11'); -assert.sameValue(Temporal.PlainDate.from('2020-02-29').add(p2m3w).toJSON(), '2020-05-20'); -assert.sameValue(Temporal.PlainDate.from('2020-02-28').add(p2m3w).toJSON(), '2020-05-19'); -assert.sameValue(Temporal.PlainDate.from('2021-02-28').add(p2m3w).toJSON(), '2021-05-19'); -assert.sameValue(Temporal.PlainDate.from('2020-12-28').add(p2m3w).toJSON(), '2021-03-21'); -assert.sameValue(Temporal.PlainDate.from('2019-12-28').add(p2m3w).toJSON(), '2020-03-20'); -assert.sameValue(Temporal.PlainDate.from('2019-10-28').add(p2m3w).toJSON(), '2020-01-18'); -assert.sameValue(Temporal.PlainDate.from('2019-10-31').add(p2m3w).toJSON(), '2020-01-21'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-date-from-fields.js b/js/src/tests/test262/staging/Temporal/v8/calendar-date-from-fields.js deleted file mode 100644 index 27df5f096a0c3..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-date-from-fields.js +++ /dev/null @@ -1,428 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-date-from-fields test - in V8's mjsunit test calendar-date-from-fields.js -features: [Temporal] ----*/ - -assert.throws(TypeError, () => Temporal.PlainDate.from()); -[ - undefined, - true, - false, - 123, - 456n, - Symbol(), - 123.456, - NaN, - null -].forEach(function (fields) { - assert.throws(TypeError, () => Temporal.PlainDate.from(fields)); - assert.throws(TypeError, () => Temporal.PlainDate.from(fields, undefined)); - assert.throws(TypeError, () => Temporal.PlainDate.from(fields, { overflow: 'constrain' })); - assert.throws(TypeError, () => Temporal.PlainDate.from(fields, { overflow: 'reject' })); -}); -assert.throws(RangeError, () => Temporal.PlainDate.from('string')); -assert.throws(RangeError, () => Temporal.PlainDate.from('string', undefined)); -assert.throws(RangeError, () => Temporal.PlainDate.from('string', { overflow: 'constrain' })); -assert.throws(RangeError, () => Temporal.PlainDate.from('string', { overflow: 'reject' })); -assert.throws(TypeError, () => Temporal.PlainDate.from({ - month: 1, - day: 17 -})); -assert.throws(TypeError, () => Temporal.PlainDate.from({ - year: 2021, - day: 17 -})); -assert.throws(TypeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'm1', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M1', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'm01', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12, - monthCode: 'M11', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M00', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M19', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M99', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M13', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: -1, - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: -Infinity, - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: -17 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: -Infinity -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 0 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 1, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 2, - day: 29 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 6, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 9, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 0, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 13, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M12', - day: 0 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M12', - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M01', - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M02', - day: 29 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M06', - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M09', - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M00', - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M13', - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 0 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 0, - day: 3 -})); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: 13 -}, { overflow: 'invalid' })); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: 15 -}).toJSON(), '2021-07-15'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: 3 -}).toJSON(), '2021-07-03'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 31 -}).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M07', - day: 15 -}).toJSON(), '2021-07-15'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M07', - day: 3 -}).toJSON(), '2021-07-03'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M12', - day: 31 -}).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 1, - day: 133 -}).toJSON(), '2021-01-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 2, - day: 133 -}).toJSON(), '2021-02-28'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 3, - day: 9033 -}).toJSON(), '2021-03-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 4, - day: 50 -}).toJSON(), '2021-04-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 5, - day: 77 -}).toJSON(), '2021-05-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 6, - day: 33 -}).toJSON(), '2021-06-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: 33 -}).toJSON(), '2021-07-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 8, - day: 300 -}).toJSON(), '2021-08-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 9, - day: 400 -}).toJSON(), '2021-09-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 10, - day: 400 -}).toJSON(), '2021-10-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 11, - day: 400 -}).toJSON(), '2021-11-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 500 -}).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 13, - day: 500 -}).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - month: 999999, - day: 500 -}).toJSON(), '2021-12-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M01', - day: 133 -}).toJSON(), '2021-01-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M02', - day: 133 -}).toJSON(), '2021-02-28'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M03', - day: 9033 -}).toJSON(), '2021-03-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M04', - day: 50 -}).toJSON(), '2021-04-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M05', - day: 77 -}).toJSON(), '2021-05-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M06', - day: 33 -}).toJSON(), '2021-06-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M07', - day: 33 -}).toJSON(), '2021-07-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M08', - day: 300 -}).toJSON(), '2021-08-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M09', - day: 400 -}).toJSON(), '2021-09-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M10', - day: 400 -}).toJSON(), '2021-10-31'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M11', - day: 400 -}).toJSON(), '2021-11-30'); -assert.sameValue(Temporal.PlainDate.from({ - year: 2021, - monthCode: 'M12', - day: 500 -}).toJSON(), '2021-12-31'); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 1, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 2, - day: 29 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 3, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 4, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 5, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 6, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 7, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 8, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 9, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 10, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 11, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 12, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 2021, - month: 13, - day: 5 -}, { overflow: 'reject' })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-date-until.js b/js/src/tests/test262/staging/Temporal/v8/calendar-date-until.js deleted file mode 100644 index 52b78bacd640c..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-date-until.js +++ /dev/null @@ -1,159 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-date-until test - in V8's mjsunit test calendar-date-until.js -features: [Temporal] ----*/ - -[ - 'hour', - 'minute', - 'second', - 'millisecond', - 'microsecond', - 'nanosecond' -].forEach(function (largestUnit) { - assert.throws(RangeError, () => Temporal.PlainDate.from('2021-07-16').until('2021-07-17', { largestUnit })); -}); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-16').toJSON(), 'PT0S'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-17').toJSON(), 'P1D'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-17').toJSON(), 'P32D'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-09-16').toJSON(), 'P62D'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-16').toJSON(), 'P365D'); -assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-07-16').toJSON(), 'P3652D'); -assert.sameValue(Temporal.PlainDate.from('2021-07-17').until('2021-07-16').toJSON(), '-P1D'); -assert.sameValue(Temporal.PlainDate.from('2021-08-17').until('2021-07-16').toJSON(), '-P32D'); -assert.sameValue(Temporal.PlainDate.from('2021-09-16').until('2021-07-16').toJSON(), '-P62D'); -assert.sameValue(Temporal.PlainDate.from('2022-07-16').until('2021-07-16').toJSON(), '-P365D'); -assert.sameValue(Temporal.PlainDate.from('2031-07-16').until('2021-07-16').toJSON(), '-P3652D'); -[ - 'day', - 'days' -].forEach(function (largestUnit) { - let opt = { largestUnit }; - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-16', opt).toJSON(), 'PT0S'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-17', opt).toJSON(), 'P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-17', opt).toJSON(), 'P32D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-09-16', opt).toJSON(), 'P62D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-16', opt).toJSON(), 'P365D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-07-16', opt).toJSON(), 'P3652D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-17').until('2021-07-16', opt).toJSON(), '-P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-17').until('2021-07-16', opt).toJSON(), '-P32D'); - assert.sameValue(Temporal.PlainDate.from('2021-09-16').until('2021-07-16', opt).toJSON(), '-P62D'); - assert.sameValue(Temporal.PlainDate.from('2022-07-16').until('2021-07-16', opt).toJSON(), '-P365D'); - assert.sameValue(Temporal.PlainDate.from('2031-07-16').until('2021-07-16', opt).toJSON(), '-P3652D'); -}); -[ - 'week', - 'weeks' -].forEach(function (largestUnit) { - let opt = { largestUnit }; - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-16', opt).toJSON(), 'PT0S'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-17', opt).toJSON(), 'P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-23', opt).toJSON(), 'P1W'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-17', opt).toJSON(), 'P4W4D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-13', opt).toJSON(), 'P4W'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-09-16', opt).toJSON(), 'P8W6D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-16', opt).toJSON(), 'P52W1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-07-16', opt).toJSON(), 'P521W5D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-17').until('2021-07-16', opt).toJSON(), '-P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-17').until('2021-07-16', opt).toJSON(), '-P4W4D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-13').until('2021-07-16', opt).toJSON(), '-P4W'); - assert.sameValue(Temporal.PlainDate.from('2021-09-16').until('2021-07-16', opt).toJSON(), '-P8W6D'); - assert.sameValue(Temporal.PlainDate.from('2022-07-16').until('2021-07-16', opt).toJSON(), '-P52W1D'); - assert.sameValue(Temporal.PlainDate.from('2031-07-16').until('2021-07-16', opt).toJSON(), '-P521W5D'); -}); -[ - 'month', - 'months' -].forEach(function (largestUnit) { - let opt = { largestUnit }; - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-16', opt).toJSON(), 'PT0S'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-17', opt).toJSON(), 'P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-23', opt).toJSON(), 'P7D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-16', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2020-12-16').until('2021-01-16', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-01-05').until('2021-02-05', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-01-07').until('2021-03-07', opt).toJSON(), 'P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-17', opt).toJSON(), 'P1M1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-13', opt).toJSON(), 'P28D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-09-16', opt).toJSON(), 'P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-16', opt).toJSON(), 'P12M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-07-16', opt).toJSON(), 'P120M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-17').until('2021-07-16', opt).toJSON(), '-P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-17').until('2021-07-16', opt).toJSON(), '-P1M1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-13').until('2021-07-16', opt).toJSON(), '-P28D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-16').until('2021-07-16', opt).toJSON(), '-P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-08-16').until('2021-07-13', opt).toJSON(), '-P1M3D'); - assert.sameValue(Temporal.PlainDate.from('2021-09-16').until('2021-07-16', opt).toJSON(), '-P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-09-21').until('2021-07-16', opt).toJSON(), '-P2M5D'); - assert.sameValue(Temporal.PlainDate.from('2022-07-16').until('2021-07-16', opt).toJSON(), '-P12M'); - assert.sameValue(Temporal.PlainDate.from('2022-07-17').until('2021-07-16', opt).toJSON(), '-P12M1D'); - assert.sameValue(Temporal.PlainDate.from('2031-07-16').until('2021-07-16', opt).toJSON(), '-P120M'); -}); -[ - 'year', - 'years' -].forEach(function (largestUnit) { - let opt = { largestUnit }; - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-16', opt).toJSON(), 'PT0S'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-17', opt).toJSON(), 'P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-07-23', opt).toJSON(), 'P7D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-16', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2020-12-16').until('2021-01-16', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-01-05').until('2021-02-05', opt).toJSON(), 'P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-01-07').until('2021-03-07', opt).toJSON(), 'P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-17', opt).toJSON(), 'P1M1D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-08-13', opt).toJSON(), 'P28D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2021-09-16', opt).toJSON(), 'P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-16', opt).toJSON(), 'P1Y'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-07-19', opt).toJSON(), 'P1Y3D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2022-09-19', opt).toJSON(), 'P1Y2M3D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-07-16', opt).toJSON(), 'P10Y'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('2031-12-16', opt).toJSON(), 'P10Y5M'); - assert.sameValue(Temporal.PlainDate.from('1997-12-16').until('2021-07-16', opt).toJSON(), 'P23Y7M'); - assert.sameValue(Temporal.PlainDate.from('1997-07-16').until('2021-07-16', opt).toJSON(), 'P24Y'); - assert.sameValue(Temporal.PlainDate.from('1997-07-16').until('2021-07-15', opt).toJSON(), 'P23Y11M29D'); - assert.sameValue(Temporal.PlainDate.from('1997-06-16').until('2021-06-15', opt).toJSON(), 'P23Y11M30D'); - assert.sameValue(Temporal.PlainDate.from('1960-02-16').until('2020-03-16', opt).toJSON(), 'P60Y1M'); - assert.sameValue(Temporal.PlainDate.from('1960-02-16').until('2021-03-15', opt).toJSON(), 'P61Y27D'); - assert.sameValue(Temporal.PlainDate.from('1960-02-16').until('2020-03-15', opt).toJSON(), 'P60Y28D'); - assert.sameValue(Temporal.PlainDate.from('2021-03-30').until('2021-07-16', opt).toJSON(), 'P3M16D'); - assert.sameValue(Temporal.PlainDate.from('2020-03-30').until('2021-07-16', opt).toJSON(), 'P1Y3M16D'); - assert.sameValue(Temporal.PlainDate.from('1960-03-30').until('2021-07-16', opt).toJSON(), 'P61Y3M16D'); - assert.sameValue(Temporal.PlainDate.from('2019-12-30').until('2021-07-16', opt).toJSON(), 'P1Y6M16D'); - assert.sameValue(Temporal.PlainDate.from('2020-12-30').until('2021-07-16', opt).toJSON(), 'P6M16D'); - assert.sameValue(Temporal.PlainDate.from('1997-12-30').until('2021-07-16', opt).toJSON(), 'P23Y6M16D'); - assert.sameValue(Temporal.PlainDate.from('0001-12-25').until('2021-07-16', opt).toJSON(), 'P2019Y6M21D'); - assert.sameValue(Temporal.PlainDate.from('2019-12-30').until('2021-03-05', opt).toJSON(), 'P1Y2M5D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-17').until('2021-07-16', opt).toJSON(), '-P1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-17').until('2021-07-16', opt).toJSON(), '-P1M1D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-13').until('2021-07-16', opt).toJSON(), '-P28D'); - assert.sameValue(Temporal.PlainDate.from('2021-08-16').until('2021-07-16', opt).toJSON(), '-P1M'); - assert.sameValue(Temporal.PlainDate.from('2021-08-16').until('2021-07-13', opt).toJSON(), '-P1M3D'); - assert.sameValue(Temporal.PlainDate.from('2021-09-16').until('2021-07-16', opt).toJSON(), '-P2M'); - assert.sameValue(Temporal.PlainDate.from('2021-09-21').until('2021-07-16', opt).toJSON(), '-P2M5D'); - assert.sameValue(Temporal.PlainDate.from('2022-07-16').until('2021-07-16', opt).toJSON(), '-P1Y'); - assert.sameValue(Temporal.PlainDate.from('2022-07-17').until('2021-07-16', opt).toJSON(), '-P1Y1D'); - assert.sameValue(Temporal.PlainDate.from('2022-10-17').until('2021-07-16', opt).toJSON(), '-P1Y3M1D'); - assert.sameValue(Temporal.PlainDate.from('2031-07-16').until('2021-07-16', opt).toJSON(), '-P10Y'); - assert.sameValue(Temporal.PlainDate.from('2032-07-16').until('2021-08-16', opt).toJSON(), '-P10Y11M'); - assert.sameValue(Temporal.PlainDate.from('2031-12-16').until('2021-07-16', opt).toJSON(), '-P10Y5M'); - assert.sameValue(Temporal.PlainDate.from('2011-07-16').until('1997-12-16', opt).toJSON(), '-P13Y7M'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('1997-07-16', opt).toJSON(), '-P24Y'); - assert.sameValue(Temporal.PlainDate.from('2021-07-15').until('1997-07-16', opt).toJSON(), '-P23Y11M30D'); - assert.sameValue(Temporal.PlainDate.from('2021-06-15').until('1997-06-16', opt).toJSON(), '-P23Y11M29D'); - assert.sameValue(Temporal.PlainDate.from('2020-03-16').until('1960-02-16', opt).toJSON(), '-P60Y1M'); - assert.sameValue(Temporal.PlainDate.from('2021-03-15').until('1960-02-16', opt).toJSON(), '-P61Y28D'); - assert.sameValue(Temporal.PlainDate.from('2020-03-15').until('1960-02-16', opt).toJSON(), '-P60Y28D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('1960-03-30', opt).toJSON(), '-P61Y3M17D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('0001-12-25', opt).toJSON(), '-P2019Y6M22D'); - assert.sameValue(Temporal.PlainDate.from('2021-07-16').until('1997-12-30', opt).toJSON(), '-P23Y6M17D'); -}); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-day-of-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-day-of-year.js deleted file mode 100644 index 48929bd405b78..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-day-of-year.js +++ /dev/null @@ -1,34 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-day-of-year test - in V8's mjsunit test calendar-day-of-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(1970, 1, 1)).dayOfYear, 1); -assert.sameValue((new Temporal.PlainDate(2000, 1, 1)).dayOfYear, 1); -assert.sameValue((new Temporal.PlainDate(2021, 1, 15)).dayOfYear, 15); -assert.sameValue((new Temporal.PlainDate(2020, 2, 15)).dayOfYear, 46); -assert.sameValue((new Temporal.PlainDate(2000, 2, 15)).dayOfYear, 46); -assert.sameValue((new Temporal.PlainDate(2020, 3, 15)).dayOfYear, 75); -assert.sameValue((new Temporal.PlainDate(2000, 3, 15)).dayOfYear, 75); -assert.sameValue((new Temporal.PlainDate(2001, 3, 15)).dayOfYear, 74); -assert.sameValue((new Temporal.PlainDate(2000, 12, 31)).dayOfYear, 366); -assert.sameValue((new Temporal.PlainDate(2001, 12, 31)).dayOfYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)).dayOfYear, 23); -assert.sameValue((new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)).dayOfYear, 54); -assert.sameValue((new Temporal.PlainDateTime(1996, 3, 23, 5, 30, 13)).dayOfYear, 83); -assert.sameValue((new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)).dayOfYear, 82); -assert.sameValue((new Temporal.PlainDateTime(1997, 12, 31, 5, 30, 13)).dayOfYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1996, 12, 31, 5, 30, 13)).dayOfYear, 366); -assert.sameValue(Temporal.PlainDate.from('2019-01-18').dayOfYear, 18); -assert.sameValue(Temporal.PlainDate.from('2020-02-18').dayOfYear, 49); -assert.sameValue(Temporal.PlainDate.from('2019-12-31').dayOfYear, 365); -assert.sameValue(Temporal.PlainDate.from('2000-12-31').dayOfYear, 366); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-day.js b/js/src/tests/test262/staging/Temporal/v8/calendar-day.js deleted file mode 100644 index f71391a9d0e09..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-day.js +++ /dev/null @@ -1,18 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-day test - in V8's mjsunit test calendar-day.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).day, 15); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).day, 23); -assert.sameValue((new Temporal.PlainMonthDay(2, 6)).day, 6); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').day, 18); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-month.js b/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-month.js deleted file mode 100644 index 6875825b99d07..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-month.js +++ /dev/null @@ -1,55 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-days-in-month test - in V8's mjsunit test calendar-days-in-month.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 1, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2020, 2, 15)).daysInMonth, 29); -assert.sameValue((new Temporal.PlainDate(2000, 2, 15)).daysInMonth, 29); -assert.sameValue((new Temporal.PlainDate(2021, 2, 15)).daysInMonth, 28); -assert.sameValue((new Temporal.PlainDate(2021, 3, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2021, 4, 15)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDate(2021, 5, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2021, 6, 15)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2021, 8, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2021, 9, 15)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDate(2021, 10, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDate(2021, 11, 15)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDate(2021, 12, 15)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 1, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1996, 2, 23, 5, 30, 13)).daysInMonth, 29); -assert.sameValue((new Temporal.PlainDateTime(2000, 2, 23, 5, 30, 13)).daysInMonth, 29); -assert.sameValue((new Temporal.PlainDateTime(1997, 2, 23, 5, 30, 13)).daysInMonth, 28); -assert.sameValue((new Temporal.PlainDateTime(1997, 3, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 4, 23, 5, 30, 13)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDateTime(1997, 5, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 6, 23, 5, 30, 13)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDateTime(1997, 7, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 9, 23, 5, 30, 13)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDateTime(1997, 10, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue((new Temporal.PlainDateTime(1997, 11, 23, 5, 30, 13)).daysInMonth, 30); -assert.sameValue((new Temporal.PlainDateTime(1997, 12, 23, 5, 30, 13)).daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-01-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2020-02-18').daysInMonth, 29); -assert.sameValue(Temporal.PlainDate.from('2019-02-18').daysInMonth, 28); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-04-18').daysInMonth, 30); -assert.sameValue(Temporal.PlainDate.from('2019-05-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-06-18').daysInMonth, 30); -assert.sameValue(Temporal.PlainDate.from('2019-07-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-08-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-09-18').daysInMonth, 30); -assert.sameValue(Temporal.PlainDate.from('2019-10-18').daysInMonth, 31); -assert.sameValue(Temporal.PlainDate.from('2019-11-18').daysInMonth, 30); -assert.sameValue(Temporal.PlainDate.from('2019-12-18').daysInMonth, 31); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-week.js b/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-week.js deleted file mode 100644 index 4d93610c2ff11..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-week.js +++ /dev/null @@ -1,17 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-days-in-week test - in V8's mjsunit test calendar-days-in-week.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).daysInWeek, 7); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).daysInWeek, 7); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').daysInWeek, 7); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-year.js deleted file mode 100644 index 7a89129d487da..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-days-in-year.js +++ /dev/null @@ -1,44 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-days-in-year test - in V8's mjsunit test calendar-days-in-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(1995, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(1996, 7, 15)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDate(1997, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(1998, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(1999, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(2000, 7, 15)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDate(2001, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(2002, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(2003, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDate(2004, 7, 15)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDate(2005, 7, 15)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).daysInYear, 366); -assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2020-03-18').daysInYear, 366); -assert.sameValue(Temporal.PlainDate.from('2021-03-18').daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2022-03-18').daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2023-03-18').daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2024-03-18').daysInYear, 366); -assert.sameValue(Temporal.PlainDate.from('2025-03-18').daysInYear, 365); -assert.sameValue(Temporal.PlainDate.from('2026-03-18').daysInYear, 365); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-in-leap-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-in-leap-year.js deleted file mode 100644 index 5d356f58aab18..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-in-leap-year.js +++ /dev/null @@ -1,44 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-in-leap-year test - in V8's mjsunit test calendar-in-leap-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(1995, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(1996, 7, 15)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDate(1997, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(1998, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(1999, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(2000, 7, 15)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDate(2001, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(2002, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(2003, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDate(2004, 7, 15)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDate(2005, 7, 15)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(1995, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(1996, 8, 23, 5, 30, 13)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(1998, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(1999, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(2000, 8, 23, 5, 30, 13)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDateTime(2001, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(2002, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(2003, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue((new Temporal.PlainDateTime(2004, 8, 23, 5, 30, 13)).inLeapYear, true); -assert.sameValue((new Temporal.PlainDateTime(2005, 8, 23, 5, 30, 13)).inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2020-03-18').inLeapYear, true); -assert.sameValue(Temporal.PlainDate.from('2021-03-18').inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2022-03-18').inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2023-03-18').inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2024-03-18').inLeapYear, true); -assert.sameValue(Temporal.PlainDate.from('2025-03-18').inLeapYear, false); -assert.sameValue(Temporal.PlainDate.from('2026-03-18').inLeapYear, false); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-month-code.js b/js/src/tests/test262/staging/Temporal/v8/calendar-month-code.js deleted file mode 100644 index 93b02070f6f3a..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-month-code.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-month-code test - in V8's mjsunit test calendar-month-code.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).monthCode, 'M07'); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).monthCode, 'M08'); -assert.sameValue((new Temporal.PlainYearMonth(1999, 6)).monthCode, 'M06'); -assert.sameValue((new Temporal.PlainMonthDay(2, 6)).monthCode, 'M02'); -assert.sameValue(Temporal.PlainDate.from('2019-03-15').monthCode, 'M03'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-month-day-from-fields.js b/js/src/tests/test262/staging/Temporal/v8/calendar-month-day-from-fields.js deleted file mode 100644 index 2dc5d3390b3ce..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-month-day-from-fields.js +++ /dev/null @@ -1,425 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-month-day-from-fields test - in V8's mjsunit test calendar-month-day-from-fields.js -features: [Temporal] ----*/ - -assert.throws(TypeError, () => Temporal.PlainMonthDay.from()); -[ - undefined, - true, - false, - 123, - 456n, - Symbol(), -].forEach(function (fields) { - assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields)); - assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields, undefined)); - assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields, { overflow: 'constrain' })); - assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields, { overflow: 'reject' })); -}); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string')); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', undefined)); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', { overflow: 'constrain' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', { overflow: 'reject' })); -assert.sameValue(Temporal.PlainMonthDay.from({ - month: 1, - day: 17 -}).toJSON(), '01-17'); -assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - day: 17 -})); -assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'm1', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M1', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'm01', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - monthCode: 'M11', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M00', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M19', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M99', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M13', - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: -1, - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: -Infinity, - day: 17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: -17 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: -Infinity -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 0 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 1, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 2, - day: 29 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 6, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 9, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 0, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 13, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M12', - day: 0 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M12', - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M01', - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M06', - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M09', - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M00', - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M13', - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 0 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 0, - day: 3 -})); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: 13 -}, { overflow: 'invalid' })); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: 15 -}).toJSON(), '07-15'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: 3 -}).toJSON(), '07-03'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 31 -}).toJSON(), '12-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M07', - day: 15 -}).toJSON(), '07-15'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M07', - day: 3 -}).toJSON(), '07-03'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M12', - day: 31 -}).toJSON(), '12-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M02', - day: 29 -}).toJSON(), '02-28'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 1, - day: 133 -}).toJSON(), '01-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 2, - day: 133 -}).toJSON(), '02-28'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 3, - day: 9033 -}).toJSON(), '03-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 4, - day: 50 -}).toJSON(), '04-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 5, - day: 77 -}).toJSON(), '05-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 6, - day: 33 -}).toJSON(), '06-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: 33 -}).toJSON(), '07-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 8, - day: 300 -}).toJSON(), '08-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 9, - day: 400 -}).toJSON(), '09-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 10, - day: 400 -}).toJSON(), '10-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 11, - day: 400 -}).toJSON(), '11-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 500 -}).toJSON(), '12-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 13, - day: 500 -}).toJSON(), '12-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - month: 999999, - day: 500 -}).toJSON(), '12-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M01', - day: 133 -}).toJSON(), '01-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M02', - day: 133 -}).toJSON(), '02-28'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M03', - day: 9033 -}).toJSON(), '03-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M04', - day: 50 -}).toJSON(), '04-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M05', - day: 77 -}).toJSON(), '05-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M06', - day: 33 -}).toJSON(), '06-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M07', - day: 33 -}).toJSON(), '07-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M08', - day: 300 -}).toJSON(), '08-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M09', - day: 400 -}).toJSON(), '09-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M10', - day: 400 -}).toJSON(), '10-31'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M11', - day: 400 -}).toJSON(), '11-30'); -assert.sameValue(Temporal.PlainMonthDay.from({ - year: 2021, - monthCode: 'M12', - day: 500 -}).toJSON(), '12-31'); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 1, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 2, - day: 29 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 3, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 4, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 5, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 6, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 7, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 8, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 9, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 10, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 11, - day: 31 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 12, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ - year: 2021, - month: 13, - day: 5 -}, { overflow: 'reject' })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-month.js b/js/src/tests/test262/staging/Temporal/v8/calendar-month.js deleted file mode 100644 index eff2a392c7ebd..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-month.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-month test - in V8's mjsunit test calendar-month.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).month, 7); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).month, 8); -assert.sameValue((new Temporal.PlainYearMonth(1999, 6)).month, 6); -assert.sameValue(Temporal.PlainDate.from('2019-03-15').month, 3); -assert.sameValue((new Temporal.PlainMonthDay(3, 16)).month, undefined); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-months-in-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-months-in-year.js deleted file mode 100644 index 10cb76a829112..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-months-in-year.js +++ /dev/null @@ -1,20 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-months-in-year test - in V8's mjsunit test calendar-months-in-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).monthsInYear, 12); -assert.sameValue((new Temporal.PlainDate(1234, 7, 15)).monthsInYear, 12); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).monthsInYear, 12); -assert.sameValue((new Temporal.PlainDateTime(1234, 8, 23, 5, 30, 13)).monthsInYear, 12); -assert.sameValue(Temporal.PlainDate.from('2019-03-18').monthsInYear, 12); -assert.sameValue(Temporal.PlainDate.from('1234-03-18').monthsInYear, 12); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-week-of-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-week-of-year.js deleted file mode 100644 index 12fdbc82cf29c..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-week-of-year.js +++ /dev/null @@ -1,52 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-week-of-year test - in V8's mjsunit test calendar-week-of-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(1977, 1, 1)).weekOfYear, 53); -assert.sameValue((new Temporal.PlainDate(1977, 1, 2)).weekOfYear, 53); -assert.sameValue((new Temporal.PlainDate(1977, 12, 31)).weekOfYear, 52); -assert.sameValue((new Temporal.PlainDate(1978, 1, 1)).weekOfYear, 52); -assert.sameValue((new Temporal.PlainDate(1978, 1, 2)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1978, 12, 31)).weekOfYear, 52); -assert.sameValue((new Temporal.PlainDate(1979, 1, 1)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1979, 12, 30)).weekOfYear, 52); -assert.sameValue((new Temporal.PlainDate(1979, 12, 31)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1980, 1, 1)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1980, 12, 28)).weekOfYear, 52); -assert.sameValue((new Temporal.PlainDate(1980, 12, 29)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1980, 12, 30)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1980, 12, 31)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1981, 1, 1)).weekOfYear, 1); -assert.sameValue((new Temporal.PlainDate(1981, 12, 31)).weekOfYear, 53); -assert.sameValue((new Temporal.PlainDate(1982, 1, 1)).weekOfYear, 53); -assert.sameValue((new Temporal.PlainDate(1982, 1, 2)).weekOfYear, 53); -assert.sameValue((new Temporal.PlainDate(1982, 1, 3)).weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1977-01-01').weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1977-01-02').weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1977-12-31').weekOfYear, 52); -assert.sameValue(Temporal.PlainDate.from('1978-01-01').weekOfYear, 52); -assert.sameValue(Temporal.PlainDate.from('1978-01-02').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1978-12-31').weekOfYear, 52); -assert.sameValue(Temporal.PlainDate.from('1979-01-01').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1979-12-30').weekOfYear, 52); -assert.sameValue(Temporal.PlainDate.from('1979-12-31').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1980-01-01').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1980-12-28').weekOfYear, 52); -assert.sameValue(Temporal.PlainDate.from('1980-12-29').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1980-12-30').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1980-12-31').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1981-01-01').weekOfYear, 1); -assert.sameValue(Temporal.PlainDate.from('1981-12-31').weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1982-01-01').weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1982-01-02').weekOfYear, 53); -assert.sameValue(Temporal.PlainDate.from('1982-01-03').weekOfYear, 53); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-year-month-from-fields.js b/js/src/tests/test262/staging/Temporal/v8/calendar-year-month-from-fields.js deleted file mode 100644 index 224e8a84b2404..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-year-month-from-fields.js +++ /dev/null @@ -1,229 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-year-month-from-fields test - in V8's mjsunit test calendar-year-month-from-fields.js -features: [Temporal] ----*/ - -assert.throws(TypeError, () => Temporal.PlainYearMonth.from()); -[ - undefined, - true, - false, - 123, - 456n, - Symbol(), -].forEach(function (fields) { - assert.throws(TypeError, () => Temporal.PlainYearMonth.from(fields)); - assert.throws(TypeError, () => Temporal.PlainYearMonth.from(fields, undefined)); - assert.throws(TypeError, () => Temporal.PlainYearMonth.from(fields, { overflow: 'constrain' })); - assert.throws(TypeError, () => Temporal.PlainYearMonth.from(fields, { overflow: 'reject' })); -}); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string')); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', undefined)); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', { overflow: 'constrain' })); -assert.throws(RangeError, () => Temporal.PlainMonthDay.from('string', { overflow: 'reject' })); -assert.throws(TypeError, () => Temporal.PlainYearMonth.from({ month: 1 })); -assert.throws(TypeError, () => Temporal.PlainYearMonth.from({ year: 2021 })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'm1' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M1' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'm01' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 12, - monthCode: 'M11' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M00' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M19' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M99' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M13' -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: -1 -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: -Infinity -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 0, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 13, - day: 5 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M00' -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M13' -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 0 -})); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 7 -}, { overflow: 'invalid' })); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 7 -}).toJSON(), '2021-07'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 12 -}).toJSON(), '2021-12'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M07' -}).toJSON(), '2021-07'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M12' -}).toJSON(), '2021-12'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 1 -}).toJSON(), '2021-01'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 2 -}).toJSON(), '2021-02'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 3 -}).toJSON(), '2021-03'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 4 -}).toJSON(), '2021-04'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 5 -}).toJSON(), '2021-05'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 6 -}).toJSON(), '2021-06'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 7 -}).toJSON(), '2021-07'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 8 -}).toJSON(), '2021-08'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 9 -}).toJSON(), '2021-09'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 10 -}).toJSON(), '2021-10'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 11 -}).toJSON(), '2021-11'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 12 -}).toJSON(), '2021-12'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 13 -}).toJSON(), '2021-12'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - month: 999999 -}).toJSON(), '2021-12'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M01' -}).toJSON(), '2021-01'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M02' -}).toJSON(), '2021-02'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M03' -}).toJSON(), '2021-03'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M04' -}).toJSON(), '2021-04'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M05' -}).toJSON(), '2021-05'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M06' -}).toJSON(), '2021-06'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M07' -}).toJSON(), '2021-07'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M08' -}).toJSON(), '2021-08'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M09' -}).toJSON(), '2021-09'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M10' -}).toJSON(), '2021-10'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M11' -}).toJSON(), '2021-11'); -assert.sameValue(Temporal.PlainYearMonth.from({ - year: 2021, - monthCode: 'M12' -}).toJSON(), '2021-12'); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 13 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ - year: 2021, - month: 9995 -}, { overflow: 'reject' })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/calendar-year.js b/js/src/tests/test262/staging/Temporal/v8/calendar-year.js deleted file mode 100644 index eff09782cbf82..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/calendar-year.js +++ /dev/null @@ -1,18 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from calendar-year test - in V8's mjsunit test calendar-year.js -features: [Temporal] ----*/ - -assert.sameValue((new Temporal.PlainDate(2021, 7, 15)).year, 2021); -assert.sameValue((new Temporal.PlainDateTime(1997, 8, 23, 5, 30, 13)).year, 1997); -assert.sameValue((new Temporal.PlainYearMonth(1999, 6)).year, 1999); -assert.sameValue(Temporal.PlainDate.from('2019-03-15').year, 2019); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-abs.js b/js/src/tests/test262/staging/Temporal/v8/duration-abs.js deleted file mode 100644 index f70f71ecaf583..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-abs.js +++ /dev/null @@ -1,23 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-abs test - in V8's mjsunit test duration-abs.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(); -TemporalHelpers.assertDuration(d1.abs(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d2.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -let d3 = new Temporal.Duration(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -TemporalHelpers.assertDuration(d3.abs(), 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -TemporalHelpers.assertDuration(d4.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-add.js b/js/src/tests/test262/staging/Temporal/v8/duration-add.js deleted file mode 100644 index 117666a8d2872..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-add.js +++ /dev/null @@ -1,29 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-add test - in V8's mjsunit test duration-add.js -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(); -let badDur = { add: d1.add }; -assert.throws(TypeError, () => badDur.add(d1)); -let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -assert.throws(RangeError, () => d2.add(d1)); -assert.throws(RangeError, () => d1.add(d2)); -assert.throws(RangeError, () => d2.add(d2)); -let d3 = new Temporal.Duration(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -assert.throws(RangeError, () => d3.add(d3)); -let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -assert.throws(RangeError, () => d4.add(d1)); -assert.throws(RangeError, () => d1.add(d4)); -assert.throws(RangeError, () => d4.add(d4)); -assert.throws(RangeError, () => d2.add(d4)); -assert.throws(RangeError, () => d4.add(d2)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-constructor.js b/js/src/tests/test262/staging/Temporal/v8/duration-constructor.js deleted file mode 100644 index b8ae34ba3d349..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-constructor.js +++ /dev/null @@ -1,52 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-constructor test - in V8's mjsunit test duration-constructor.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(); -TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -let d3 = new Temporal.Duration(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -TemporalHelpers.assertDuration(d3, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -TemporalHelpers.assertDuration(d4, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -assert.throws(TypeError, () => Temporal.Duration()); -TemporalHelpers.assertDuration(new Temporal.Duration(undefined, 234, true, false, '567'), 0, 234, 1, 0, 567, 0, 0, 0, 0, 0); -assert.throws(TypeError, () => new Temporal.Duration(Symbol(123))); -assert.throws(TypeError, () => new Temporal.Duration(123n)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, 5, 6, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, 5, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, 4, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, 3, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, 2, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -5, -6, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -5, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -4, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -3, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -2, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-1, -Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(-Infinity)); -assert.throws(RangeError, () => new Temporal.Duration(1, -2)); -assert.throws(RangeError, () => new Temporal.Duration(1, 0, -2)); -assert.throws(RangeError, () => new Temporal.Duration(-1, 0, 0, 3)); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 1, -1)); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -1, 1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-from.js b/js/src/tests/test262/staging/Temporal/v8/duration-from.js deleted file mode 100644 index 6ae7928c4d8f2..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-from.js +++ /dev/null @@ -1,146 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-from test - in V8's mjsunit test duration-from.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -let d2 = Temporal.Duration.from(d1); -TemporalHelpers.assertDuration(d2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -assert.notSameValue(d2, d1); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT0S'), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1Y'), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2M'), 0, 2, 0, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3W'), 0, 0, 3, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P4D'), 0, 0, 0, 4, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT5H'), 0, 0, 0, 0, 5, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT6M'), 0, 0, 0, 0, 0, 6, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT7S'), 0, 0, 0, 0, 0, 0, 7, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT0.008S'), 0, 0, 0, 0, 0, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT0.000009S'), 0, 0, 0, 0, 0, 0, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT0.000000001S'), 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1Y2M3W4DT5H6M7.008009001S'), 1, 2, 3, 4, 5, 6, 7, 8, 9, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P111111111Y222222222M333333333W444444444D' + 'T555555555H666666666M777777777.987654321S'), 111111111, 222222222, 333333333, 444444444, 555555555, 666666666, 777777777, 987, 654, 321); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1Y3WT5H7.000009001S'), 1, 0, 3, 0, 5, 0, 7, 0, 9, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2M4DT6M0.008000001S'), 0, 2, 0, 4, 0, 6, 0, 8, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1Y4DT7.000000001S'), 1, 0, 0, 4, 0, 0, 7, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2MT5H0.008S'), 0, 2, 0, 0, 5, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3WT6M0.000009S'), 0, 0, 3, 0, 0, 6, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1YT5H0.000009000S'), 1, 0, 0, 0, 5, 0, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2MT6M0.000000001S'), 0, 2, 0, 0, 0, 6, 0, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3WT7S'), 0, 0, 3, 0, 0, 0, 7, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P4DT0.008S'), 0, 0, 0, 4, 0, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1YT5H0.000000001S'), 1, 0, 0, 0, 5, 0, 0, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2MT6M'), 0, 2, 0, 0, 0, 6, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3WT7S'), 0, 0, 3, 0, 0, 0, 7, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P4DT0.008S'), 0, 0, 0, 4, 0, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT5H0.000009S'), 0, 0, 0, 0, 5, 0, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1YT6M'), 1, 0, 0, 0, 0, 6, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2MT7S'), 0, 2, 0, 0, 0, 0, 7, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3WT0.008S'), 0, 0, 3, 0, 0, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P4DT0.000009S'), 0, 0, 0, 4, 0, 0, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT5H0.000000001S'), 0, 0, 0, 0, 5, 0, 0, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('P1YT7S'), 1, 0, 0, 0, 0, 0, 7, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P2MT0.008S'), 0, 2, 0, 0, 0, 0, 0, 8, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P3WT0.000009S'), 0, 0, 3, 0, 0, 0, 0, 0, 9, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('P4DT0.000000001S'), 0, 0, 0, 4, 0, 0, 0, 0, 0, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT5H'), 0, 0, 0, 0, 5, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('-P1Y2M3W4DT5H6M7.008009001S'), -1, -2, -3, -4, -5, -6, -7, -8, -9, -1); -TemporalHelpers.assertDuration(Temporal.Duration.from('-P111111111Y222222222M333333333W444444444D' + 'T555555555H666666666M777777777.987654321S'), -111111111, -222222222, -333333333, -444444444, -555555555, -666666666, -777777777, -987, -654, -321); -TemporalHelpers.assertDuration(Temporal.Duration.from('+P1Y2M3W4DT5H6M7.008009001S'), 1, 2, 3, 4, 5, 6, 7, 8, 9, 1); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.5H'), 0, 0, 0, 0, 2, 30, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.25H'), 0, 0, 0, 0, 2, 15, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.05H'), 0, 0, 0, 0, 2, 3, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.005H'), 0, 0, 0, 0, 2, 0, 18, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.505H'), 0, 0, 0, 0, 2, 30, 18, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2.0025H'), 0, 0, 0, 0, 2, 0, 9, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.5M'), 0, 0, 0, 0, 0, 3, 30, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.25M'), 0, 0, 0, 0, 0, 3, 15, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.125M'), 0, 0, 0, 0, 0, 3, 7, 500, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.025M'), 0, 0, 0, 0, 0, 3, 1, 500, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.01M'), 0, 0, 0, 0, 0, 3, 0, 600, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.005M'), 0, 0, 0, 0, 0, 3, 0, 300, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.001M'), 0, 0, 0, 0, 0, 3, 0, 60, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3.006M'), 0, 0, 0, 0, 0, 3, 0, 360, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,5H'), 0, 0, 0, 0, 2, 30, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,25H'), 0, 0, 0, 0, 2, 15, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,05H'), 0, 0, 0, 0, 2, 3, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,005H'), 0, 0, 0, 0, 2, 0, 18, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,505H'), 0, 0, 0, 0, 2, 30, 18, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT2,0025H'), 0, 0, 0, 0, 2, 0, 9, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,5M'), 0, 0, 0, 0, 0, 3, 30, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,25M'), 0, 0, 0, 0, 0, 3, 15, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,125M'), 0, 0, 0, 0, 0, 3, 7, 500, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,025M'), 0, 0, 0, 0, 0, 3, 1, 500, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,01M'), 0, 0, 0, 0, 0, 3, 0, 600, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,005M'), 0, 0, 0, 0, 0, 3, 0, 300, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,001M'), 0, 0, 0, 0, 0, 3, 0, 60, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from('PT3,006M'), 0, 0, 0, 0, 0, 3, 0, 360, 0, 0); -assert.throws(RangeError, () => Temporal.Duration.from('P2H')); -assert.throws(RangeError, () => Temporal.Duration.from('P2.5M')); -assert.throws(RangeError, () => Temporal.Duration.from('P2,5M')); -assert.throws(RangeError, () => Temporal.Duration.from('P2S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2.H3M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2,H3M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2.H3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2,H3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2.H0.5M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2,H0,5M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2.H0.5S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2,H0,5S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2H3.2M3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2H3,2M3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2H3.2M0.3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT2H3,2M0,3S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT.1H')); -assert.throws(RangeError, () => Temporal.Duration.from('PT,1H')); -assert.throws(RangeError, () => Temporal.Duration.from('PT.1M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT,1M')); -assert.throws(RangeError, () => Temporal.Duration.from('PT.1S')); -assert.throws(RangeError, () => Temporal.Duration.from('PT,1S')); -TemporalHelpers.assertDuration(Temporal.Duration.from({ - years: 0, - months: 0, - weeks: 0, - days: 0, - hours: 0, - minutes: 0, - seconds: 0, - milliseconds: 0, - microseconds: 0, - nanoseconds: 0 -}), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(Temporal.Duration.from({ - years: 1, - months: 2, - weeks: 3, - days: 4, - hours: 5, - minutes: 6, - seconds: 7, - milliseconds: 8, - microseconds: 9, - nanoseconds: 10 -}), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(Temporal.Duration.from({ - years: -1, - months: -2, - weeks: -3, - days: -4, - hours: -5, - minutes: -6, - seconds: -7, - milliseconds: -8, - microseconds: -9, - nanoseconds: -10 -}), -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-negated.js b/js/src/tests/test262/staging/Temporal/v8/duration-negated.js deleted file mode 100644 index 5abe59eefa57f..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-negated.js +++ /dev/null @@ -1,25 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-negated test - in V8's mjsunit test duration-negated.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(); -TemporalHelpers.assertDuration(d1.negated(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d2.negated(), -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -let d3 = new Temporal.Duration(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -TemporalHelpers.assertDuration(d3.negated(), -100000, -200000, -300000, -400000, -500000, -600000, -700000, -800000, -900000, -1000000); -let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -TemporalHelpers.assertDuration(d4.negated(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -let d5 = new Temporal.Duration(-100000, -200000, -300000, -400000, -500000, -600000, -700000, -800000, -900000, -1000000); -TemporalHelpers.assertDuration(d5.negated(), 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-to-json-boundary-cases.js b/js/src/tests/test262/staging/Temporal/v8/duration-to-json-boundary-cases.js deleted file mode 100644 index 64da702aa467e..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-to-json-boundary-cases.js +++ /dev/null @@ -1,69 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2022 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-to-json-boundary-cases test - in V8's mjsunit test duration-to-json-boundary-cases.js -features: [Temporal] ----*/ - -let MAX_UINT32 = Math.pow(2, 32); -assert.throws(RangeError, () => new Temporal.Duration(MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(-MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(0, MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(0, -MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, -MAX_UINT32)); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, Number.MAX_SAFE_INTEGER / 86400 + 1)); -assert.sameValue(new Temporal.Duration(0, 0, 0, Math.floor(Number.MAX_SAFE_INTEGER / 86400)).toJSON(), 'P' + Math.floor(Number.MAX_SAFE_INTEGER / 86400) + 'D'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -(Number.MAX_SAFE_INTEGER / 86400 + 1))); -assert.sameValue(new Temporal.Duration(0, 0, 0, -Math.floor(Number.MAX_SAFE_INTEGER / 86400)).toJSON(), '-P' + Math.floor(Number.MAX_SAFE_INTEGER / 86400) + 'D'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, Number.MAX_SAFE_INTEGER / 3600 + 1)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, Math.floor(Number.MAX_SAFE_INTEGER / 3600)).toJSON(), 'PT' + Math.floor(Number.MAX_SAFE_INTEGER / 3600) + 'H'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -(Number.MAX_SAFE_INTEGER / 3600 + 1))); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -Math.floor(Number.MAX_SAFE_INTEGER / 3600)).toJSON(), '-PT' + Math.floor(Number.MAX_SAFE_INTEGER / 3600) + 'H'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER / 60 + 1)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, Math.floor(Number.MAX_SAFE_INTEGER / 60)).toJSON(), 'PT' + Math.floor(Number.MAX_SAFE_INTEGER / 60) + 'M'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -(Number.MAX_SAFE_INTEGER / 60 + 1))); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, -Math.floor(Number.MAX_SAFE_INTEGER / 60)).toJSON(), '-PT' + Math.floor(Number.MAX_SAFE_INTEGER / 60) + 'M'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER + 1)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + 'S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -(Number.MAX_SAFE_INTEGER + 1))); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER).toJSON(), '-PT' + Number.MAX_SAFE_INTEGER + 'S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 999).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + '.999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -999).toJSON(), '-PT' + Number.MAX_SAFE_INTEGER + '.999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 999, 1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 999, 999).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + '.999999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -999, -1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -999, -999).toJSON(), '-PT' + Number.MAX_SAFE_INTEGER + '.999999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 999, 999, 1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER, 999, 999, 999).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + '.999999999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -999, -999, -1000)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -Number.MAX_SAFE_INTEGER, -999, -999, -999).toJSON(), '-PT' + Number.MAX_SAFE_INTEGER + '.999999999S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000) + 1, Number.MAX_SAFE_INTEGER, 0, 0)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000), Number.MAX_SAFE_INTEGER, 0, 0).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + '.' + Number.MAX_SAFE_INTEGER % 1000 + 'S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -(Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000) + 1), -Number.MAX_SAFE_INTEGER, 0, 0)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -(Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000)), -Number.MAX_SAFE_INTEGER, 0, 0).toJSON(), '-PT' + Number.MAX_SAFE_INTEGER + '.' + Number.MAX_SAFE_INTEGER % 1000 + 'S'); -assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000000) + 1, 0, Number.MAX_SAFE_INTEGER, 0)); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, Number.MAX_SAFE_INTEGER - Math.floor(Number.MAX_SAFE_INTEGER / 1000000), 0, Number.MAX_SAFE_INTEGER, 0).toJSON(), 'PT' + Number.MAX_SAFE_INTEGER + '.' + Number.MAX_SAFE_INTEGER % 1000000 + 'S'); -assert.sameValue(new Temporal.Duration(MAX_UINT32 - 1).toJSON(), 'P' + (MAX_UINT32 - 1) + 'Y'); -assert.sameValue(new Temporal.Duration(-(MAX_UINT32 - 1)).toJSON(), '-P' + (MAX_UINT32 - 1) + 'Y'); -assert.sameValue(new Temporal.Duration(0, MAX_UINT32 - 1).toJSON(), 'P' + (MAX_UINT32 - 1) + 'M'); -assert.sameValue(new Temporal.Duration(0, -(MAX_UINT32 - 1)).toJSON(), '-P' + (MAX_UINT32 - 1) + 'M'); -assert.sameValue(new Temporal.Duration(0, 0, MAX_UINT32 - 1).toJSON(), 'P' + (MAX_UINT32 - 1) + 'W'); -assert.sameValue(new Temporal.Duration(0, 0, -(MAX_UINT32 - 1)).toJSON(), '-P' + (MAX_UINT32 - 1) + 'W'); -assert.sameValue(new Temporal.Duration(0, 0, 0, MAX_UINT32 - 1).toJSON(), 'P' + (MAX_UINT32 - 1) + 'D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, -(MAX_UINT32 - 1)).toJSON(), '-P' + (MAX_UINT32 - 1) + 'D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, MAX_UINT32 - 1).toJSON(), 'PT' + (MAX_UINT32 - 1) + 'H'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -(MAX_UINT32 - 1)).toJSON(), '-PT' + (MAX_UINT32 - 1) + 'H'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, MAX_UINT32 - 1).toJSON(), 'PT' + (MAX_UINT32 - 1) + 'M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, -(MAX_UINT32 - 1)).toJSON(), '-PT' + (MAX_UINT32 - 1) + 'M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, MAX_UINT32 - 1).toJSON(), 'PT' + (MAX_UINT32 - 1) + 'S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -(MAX_UINT32 - 1)).toJSON(), '-PT' + (MAX_UINT32 - 1) + 'S'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-to-json.js b/js/src/tests/test262/staging/Temporal/v8/duration-to-json.js deleted file mode 100644 index 5a950c927e44b..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-to-json.js +++ /dev/null @@ -1,121 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-to-json test - in V8's mjsunit test duration-to-json.js -features: [Temporal] ----*/ - -assert.sameValue(new Temporal.Duration().toJSON(), 'PT0S'); -assert.sameValue(new Temporal.Duration(1).toJSON(), 'P1Y'); -assert.sameValue(new Temporal.Duration(-1).toJSON(), '-P1Y'); -assert.sameValue(new Temporal.Duration(1234567890).toJSON(), 'P1234567890Y'); -assert.sameValue(new Temporal.Duration(-1234567890).toJSON(), '-P1234567890Y'); -assert.sameValue(new Temporal.Duration(1, 2).toJSON(), 'P1Y2M'); -assert.sameValue(new Temporal.Duration(-1, -2).toJSON(), '-P1Y2M'); -assert.sameValue(new Temporal.Duration(0, 2).toJSON(), 'P2M'); -assert.sameValue(new Temporal.Duration(0, -2).toJSON(), '-P2M'); -assert.sameValue(new Temporal.Duration(0, 1234567890).toJSON(), 'P1234567890M'); -assert.sameValue(new Temporal.Duration(0, -1234567890).toJSON(), '-P1234567890M'); -assert.sameValue(new Temporal.Duration(1, 2, 3).toJSON(), 'P1Y2M3W'); -assert.sameValue(new Temporal.Duration(-1, -2, -3).toJSON(), '-P1Y2M3W'); -assert.sameValue(new Temporal.Duration(0, 0, 3).toJSON(), 'P3W'); -assert.sameValue(new Temporal.Duration(0, 0, -3).toJSON(), '-P3W'); -assert.sameValue(new Temporal.Duration(1, 0, 3).toJSON(), 'P1Y3W'); -assert.sameValue(new Temporal.Duration(-1, 0, -3).toJSON(), '-P1Y3W'); -assert.sameValue(new Temporal.Duration(0, 2, 3).toJSON(), 'P2M3W'); -assert.sameValue(new Temporal.Duration(0, -2, -3).toJSON(), '-P2M3W'); -assert.sameValue(new Temporal.Duration(0, 0, 1234567890).toJSON(), 'P1234567890W'); -assert.sameValue(new Temporal.Duration(0, 0, -1234567890).toJSON(), '-P1234567890W'); -assert.sameValue(new Temporal.Duration(1, 2, 3, 4).toJSON(), 'P1Y2M3W4D'); -assert.sameValue(new Temporal.Duration(-1, -2, -3, -4).toJSON(), '-P1Y2M3W4D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 1234567890).toJSON(), 'P1234567890D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, -1234567890).toJSON(), '-P1234567890D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 4).toJSON(), 'P4D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, -4).toJSON(), '-P4D'); -assert.sameValue(new Temporal.Duration(1, 0, 0, 4).toJSON(), 'P1Y4D'); -assert.sameValue(new Temporal.Duration(-1, 0, 0, -4).toJSON(), '-P1Y4D'); -assert.sameValue(new Temporal.Duration(0, 2, 0, 4).toJSON(), 'P2M4D'); -assert.sameValue(new Temporal.Duration(0, -2, 0, -4).toJSON(), '-P2M4D'); -assert.sameValue(new Temporal.Duration(0, 0, 3, 4).toJSON(), 'P3W4D'); -assert.sameValue(new Temporal.Duration(0, 0, -3, -4).toJSON(), '-P3W4D'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 5).toJSON(), 'PT5H'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -5).toJSON(), '-PT5H'); -assert.sameValue(new Temporal.Duration(1, 0, 0, 0, 5).toJSON(), 'P1YT5H'); -assert.sameValue(new Temporal.Duration(-1, 0, 0, 0, -5).toJSON(), '-P1YT5H'); -assert.sameValue(new Temporal.Duration(0, 2, 0, 0, 5).toJSON(), 'P2MT5H'); -assert.sameValue(new Temporal.Duration(0, -2, 0, 0, -5).toJSON(), '-P2MT5H'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 6).toJSON(), 'PT6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, -6).toJSON(), '-PT6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 5, 6).toJSON(), 'PT5H6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -5, -6).toJSON(), '-PT5H6M'); -assert.sameValue(new Temporal.Duration(0, 0, 3, 0, 0, 6).toJSON(), 'P3WT6M'); -assert.sameValue(new Temporal.Duration(0, 0, -3, 0, 0, -6).toJSON(), '-P3WT6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 4, 0, 6).toJSON(), 'P4DT6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, -4, 0, -6).toJSON(), '-P4DT6M'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 7).toJSON(), 'PT7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -7).toJSON(), '-PT7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 5, 0, 7).toJSON(), 'PT5H7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -5, 0, -7).toJSON(), '-PT5H7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 6, 7).toJSON(), 'PT6M7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, -6, -7).toJSON(), '-PT6M7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 5, 6, 7).toJSON(), 'PT5H6M7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, -5, -6, -7).toJSON(), '-PT5H6M7S'); -assert.sameValue(new Temporal.Duration(1, 0, 0, 0, 5, 6, 7).toJSON(), 'P1YT5H6M7S'); -assert.sameValue(new Temporal.Duration(-1, 0, 0, 0, -5, -6, -7).toJSON(), '-P1YT5H6M7S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 8).toJSON(), 'PT0.008S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -8).toJSON(), '-PT0.008S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 80).toJSON(), 'PT0.08S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -80).toJSON(), '-PT0.08S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 87).toJSON(), 'PT0.087S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -87).toJSON(), '-PT0.087S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876).toJSON(), 'PT0.876S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876).toJSON(), '-PT0.876S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876543).toJSON(), 'PT876.543S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876543).toJSON(), '-PT876.543S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 9).toJSON(), 'PT0.000009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -9).toJSON(), '-PT0.000009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 90).toJSON(), 'PT0.00009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -90).toJSON(), '-PT0.00009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 98).toJSON(), 'PT0.000098S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -98).toJSON(), '-PT0.000098S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 900).toJSON(), 'PT0.0009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -900).toJSON(), '-PT0.0009S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987).toJSON(), 'PT0.000987S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987).toJSON(), '-PT0.000987S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654).toJSON(), 'PT0.987654S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654).toJSON(), '-PT0.987654S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654321).toJSON(), 'PT987.654321S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654321).toJSON(), '-PT987.654321S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1).toJSON(), 'PT0.000000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1).toJSON(), '-PT0.000000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 10).toJSON(), 'PT0.00000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -10).toJSON(), '-PT0.00000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 12).toJSON(), 'PT0.000000012S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -12).toJSON(), '-PT0.000000012S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 100).toJSON(), 'PT0.0000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -100).toJSON(), '-PT0.0000001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123).toJSON(), 'PT0.000000123S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123).toJSON(), '-PT0.000000123S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456).toJSON(), 'PT0.000123456S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456).toJSON(), '-PT0.000123456S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456789).toJSON(), 'PT0.123456789S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456789).toJSON(), '-PT0.123456789S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1234567891).toJSON(), 'PT1.234567891S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1234567891).toJSON(), '-PT1.234567891S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1).toJSON(), 'PT4.003002001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1).toJSON(), '-PT4.003002001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90001).toJSON(), 'PT4.003092001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90001).toJSON(), '-PT4.003092001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90080001).toJSON(), 'PT4.093082001S'); -assert.sameValue(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90080001).toJSON(), '-PT4.093082001S'); -assert.sameValue(new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 1).toJSON(), 'P1Y2M3W4DT5H6M7.008009001S'); -assert.sameValue(new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -1).toJSON(), '-P1Y2M3W4DT5H6M7.008009001S'); -assert.sameValue(new Temporal.Duration(1234, 2345, 3456, 4567, 5678, 6789, 7890, 890, 901, 123).toJSON(), 'P1234Y2345M3456W4567DT5678H6789M7890.890901123S'); -assert.sameValue(new Temporal.Duration(-1234, -2345, -3456, -4567, -5678, -6789, -7890, -890, -901, -123).toJSON(), '-P1234Y2345M3456W4567DT5678H6789M7890.890901123S'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-value-of.js b/js/src/tests/test262/staging/Temporal/v8/duration-value-of.js deleted file mode 100644 index e1c6f6e5cd6a4..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-value-of.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-valueOf test - in V8's mjsunit test duration-valueOf.js -features: [Temporal] ----*/ - -let d1 = new Temporal.Duration(); -assert.throws(TypeError, () => d1.valueOf()); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/duration-with.js b/js/src/tests/test262/staging/Temporal/v8/duration-with.js deleted file mode 100644 index d3f5aaaa3d478..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/duration-with.js +++ /dev/null @@ -1,109 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from duration-with test - in V8's mjsunit test duration-with.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let like1 = { - years: 9, - months: 8, - weeks: 7, - days: 6, - hours: 5, - minutes: 4, - seconds: 3, - milliseconds: 2, - microseconds: 1, - nanoseconds: 10 -}; -let like2 = { - years: 9, - hours: 5 -}; -let like3 = { - months: 8, - minutes: 4 -}; -let like4 = { - weeks: 7, - seconds: 3 -}; -let like5 = { - days: 6, - milliseconds: 2 -}; -let like6 = { - microseconds: 987, - nanoseconds: 123 -}; -let like7 = { - years: -9, - months: -8, - weeks: -7, - days: -6, - hours: -5, - minutes: -4, - seconds: -3, - milliseconds: -2, - microseconds: -1, - nanoseconds: -10 -}; -let d1 = new Temporal.Duration(); -TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(d1.with(like1), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10); -TemporalHelpers.assertDuration(d1.with(like2), 9, 0, 0, 0, 5, 0, 0, 0, 0, 0); -TemporalHelpers.assertDuration(d1.with(like3), 0, 8, 0, 0, 0, 4, 0, 0, 0, 0); -TemporalHelpers.assertDuration(d1.with(like4), 0, 0, 7, 0, 0, 0, 3, 0, 0, 0); -TemporalHelpers.assertDuration(d1.with(like5), 0, 0, 0, 6, 0, 0, 0, 2, 0, 0); -TemporalHelpers.assertDuration(d1.with(like6), 0, 0, 0, 0, 0, 0, 0, 0, 987, 123); -TemporalHelpers.assertDuration(d1.with(like7), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10); -let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -TemporalHelpers.assertDuration(d2.with(like1), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10); -TemporalHelpers.assertDuration(d2.with(like7), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10); -assert.throws(RangeError, () => d2.with({ years: -1 })); -assert.throws(RangeError, () => d2.with({ months: -2 })); -assert.throws(RangeError, () => d2.with({ weeks: -3 })); -assert.throws(RangeError, () => d2.with({ days: -4 })); -assert.throws(RangeError, () => d2.with({ hours: -5 })); -assert.throws(RangeError, () => d2.with({ minutes: -6 })); -assert.throws(RangeError, () => d2.with({ seconds: -7 })); -assert.throws(RangeError, () => d2.with({ milliseconds: -8 })); -assert.throws(RangeError, () => d2.with({ microseconds: -9 })); -assert.throws(RangeError, () => d2.with({ nanoseconds: -10 })); -let d3 = new Temporal.Duration(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -TemporalHelpers.assertDuration(d3, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000); -TemporalHelpers.assertDuration(d3.with(like1), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10); -TemporalHelpers.assertDuration(d3.with(like7), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10); -let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -TemporalHelpers.assertDuration(d4, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); -TemporalHelpers.assertDuration(d4.with(like1), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10); -assert.throws(RangeError, () => d4.with({ years: 1 })); -assert.throws(RangeError, () => d4.with({ months: 2 })); -assert.throws(RangeError, () => d4.with({ weeks: 3 })); -assert.throws(RangeError, () => d4.with({ days: 4 })); -assert.throws(RangeError, () => d4.with({ hours: 5 })); -assert.throws(RangeError, () => d4.with({ minutes: 6 })); -assert.throws(RangeError, () => d4.with({ seconds: 7 })); -assert.throws(RangeError, () => d4.with({ milliseconds: 8 })); -assert.throws(RangeError, () => d4.with({ microseconds: 9 })); -assert.throws(RangeError, () => d4.with({ nanoseconds: 10 })); -assert.throws(TypeError, () => d1.with({ year: 1 })); -assert.throws(TypeError, () => d1.with({ month: 1 })); -assert.throws(TypeError, () => d1.with({ week: 1 })); -assert.throws(TypeError, () => d1.with({ day: 1 })); -assert.throws(TypeError, () => d1.with({ hour: 1 })); -assert.throws(TypeError, () => d1.with({ minute: 1 })); -assert.throws(TypeError, () => d1.with({ second: 1 })); -assert.throws(TypeError, () => d1.with({ millisecond: 1 })); -assert.throws(TypeError, () => d1.with({ microsecond: 1 })); -assert.throws(TypeError, () => d1.with({ nanosecond: 1 })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-add.js b/js/src/tests/test262/staging/Temporal/v8/instant-add.js deleted file mode 100644 index fa8938354938b..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-add.js +++ /dev/null @@ -1,35 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-add test - in V8's mjsunit test instant-add.js -features: [Temporal] ----*/ - -let i1 = new Temporal.Instant(50000n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 3, 2, 1)).epochNanoseconds, 3052001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1)).epochNanoseconds, BigInt(4 * 1000000000) + 3052001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, 5, 4, 3, 2, 1)).epochNanoseconds, BigInt(5 * 60 + 4) * 1000000000n + 3052001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 6, 5, 4, 3, 2, 1)).epochNanoseconds, BigInt(6 * 3600 + 5 * 60 + 4) * 1000000000n + 3052001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -3, -2, -1)).epochNanoseconds, -2952001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1)).epochNanoseconds, BigInt(-4 * 1000000000) - 2952001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, 0, -5, -4, -3, -2, -1)).epochNanoseconds, BigInt(5 * 60 + 4) * -1000000000n - 2952001n); -assert.sameValue(i1.add(new Temporal.Duration(0, 0, 0, 0, -6, -5, -4, -3, -2, -1)).epochNanoseconds, BigInt(6 * 3600 + 5 * 60 + 4) * -1000000000n - 2952001n); -let badInstant = { add: i1.add }; -assert.throws(TypeError, () => badInstant.add(new Temporal.Duration(0, 0, 0, 0, 5))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(1))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, 2))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, 0, 3))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, 0, 0, 4))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(-1))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, -2))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, 0, -3))); -assert.throws(RangeError, () => i1.add(new Temporal.Duration(0, 0, 0, -4))); -let i2 = new Temporal.Instant(86400n * 99999999999999999n); -assert.throws(RangeError, () => i2.add(new Temporal.Duration(0, 0, 0, 0, 999999999))); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-compare.js b/js/src/tests/test262/staging/Temporal/v8/instant-compare.js deleted file mode 100644 index aad985f5b7bc4..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-compare.js +++ /dev/null @@ -1,22 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-compare test - in V8's mjsunit test instant-compare.js -features: [Temporal] ----*/ - -let inst1 = new Temporal.Instant(1234567890123456789n); -let inst2 = new Temporal.Instant(1234567890123456000n); -let inst3 = new Temporal.Instant(1234567890123456000n); -assert.sameValue(Temporal.Instant.compare(inst2, inst3), 0); -assert.sameValue(Temporal.Instant.compare(inst1, inst2), 1); -assert.sameValue(Temporal.Instant.compare(inst3, inst1), -1); -assert.throws(RangeError, () => Temporal.Instant.compare(inst1, 'invalid iso8601 string')); -assert.throws(RangeError, () => Temporal.Instant.compare('invalid iso8601 string', inst1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-constructor.js b/js/src/tests/test262/staging/Temporal/v8/instant-constructor.js deleted file mode 100644 index df15cab984daf..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-constructor.js +++ /dev/null @@ -1,44 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-constructor test - in V8's mjsunit test instant-constructor.js -features: [Temporal] ----*/ - -let inst1 = new Temporal.Instant(1234567890123456789n); -assert.sameValue(inst1.epochNanoseconds, 1234567890123456789n); -assert.sameValue(inst1.epochMilliseconds, 1234567890123); -let inst2 = new Temporal.Instant(-1234567890123456789n); -assert.sameValue(inst2.epochNanoseconds, -1234567890123456789n); -assert.sameValue(inst2.epochMilliseconds, -1234567890124); -assert.throws(TypeError, () => Temporal.Instant(1234567890123456789n)); -assert.throws(TypeError, () => { - let inst = new Temporal.Instant(undefined); -}); -assert.throws(TypeError, () => { - let inst = new Temporal.Instant(null); -}); -assert.sameValue(new Temporal.Instant(true).epochNanoseconds, 1n); -assert.sameValue(new Temporal.Instant(false).epochNanoseconds, 0n); -assert.throws(TypeError, () => { - let inst = Temporal.Instant(12345); -}); -assert.sameValue(new Temporal.Instant('1234567890123456789').epochNanoseconds, 1234567890123456789n); -assert.throws(TypeError, () => { - let inst = new Temporal.Instant(Symbol(12345n)); -}); -assert.throws(RangeError, () => { - let inst = new Temporal.Instant(8640000000000000000001n); -}); -assert.throws(RangeError, () => { - let inst = new Temporal.Instant(-8640000000000000000001n); -}); -assert.sameValue(new Temporal.Instant(8640000000000000000000n).epochNanoseconds, 8640000000000000000000n); -assert.sameValue(new Temporal.Instant(-8640000000000000000000n).epochNanoseconds, -8640000000000000000000n); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-equals.js b/js/src/tests/test262/staging/Temporal/v8/instant-equals.js deleted file mode 100644 index 050a0ebeb9d71..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-equals.js +++ /dev/null @@ -1,21 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-equals test - in V8's mjsunit test instant-equals.js -features: [Temporal] ----*/ - -let inst1 = new Temporal.Instant(1234567890123456789n); -let inst2 = new Temporal.Instant(1234567890123456000n); -let inst3 = new Temporal.Instant(1234567890123456000n); -assert(!inst1.equals(inst2)); -assert(inst2.equals(inst3)); -let badInst = { equals: inst1.equals }; -assert.throws(TypeError, () => badInst.equals(inst1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-milliseconds.js b/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-milliseconds.js deleted file mode 100644 index 695100d4c6be7..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-milliseconds.js +++ /dev/null @@ -1,29 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-from-epoch-milliseconds test - in V8's mjsunit test instant-from-epoch-milliseconds.js -features: [Temporal] ----*/ - -let bigint_nano = 567890123456789000000n; -let milli = 567890123456789; -let bigint_milli = BigInt(milli); -let inst1 = new Temporal.Instant(bigint_nano); -assert.throws(TypeError, () => Temporal.Instant.fromEpochMilliseconds(bigint_milli)); -let inst2 = Temporal.Instant.fromEpochMilliseconds(milli); -assert(inst2.equals(inst1)); -let just_fit_neg = -8640000000000000; -let just_fit_pos = 8640000000000000; -let too_big = 8640000000000001; -let too_small = -8640000000000001; -assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(too_small)); -assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(too_big)); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(just_fit_neg).epochMilliseconds, just_fit_neg); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(just_fit_pos).epochMilliseconds, just_fit_pos); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-nanoseconds.js b/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-nanoseconds.js deleted file mode 100644 index bfa476f076bdf..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-from-epoch-nanoseconds.js +++ /dev/null @@ -1,30 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-from-epoch-nanoseconds test - in V8's mjsunit test instant-from-epoch-nanoseconds.js -features: [Temporal] ----*/ - -let bigint1 = 1234567890123456789n; -let inst1 = new Temporal.Instant(bigint1); -let inst2 = Temporal.Instant.fromEpochNanoseconds(bigint1); -assert(inst2.equals(inst1)); -let just_fit_neg_bigint = -8640000000000000000000n; -let just_fit_pos_bigint = 8640000000000000000000n; -let too_big_bigint = 8640000000000000000001n; -let too_small_bigint = -8640000000000000000001n; -assert.throws(RangeError, () => { - let inst = Temporal.Instant.fromEpochNanoseconds(too_small_bigint); -}); -assert.throws(RangeError, () => { - let inst = Temporal.Instant.fromEpochNanoseconds(too_big_bigint); -}); -assert.sameValue(Temporal.Instant.fromEpochNanoseconds(just_fit_neg_bigint).epochNanoseconds, just_fit_neg_bigint); -assert.sameValue(Temporal.Instant.fromEpochNanoseconds(just_fit_pos_bigint).epochNanoseconds, just_fit_pos_bigint); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-subtract.js b/js/src/tests/test262/staging/Temporal/v8/instant-subtract.js deleted file mode 100644 index de1f91d198a35..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-subtract.js +++ /dev/null @@ -1,35 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-subtract test - in V8's mjsunit test instant-subtract.js -features: [Temporal] ----*/ - -let i1 = new Temporal.Instant(50000n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -3, -2, -1)).epochNanoseconds, 3052001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1)).epochNanoseconds, BigInt(4 * 1000000000) + 3052001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, -5, -4, -3, -2, -1)).epochNanoseconds, BigInt(5 * 60 + 4) * 1000000000n + 3052001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, -6, -5, -4, -3, -2, -1)).epochNanoseconds, BigInt(6 * 3600 + 5 * 60 + 4) * 1000000000n + 3052001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 3, 2, 1)).epochNanoseconds, -2952001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1)).epochNanoseconds, BigInt(-4 * 1000000000) - 2952001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 5, 4, 3, 2, 1)).epochNanoseconds, BigInt(5 * 60 + 4) * -1000000000n - 2952001n); -assert.sameValue(i1.subtract(new Temporal.Duration(0, 0, 0, 0, 6, 5, 4, 3, 2, 1)).epochNanoseconds, BigInt(6 * 3600 + 5 * 60 + 4) * -1000000000n - 2952001n); -let badInstant = { subtract: i1.subtract }; -assert.throws(TypeError, () => badInstant.subtract(new Temporal.Duration(0, 0, 0, 0, 5))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(1))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 2))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 3))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 0, 4))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(-1))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, -2))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, -3))); -assert.throws(RangeError, () => i1.subtract(new Temporal.Duration(0, 0, 0, -4))); -let i2 = new Temporal.Instant(-86400n * 99999999999999999n); -assert.throws(RangeError, () => i2.subtract(new Temporal.Duration(0, 0, 0, 0, 999999999))); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-to-json.js b/js/src/tests/test262/staging/Temporal/v8/instant-to-json.js deleted file mode 100644 index 6bd564ba54701..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-to-json.js +++ /dev/null @@ -1,34 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-to-json test - in V8's mjsunit test instant-to-json.js -features: [Temporal] ----*/ - -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(0).toJSON(), '1970-01-01T00:00:00Z'); -let days_in_ms = 24 * 60 * 60 * 1000; -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(365 * days_in_ms - 1).toJSON(), '1970-12-31T23:59:59.999Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(365 * days_in_ms).toJSON(), '1971-01-01T00:00:00Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(2 * 365 * days_in_ms - 1).toJSON(), '1971-12-31T23:59:59.999Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds(2 * 365 * days_in_ms).toJSON(), '1972-01-01T00:00:00Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds((2 * 365 + 58) * days_in_ms).toJSON(), '1972-02-28T00:00:00Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds((2 * 365 + 59) * days_in_ms).toJSON(), '1972-02-29T00:00:00Z'); -assert.sameValue(Temporal.Instant.fromEpochMilliseconds((15 * 365 + 4) * days_in_ms).toJSON(), '1985-01-01T00:00:00Z'); -const year_in_sec = 24 * 60 * 60 * 365; -const number_of_random_test = 500; -for (let i = 0; i < number_of_random_test; i++) { - let ms = Math.floor(Math.random() * year_in_sec * 1000 * 10000) - year_in_sec * 1000 * 5000; - let d = new Date(ms); - const dateout = d.toJSON().substr(0, 19); - const temporalout = Temporal.Instant.fromEpochMilliseconds(ms).toJSON().substr(0, 19); - if (dateout[0] != '0') { - assert.sameValue(temporalout, dateout, ms); - } -} - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/instant-value-of.js b/js/src/tests/test262/staging/Temporal/v8/instant-value-of.js deleted file mode 100644 index b6ce1dc6dd527..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/instant-value-of.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from instant-valueOf test - in V8's mjsunit test instant-valueOf.js -features: [Temporal] ----*/ - -let d1 = Temporal.Now.instant(); -assert.throws(TypeError, () => d1.valueOf()); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-add.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-add.js deleted file mode 100644 index 6fa3a07809f82..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-add.js +++ /dev/null @@ -1,28 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-add test - in V8's mjsunit test plain-date-add.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d = new Temporal.PlainDate(2021, 7, 20); -TemporalHelpers.assertPlainDate(d.add('P1D'), 2021, 7, 'M07', 21); -TemporalHelpers.assertPlainDate(d.subtract('-P1D'), 2021, 7, 'M07', 21); -TemporalHelpers.assertPlainDate(d.add('-P1D'), 2021, 7, 'M07', 19); -TemporalHelpers.assertPlainDate(d.subtract('P1D'), 2021, 7, 'M07', 19); -TemporalHelpers.assertPlainDate(d.add('P11D'), 2021, 7, 'M07', 31); -TemporalHelpers.assertPlainDate(d.subtract('-P11D'), 2021, 7, 'M07', 31); -TemporalHelpers.assertPlainDate(d.add('P12D'), 2021, 8, 'M08', 1); -TemporalHelpers.assertPlainDate(d.subtract('-P12D'), 2021, 8, 'M08', 1); -let goodDate = new Temporal.PlainDate(2021, 7, 20); -let badDate = { add: goodDate.add }; -assert.throws(TypeError, () => badDate.add('P1D')); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7, 20).add('bad duration')); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-compare.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-compare.js deleted file mode 100644 index 3dd0972b0f40c..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-compare.js +++ /dev/null @@ -1,34 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-compare test - in V8's mjsunit test plain-date-compare.js -features: [Temporal] ----*/ - -let t1 = new Temporal.PlainDate(2021, 3, 14); -let t2 = new Temporal.PlainDate(2021, 3, 14); -let t3 = t1; -let t4 = new Temporal.PlainDate(2021, 3, 15); -let t5 = new Temporal.PlainDate(2021, 4, 14); -let t6 = new Temporal.PlainDate(2022, 3, 14); -assert.sameValue(Temporal.PlainDate.compare(t1, t1), 0); -assert.sameValue(Temporal.PlainDate.compare(t1, t2), 0); -assert.sameValue(Temporal.PlainDate.compare(t1, t3), 0); -assert.sameValue(Temporal.PlainDate.compare(t1, '2021-03-14'), 0); -assert.sameValue(Temporal.PlainDate.compare(t1, '2021-03-14T23:59:59'), 0); -assert.sameValue(Temporal.PlainDate.compare(t4, t1), 1); -assert.sameValue(Temporal.PlainDate.compare(t5, t1), 1); -assert.sameValue(Temporal.PlainDate.compare(t6, t1), 1); -assert.sameValue(Temporal.PlainDate.compare(t1, t4), -1); -assert.sameValue(Temporal.PlainDate.compare(t1, t5), -1); -assert.sameValue(Temporal.PlainDate.compare(t1, t6), -1); -assert.sameValue(Temporal.PlainDate.compare('2021-07-21', t1), 1); -assert.throws(RangeError, () => Temporal.PlainDate.compare(t1, 'invalid iso8601 string')); -assert.throws(RangeError, () => Temporal.PlainDate.compare('invalid iso8601 string', t1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-constructor.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-constructor.js deleted file mode 100644 index 8d95da9f4d6fd..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-constructor.js +++ /dev/null @@ -1,78 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-constructor test - in V8's mjsunit test plain-date-constructor.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(1911, 10, 10); -TemporalHelpers.assertPlainDate(d1, 1911, 10, 'M10', 10); -let d2 = new Temporal.PlainDate(2020, 3, 12); -TemporalHelpers.assertPlainDate(d2, 2020, 3, 'M03', 12); -let d3 = new Temporal.PlainDate(1, 12, 25); -TemporalHelpers.assertPlainDate(d3, 1, 12, 'M12', 25); -let d4 = new Temporal.PlainDate(1970, 1, 1); -TemporalHelpers.assertPlainDate(d4, 1970, 1, 'M01', 1); -let d5 = new Temporal.PlainDate(-10, 12, 1); -TemporalHelpers.assertPlainDate(d5, -10, 12, 'M12', 1); -let d6 = new Temporal.PlainDate(-25406, 1, 1); -TemporalHelpers.assertPlainDate(d6, -25406, 1, 'M01', 1); -let d7 = new Temporal.PlainDate(26890, 12, 31); -TemporalHelpers.assertPlainDate(d7, 26890, 12, 'M12', 31); -assert.throws(TypeError, () => Temporal.PlainDate(2021, 7, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate()); -assert.throws(RangeError, () => new Temporal.PlainDate(2021)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 0)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 13)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7, 0)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, -7, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, -7, -1)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 0, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 13, 1)); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 1, 31), 2021, 1, 'M01', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 2, 28), 2021, 2, 'M02', 28); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 3, 31), 2021, 3, 'M03', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 4, 30), 2021, 4, 'M04', 30); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 5, 31), 2021, 5, 'M05', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 6, 30), 2021, 6, 'M06', 30); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 7, 31), 2021, 7, 'M07', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 8, 31), 2021, 8, 'M08', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 9, 30), 2021, 9, 'M09', 30); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 10, 31), 2021, 10, 'M10', 31); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 11, 30), 2021, 11, 'M11', 30); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2021, 12, 31), 2021, 12, 'M12', 31); -assert.throws(RangeError, () => new Temporal.PlainDate(1900, 2, 29)); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2000, 2, 29), 2000, 2, 'M02', 29); -assert.throws(RangeError, () => new Temporal.PlainDate(2001, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDate(2002, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDate(2003, 2, 29)); -TemporalHelpers.assertPlainDate(new Temporal.PlainDate(2004, 2, 29), 2004, 2, 'M02', 29); -assert.throws(RangeError, () => new Temporal.PlainDate(2100, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 1, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 3, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 4, 31)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 5, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 6, 31)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 7, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 8, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 9, 31)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 10, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 11, 31)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 12, 32)); -assert.throws(RangeError, () => new Temporal.PlainDate(Infinity, 12, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate(-Infinity, 12, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 12, Infinity)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, 12, -Infinity)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, -Infinity, 1)); -assert.throws(RangeError, () => new Temporal.PlainDate(2021, Infinity, 1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-equals.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-equals.js deleted file mode 100644 index 2cdc96c5c2185..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-equals.js +++ /dev/null @@ -1,22 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-equals test - in V8's mjsunit test plain-date-equals.js -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(2021, 2, 28); -let d2 = Temporal.PlainDate.from('2021-02-28'); -let d3 = Temporal.PlainDate.from('2021-01-28'); -assert(d1.equals(d2)); -assert(!d1.equals(d3)); -assert(!d2.equals(d3)); -let badDate = { equals: d1.equals }; -assert.throws(TypeError, () => badDate.equals(d1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-from.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-from.js deleted file mode 100644 index be83b05661b7c..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-from.js +++ /dev/null @@ -1,85 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-from test - in V8's mjsunit test plain-date-from.js -features: [Temporal] ----*/ - -let d1 = Temporal.Now.plainDateISO(); -[ - true, - false, - 'string is invalid', - Symbol(), - 123, - 456n, - Infinity, - NaN, - null -].forEach(function (invalidOptions) { - assert.throws(TypeError, () => Temporal.PlainDate.from(d1, invalidOptions)); -}); -assert.throws(RangeError, () => Temporal.PlainDate.from(d1, { overflow: 'invalid overflow' })); -[ - undefined, - {}, - { overflow: 'constrain' }, - { overflow: 'reject' } -].forEach(function (validOptions) { - let d = new Temporal.PlainDate(1, 2, 3); - let d2 = Temporal.PlainDate.from(d, validOptions); - assert.sameValue(d2.year, 1); - assert.sameValue(d2.month, 2); - assert.sameValue(d2.day, 3); - assert.sameValue(d2.calendarId, 'iso8601'); -}); -[ - undefined, - {}, - { overflow: 'constrain' }, - { overflow: 'reject' } -].forEach(function (validOptions) { - let d3 = Temporal.PlainDate.from({ - year: 9, - month: 8, - day: 7 - }, validOptions); - assert.sameValue(d3.year, 9); - assert.sameValue(d3.month, 8); - assert.sameValue(d3.monthCode, 'M08'); - assert.sameValue(d3.day, 7); - assert.sameValue(d3.calendarId, 'iso8601'); -}); -[ - undefined, - {}, - { overflow: 'constrain' } -].forEach(function (validOptions) { - let d4 = Temporal.PlainDate.from({ - year: 9, - month: 14, - day: 32 - }, validOptions); - assert.sameValue(d4.year, 9); - assert.sameValue(d4.month, 12); - assert.sameValue(d4.monthCode, 'M12'); - assert.sameValue(d4.day, 31); - assert.sameValue(d4.calendarId, 'iso8601'); -}); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 9, - month: 14, - day: 30 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDate.from({ - year: 9, - month: 12, - day: 32 -}, { overflow: 'reject' })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-get-iso-fields.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-get-iso-fields.js deleted file mode 100644 index 8eb634b4e4357..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-get-iso-fields.js +++ /dev/null @@ -1,29 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-get-iso-fields test - in V8's mjsunit test plain-date-get-iso-fields.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(1911, 10, 10); -TemporalHelpers.assertPlainDate(d1, 1911, 10, 'M10', 10); -let d2 = new Temporal.PlainDate(2020, 3, 12); -TemporalHelpers.assertPlainDate(d2, 2020, 3, 'M03', 12); -let d3 = new Temporal.PlainDate(1, 12, 25); -TemporalHelpers.assertPlainDate(d3, 1, 12, 'M12', 25); -let d4 = new Temporal.PlainDate(1970, 1, 1); -TemporalHelpers.assertPlainDate(d4, 1970, 1, 'M01', 1); -let d5 = new Temporal.PlainDate(-10, 12, 1); -TemporalHelpers.assertPlainDate(d5, -10, 12, 'M12', 1); -let d6 = new Temporal.PlainDate(-25406, 1, 1); -TemporalHelpers.assertPlainDate(d6, -25406, 1, 'M01', 1); -let d7 = new Temporal.PlainDate(26890, 12, 31); -TemporalHelpers.assertPlainDate(d7, 26890, 12, 'M12', 31); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-add.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-add.js deleted file mode 100644 index 88d807f84dc8d..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-add.js +++ /dev/null @@ -1,39 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-add test - in V8's mjsunit test plain-date-time-add.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('PT9H8M7.080090010S'), 2021, 7, 'M07', 20, 10, 10, 10, 84, 95, 16); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 1, 996).add('PT0.0000071S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 9, 96); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 1, 996).add('PT0.0071S'), 2021, 7, 'M07', 20, 0, 0, 0, 9, 96, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 1, 996).add('PT7.1S'), 2021, 7, 'M07', 20, 0, 0, 9, 96, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 1, 59).add('PT5M7S'), 2021, 7, 'M07', 20, 0, 7, 6, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 59).add('PT5H7M'), 2021, 7, 'M07', 20, 7, 6, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 19).add('PT8H'), 2021, 7, 'M07', 21, 3, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 21, 52, 53, 994, 995, 996).add('PT5H13M11.404303202S'), 2021, 7, 'M07', 21, 3, 6, 5, 399, 299, 198); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).add('PT0.000000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 1); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).add('PT0.00000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 55); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).add('PT0.0000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 595); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT0.000000007S'), 2021, 7, 'M07', 20, 1, 2, 3, 4, 4, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT0.000005007S'), 2021, 7, 'M07', 20, 1, 2, 3, 3, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT0.004005007S'), 2021, 7, 'M07', 20, 1, 2, 2, 999, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT0.005006007S'), 2021, 7, 'M07', 20, 1, 2, 2, 998, 998, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT4.005006007S'), 2021, 7, 'M07', 20, 1, 1, 58, 998, 998, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT4S'), 2021, 7, 'M07', 20, 1, 1, 59, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT5M'), 2021, 7, 'M07', 20, 0, 57, 3, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT1H5M'), 2021, 7, 'M07', 19, 23, 57, 3, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).add('-PT1H5M4S'), 2021, 7, 'M07', 19, 23, 56, 59, 4, 5, 6); -let goodDateTime = new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3); -let badDateTime = { add: goodDateTime.add }; -assert.throws(TypeError, () => badDateTime.add('PT30M')); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3).add('bad duration')); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-compare.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-compare.js deleted file mode 100644 index 8837ebc722a71..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-compare.js +++ /dev/null @@ -1,37 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-compare test - in V8's mjsunit test plain-date-time-compare.js -features: [Temporal] ----*/ - -let t1 = new Temporal.PlainDateTime(2021, 3, 14, 1, 2, 3, 4, 5, 6); -let t2 = new Temporal.PlainDateTime(2021, 3, 14, 1, 2, 3, 4, 5, 6); -let t3 = t1; -let t4 = new Temporal.PlainDateTime(2021, 3, 15, 1, 2, 3, 4, 5, 6); -let t5 = new Temporal.PlainDateTime(2021, 4, 14, 1, 2, 3, 4, 5, 6); -let t6 = new Temporal.PlainDateTime(2022, 3, 14, 1, 2, 3, 4, 5, 6); -let t7 = new Temporal.PlainDateTime(2021, 3, 14, 1, 2, 3, 4, 5, 7); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t1), 0); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t2), 0); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t3), 0); -assert.sameValue(Temporal.PlainDateTime.compare(t1, '2021-03-14T01:02:03'), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t4, t1), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t5, t1), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t6, t1), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t7, t1), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t4), -1); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t5), -1); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t6), -1); -assert.sameValue(Temporal.PlainDateTime.compare(t1, t7), -1); -assert.sameValue(Temporal.PlainDateTime.compare('2021-07-21', t1), 1); -assert.sameValue(Temporal.PlainDateTime.compare(t1, '2021-03-14T01:02:03.004005006'), 0); -assert.throws(RangeError, () => Temporal.PlainDateTime.compare(t1, 'invalid iso8601 string')); -assert.throws(RangeError, () => Temporal.PlainDateTime.compare('invalid iso8601 string', t1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-constructor.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-constructor.js deleted file mode 100644 index 0cf733cc3af59..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-constructor.js +++ /dev/null @@ -1,105 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-constructor test - in V8's mjsunit test plain-date-time-constructor.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(1911, 10, 10); -TemporalHelpers.assertPlainDateTime(d1, 1911, 10, 'M10', 10, 0, 0, 0, 0, 0, 0); -let d2 = new Temporal.PlainDateTime(2020, 3, 12); -TemporalHelpers.assertPlainDateTime(d2, 2020, 3, 'M03', 12, 0, 0, 0, 0, 0, 0); -let d3 = new Temporal.PlainDateTime(1, 12, 25); -TemporalHelpers.assertPlainDateTime(d3, 1, 12, 'M12', 25, 0, 0, 0, 0, 0, 0); -let d4 = new Temporal.PlainDateTime(1970, 1, 1); -TemporalHelpers.assertPlainDateTime(d4, 1970, 1, 'M01', 1, 0, 0, 0, 0, 0, 0); -let d5 = new Temporal.PlainDateTime(-10, 12, 1); -TemporalHelpers.assertPlainDateTime(d5, -10, 12, 'M12', 1, 0, 0, 0, 0, 0, 0); -let d6 = new Temporal.PlainDateTime(-25406, 1, 1); -TemporalHelpers.assertPlainDateTime(d6, -25406, 1, 'M01', 1, 0, 0, 0, 0, 0, 0); -let d7 = new Temporal.PlainDateTime(26890, 12, 31); -TemporalHelpers.assertPlainDateTime(d7, 26890, 12, 'M12', 31, 0, 0, 0, 0, 0, 0); -assert.throws(TypeError, () => Temporal.PlainDateTime(2021, 7, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime()); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 0)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 13)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 0)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, -7, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, -7, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 0, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 13, 1)); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 1, 31), 2021, 1, 'M01', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 2, 28), 2021, 2, 'M02', 28, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 3, 31), 2021, 3, 'M03', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 4, 30), 2021, 4, 'M04', 30, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 5, 31), 2021, 5, 'M05', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 6, 30), 2021, 6, 'M06', 30, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 31), 2021, 7, 'M07', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 8, 31), 2021, 8, 'M08', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 9, 30), 2021, 9, 'M09', 30, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 10, 31), 2021, 10, 'M10', 31, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 11, 30), 2021, 11, 'M11', 30, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 12, 31), 2021, 12, 'M12', 31, 0, 0, 0, 0, 0, 0); -assert.throws(RangeError, () => new Temporal.PlainDateTime(1900, 2, 29)); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2000, 2, 29), 2000, 2, 'M02', 29, 0, 0, 0, 0, 0, 0); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2001, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2002, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2003, 2, 29)); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2004, 2, 29), 2004, 2, 'M02', 29, 0, 0, 0, 0, 0, 0); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2100, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 1, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 2, 29)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 3, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 4, 31)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 5, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 6, 31)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 8, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 9, 31)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 10, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 11, 31)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 12, 32)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(Infinity, 12, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(-Infinity, 12, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 12, Infinity)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 12, -Infinity)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, -Infinity, 1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, Infinity, 1)); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9), 2021, 7, 'M07', 9, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 2, 3, 4, 5, 6), 2021, 7, 'M07', 9, 1, 2, 3, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 2, 3, 4, 5), 2021, 7, 'M07', 9, 1, 2, 3, 4, 5, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 2, 3, 4), 2021, 7, 'M07', 9, 1, 2, 3, 4, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 2, 3), 2021, 7, 'M07', 9, 1, 2, 3, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 2), 2021, 7, 'M07', 9, 1, 2, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 1, 0), 2021, 7, 'M07', 9, 1, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 0, 0, 0), 2021, 7, 'M07', 9, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 23, 59, 59, 999, 999, 999), 2021, 7, 'M07', 9, 23, 59, 59, 999, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, true, false, undefined, true), 2021, 7, 'M07', 9, 1, 0, 0, 1, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 9, 11.9, 12.8, 13.7, 14.6, 15.5, 1.999999), 2021, 7, 'M07', 9, 11, 12, 13, 14, 15, 1); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, -Infinity)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, Infinity)); -assert.throws(TypeError, () => new Temporal.PlainDateTime(2021, 7, 9, Symbol(2))); -assert.throws(TypeError, () => new Temporal.PlainDateTime(2021, 7, 9, 3n)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 24)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 60)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 60)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 1000)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 0, 1000)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 0, 0, 1000)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 0, -1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 9, 0, 0, 0, 0, 0, -1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-equals.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-equals.js deleted file mode 100644 index 5096b6cef7bae..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-equals.js +++ /dev/null @@ -1,37 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-equals test - in V8's mjsunit test plain-date-time-equals.js -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(2021, 2, 28, 11, 12, 13); -let d2 = Temporal.PlainDateTime.from({ - year: 2021, - month: 2, - day: 28, - hour: 11, - minute: 12, - second: 13 -}); -let d3 = Temporal.PlainDateTime.from({ - year: 2021, - month: 2, - day: 28, - hour: 11, - minute: 12, - second: 13, - nanosecond: 1 -}); -assert(d1.equals(d2)); -assert(!d1.equals(d3)); -assert(!d2.equals(d3)); -let badDate = { equals: d1.equals }; -assert.throws(TypeError, () => badDate.equals(d1)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-from.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-from.js deleted file mode 100644 index 76b23fa733380..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-from.js +++ /dev/null @@ -1,152 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-from test - in V8's mjsunit test plain-date-time-from.js -features: [Temporal] ----*/ - -let d1 = Temporal.Now.plainDateTimeISO(); -[ - true, - false, - 'string is invalid', - Symbol(), - 123, - 456n, - Infinity, - NaN, - null -].forEach(function (invalidOptions) { - assert.throws(TypeError, () => Temporal.PlainDateTime.from(d1, invalidOptions)); -}); -assert.throws(RangeError, () => Temporal.PlainDateTime.from(d1, { overflow: 'invalid overflow' })); -[ - undefined, - {}, - { overflow: 'constrain' }, - { overflow: 'reject' } -].forEach(function (validOptions) { - let d = new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7, 8, 9); - let d2 = Temporal.PlainDateTime.from(d, validOptions); - assert.sameValue(d2.year, 1); - assert.sameValue(d2.month, 2); - assert.sameValue(d2.monthCode, 'M02'); - assert.sameValue(d2.day, 3); - assert.sameValue(d2.hour, 4); - assert.sameValue(d2.minute, 5); - assert.sameValue(d2.second, 6); - assert.sameValue(d2.millisecond, 7); - assert.sameValue(d2.microsecond, 8); - assert.sameValue(d2.nanosecond, 9); - assert.sameValue(d2.calendarId, 'iso8601'); -}); -[ - undefined, - {}, - { overflow: 'constrain' }, - { overflow: 'reject' } -].forEach(function (validOptions) { - let d3 = Temporal.PlainDateTime.from({ - year: 9, - month: 8, - day: 7, - hour: 6, - minute: 5, - second: 4, - millisecond: 3, - microsecond: 2, - nanosecond: 1 - }, validOptions); - assert.sameValue(d3.year, 9); - assert.sameValue(d3.month, 8); - assert.sameValue(d3.monthCode, 'M08'); - assert.sameValue(d3.day, 7); - assert.sameValue(d3.hour, 6); - assert.sameValue(d3.minute, 5); - assert.sameValue(d3.second, 4); - assert.sameValue(d3.millisecond, 3); - assert.sameValue(d3.microsecond, 2); - assert.sameValue(d3.nanosecond, 1); - assert.sameValue(d3.calendarId, 'iso8601'); -}); -[ - undefined, - {}, - { overflow: 'constrain' } -].forEach(function (validOptions) { - let d4 = Temporal.PlainDateTime.from({ - year: 9, - month: 14, - day: 32, - hour: 24, - minute: 60, - second: 60, - millisecond: 1000, - microsecond: 1000, - nanosecond: 1000 - }, validOptions); - assert.sameValue(d4.year, 9); - assert.sameValue(d4.month, 12); - assert.sameValue(d4.monthCode, 'M12'); - assert.sameValue(d4.day, 31); - assert.sameValue(d4.hour, 23); - assert.sameValue(d4.minute, 59); - assert.sameValue(d4.second, 59); - assert.sameValue(d4.millisecond, 999); - assert.sameValue(d4.microsecond, 999); - assert.sameValue(d4.nanosecond, 999); - assert.sameValue(d4.calendarId, 'iso8601'); -}); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 14, - day: 30 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 32 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - hour: 24 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - minute: 60 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - second: 60 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - millisecond: 1000 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - microsecond: 1000 -}, { overflow: 'reject' })); -assert.throws(RangeError, () => Temporal.PlainDateTime.from({ - year: 9, - month: 12, - day: 31, - nanosecond: 1000 -}, { overflow: 'reject' })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-get-iso-fields.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-get-iso-fields.js deleted file mode 100644 index 9924abef0bb09..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-get-iso-fields.js +++ /dev/null @@ -1,29 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-get-iso-fields test - in V8's mjsunit test plain-date-time-get-iso-fields.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7, 8, 9), 1, 2, 'M02', 3, 4, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7, 8), 1, 2, 'M02', 3, 4, 5, 6, 7, 8, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6, 7), 1, 2, 'M02', 3, 4, 5, 6, 7, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5, 6), 1, 2, 'M02', 3, 4, 5, 6, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4, 5), 1, 2, 'M02', 3, 4, 5, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3, 4), 1, 2, 'M02', 3, 4, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(1, 2, 3), 1, 2, 'M02', 3, 0, 0, 0, 0, 0, 0); -assert.throws(RangeError, () => new Temporal.PlainDateTime(1, 2)); -assert.throws(RangeError, () => new Temporal.PlainDateTime(1)); -assert.throws(RangeError, () => new Temporal.PlainDateTime()); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(-25406, 1, 1), -25406, 1, 'M01', 1, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(29345, 12, 31, 23, 59, 59, 999, 999, 999), 29345, 12, 'M12', 31, 23, 59, 59, 999, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(false, true, true, undefined, true), 0, 1, 'M01', 1, 0, 1, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(11.9, 12.8, 13.7, 14.6, 15.5, 16.6, 17.7, 18.8, 1.999999), 11, 12, 'M12', 13, 14, 15, 16, 17, 18, 1); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-subtract.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-subtract.js deleted file mode 100644 index 6a65492f6d335..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-subtract.js +++ /dev/null @@ -1,39 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-subtract test - in V8's mjsunit test plain-date-time-subtract.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('-PT9H8M7.080090010S'), 2021, 7, 'M07', 20, 10, 10, 10, 84, 95, 16); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 1, 996).subtract('-PT0.0000071S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 9, 96); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 1, 996).subtract('-PT0.0071S'), 2021, 7, 'M07', 20, 0, 0, 0, 9, 96, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 1, 996).subtract('-PT7.1S'), 2021, 7, 'M07', 20, 0, 0, 9, 96, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 1, 59).subtract('-PT5M7S'), 2021, 7, 'M07', 20, 0, 7, 6, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 59).subtract('-PT5H7M'), 2021, 7, 'M07', 20, 7, 6, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 19).subtract('-PT8H'), 2021, 7, 'M07', 21, 3, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 21, 52, 53, 994, 995, 996).subtract('-PT5H13M11.404303202S'), 2021, 7, 'M07', 21, 3, 6, 5, 399, 299, 198); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).subtract('-PT0.000000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 1); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).subtract('-PT0.00000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 55); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 0, 0, 0, 0, 0, 995).subtract('-PT0.0000006S'), 2021, 7, 'M07', 20, 0, 0, 0, 0, 1, 595); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT0.000000007S'), 2021, 7, 'M07', 20, 1, 2, 3, 4, 4, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT0.000005007S'), 2021, 7, 'M07', 20, 1, 2, 3, 3, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT0.004005007S'), 2021, 7, 'M07', 20, 1, 2, 2, 999, 999, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT0.005006007S'), 2021, 7, 'M07', 20, 1, 2, 2, 998, 998, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT4.005006007S'), 2021, 7, 'M07', 20, 1, 1, 58, 998, 998, 999); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT4S'), 2021, 7, 'M07', 20, 1, 1, 59, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT5M'), 2021, 7, 'M07', 20, 0, 57, 3, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT1H5M'), 2021, 7, 'M07', 19, 23, 57, 3, 4, 5, 6); -TemporalHelpers.assertPlainDateTime(new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3, 4, 5, 6).subtract('PT1H5M4S'), 2021, 7, 'M07', 19, 23, 56, 59, 4, 5, 6); -let goodDateTime = new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3); -let badDateTime = { subtract: goodDateTime.subtract }; -assert.throws(TypeError, () => badDateTime.subtract('PT30M')); -assert.throws(RangeError, () => new Temporal.PlainDateTime(2021, 7, 20, 1, 2, 3).subtract('bad duration')); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-json.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-json.js deleted file mode 100644 index e5d8f625e5ee1..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-json.js +++ /dev/null @@ -1,55 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-to-json test - in V8's mjsunit test plain-date-time-to-json.js -features: [Temporal] ----*/ - -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1).toJSON(), '2021-07-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(9999, 12, 31).toJSON(), '9999-12-31T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(1000, 1, 1).toJSON(), '1000-01-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(10000, 1, 1).toJSON(), '+010000-01-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(25021, 7, 1).toJSON(), '+025021-07-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(999, 12, 31).toJSON(), '0999-12-31T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(99, 8, 1).toJSON(), '0099-08-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(-20, 9, 30).toJSON(), '-000020-09-30T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(-2021, 7, 1).toJSON(), '-002021-07-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(-22021, 7, 1).toJSON(), '-022021-07-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 2, 3, 4).toJSON(), '2021-07-01T02:03:04'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 3, 4).toJSON(), '2021-07-01T00:03:04'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 4).toJSON(), '2021-07-01T00:00:04'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 0).toJSON(), '2021-07-01T00:00:00'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 2, 0, 0).toJSON(), '2021-07-01T02:00:00'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 2, 3, 0).toJSON(), '2021-07-01T02:03:00'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 23, 59, 59).toJSON(), '2021-07-01T23:59:59'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59).toJSON(), '2021-07-01T00:59:59'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 0, 1).toJSON(), '2021-07-01T00:59:59.000000001'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 8, 9).toJSON(), '2021-07-01T00:59:59.000008009'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 7, 8, 9).toJSON(), '2021-07-01T00:59:59.007008009'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 0, 90).toJSON(), '2021-07-01T00:59:59.00000009'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 0, 900).toJSON(), '2021-07-01T00:59:59.0000009'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 8).toJSON(), '2021-07-01T00:59:59.000008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 8, 0).toJSON(), '2021-07-01T00:59:59.000008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 80).toJSON(), '2021-07-01T00:59:59.00008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 80, 0).toJSON(), '2021-07-01T00:59:59.00008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 800).toJSON(), '2021-07-01T00:59:59.0008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 800, 0).toJSON(), '2021-07-01T00:59:59.0008'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 7, 0, 0).toJSON(), '2021-07-01T00:59:59.007'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 7, 0).toJSON(), '2021-07-01T00:59:59.007'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 7).toJSON(), '2021-07-01T00:59:59.007'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 70, 0, 0).toJSON(), '2021-07-01T00:59:59.07'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 70, 0).toJSON(), '2021-07-01T00:59:59.07'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 70).toJSON(), '2021-07-01T00:59:59.07'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 700, 0, 0).toJSON(), '2021-07-01T00:59:59.7'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 700, 0).toJSON(), '2021-07-01T00:59:59.7'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 700).toJSON(), '2021-07-01T00:59:59.7'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 876).toJSON(), '2021-07-01T00:59:59.000876'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 876).toJSON(), '2021-07-01T00:59:59.876'); -assert.sameValue(new Temporal.PlainDateTime(2021, 7, 1, 0, 59, 59, 0, 0, 876).toJSON(), '2021-07-01T00:59:59.000000876'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-date.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-date.js deleted file mode 100644 index 6f79432a374a6..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-date.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-to-plain-date test - in V8's mjsunit test plain-date-time-to-plain-date.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(2021, 12, 11, 1, 2, 3, 4, 5, 6); -let badDateTime = { toPlainDate: d1.toPlainDate }; -assert.throws(TypeError, () => badDateTime.toPlainDate()); -TemporalHelpers.assertPlainDate(d1.toPlainDate(), 2021, 12, 'M12', 11); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-time.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-time.js deleted file mode 100644 index 404a1964d83ed..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-to-plain-time.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-to-plain-time test - in V8's mjsunit test plain-date-time-to-plain-time.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(2021, 12, 11, 1, 2, 3, 4, 5, 6); -let badDateTime = { toPlainTime: d1.toPlainTime }; -assert.throws(TypeError, () => badDateTime.toPlainTime()); -TemporalHelpers.assertPlainTime(d1.toPlainTime(), 1, 2, 3, 4, 5, 6); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-value-of.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-value-of.js deleted file mode 100644 index 41bc83c5c71c4..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-value-of.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-valueOf test - in V8's mjsunit test plain-date-time-valueOf.js -features: [Temporal] ----*/ - -let d1 = Temporal.Now.plainDateTimeISO(); -assert.throws(TypeError, () => d1.valueOf()); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-calendar.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-calendar.js deleted file mode 100644 index e620cd813716c..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-calendar.js +++ /dev/null @@ -1,25 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-with-calendar test - in V8's mjsunit test plain-date-time-with-calendar.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(1911, 10, 10, 4, 5, 6, 7, 8, 9); -let badDateTime = { withCalendar: d1.withCalendar }; -assert.throws(TypeError, () => badDateTime.withCalendar('iso8601')); - -let d2 = d1.withCalendar('roc'); -assert.sameValue('roc', d2.calendarId); -TemporalHelpers.assertPlainDateTime(d2, 0, 10, 'M10', 10, 4, 5, 6, 7, 8, 9, '', 'broc', 1); -let d3 = d2.withCalendar('iso8601'); -assert.sameValue('iso8601', d3.calendarId); -TemporalHelpers.assertPlainDateTime(d3, 1911, 10, 'M10', 10, 4, 5, 6, 7, 8, 9); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-plain-time.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-plain-time.js deleted file mode 100644 index a6be4ec01000a..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with-plain-time.js +++ /dev/null @@ -1,37 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-with-plain-time test - in V8's mjsunit test plain-date-time-with-plain-time.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(1911, 10, 10, 4, 5, 6, 7, 8, 9); -let badDate = { withPlainTime: d1.withPlainTime }; -assert.throws(TypeError, () => badDate.withPlainTime()); -let timeRecord = { - hour: 9, - minute: 8, - second: 7, - millisecond: 6, - microsecond: 5, - nanosecond: 4 -}; -TemporalHelpers.assertPlainDateTime(d1.withPlainTime(timeRecord), 1911, 10, 'M10', 10, 9, 8, 7, 6, 5, 4); - -let d3 = new Temporal.PlainDateTime(2020, 3, 15, 4, 5, 6, 7, 8, 9, 'roc'); -TemporalHelpers.assertPlainDateTime(d3.withPlainTime(timeRecord), 109, 3, 'M03', 15, 9, 8, 7, 6, 5, 4, '', 'roc', 109); -assert.throws(TypeError, () => d1.withPlainTime(null)); -assert.throws(TypeError, () => d1.withPlainTime(true)); -assert.throws(TypeError, () => d1.withPlainTime(false)); -assert.throws(TypeError, () => d1.withPlainTime(Infinity)); -assert.throws(RangeError, () => d1.withPlainTime('invalid iso8601 string')); -assert.throws(TypeError, () => d1.withPlainTime(123)); -assert.throws(TypeError, () => d1.withPlainTime(456n)); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with.js deleted file mode 100644 index 88e4eb67be43d..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-time-with.js +++ /dev/null @@ -1,59 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-time-with test - in V8's mjsunit test plain-date-time-with.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDateTime(1911, 11, 10, 4, 5, 6, 7, 8, 9); -let badDate = { with: d1.with }; -assert.throws(TypeError, () => badDate.with()); -assert.throws(TypeError, () => d1.with(null)); -assert.throws(TypeError, () => d1.with(undefined)); -assert.throws(TypeError, () => d1.with('string is invalid')); -assert.throws(TypeError, () => d1.with(true)); -assert.throws(TypeError, () => d1.with(false)); -assert.throws(TypeError, () => d1.with(NaN)); -assert.throws(TypeError, () => d1.with(Infinity)); -assert.throws(TypeError, () => d1.with(123)); -assert.throws(TypeError, () => d1.with(456n)); -assert.throws(TypeError, () => d1.with(Symbol())); -let date = Temporal.Now.plainDateISO(); -assert.throws(TypeError, () => d1.with(date)); -let dateTime = Temporal.Now.plainDateTimeISO(); -assert.throws(TypeError, () => d1.with(dateTime)); -let time = Temporal.Now.plainTimeISO(); -assert.throws(TypeError, () => d1.with(time)); -let ym = new Temporal.PlainYearMonth(2021, 7); -assert.throws(TypeError, () => d1.with(ym)); -let md = new Temporal.PlainMonthDay(12, 25); -assert.throws(TypeError, () => d1.with(md)); -assert.throws(TypeError, () => d1.with({ calendar: 'iso8601' })); -assert.throws(TypeError, () => d1.with({ timeZone: 'UTC' })); -assert.throws(TypeError, () => d1.with({ day: 3 }, null)); -assert.throws(TypeError, () => d1.with({ day: 3 }, 'string is invalid')); -assert.throws(TypeError, () => d1.with({ day: 3 }, true)); -assert.throws(TypeError, () => d1.with({ day: 3 }, false)); -assert.throws(TypeError, () => d1.with({ day: 3 }, 123)); -assert.throws(TypeError, () => d1.with({ day: 3 }, 456n)); -assert.throws(TypeError, () => d1.with({ day: 3 }, Symbol())); -assert.throws(TypeError, () => d1.with({ day: 3 }, NaN)); -assert.throws(TypeError, () => d1.with({ day: 3 }, Infinity)); -TemporalHelpers.assertPlainDateTime(d1.with({ year: 2021 }), 2021, 11, 'M11', 10, 4, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ month: 3 }), 1911, 3, 'M03', 10, 4, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ monthCode: 'M05' }), 1911, 5, 'M05', 10, 4, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ day: 1 }), 1911, 11, 'M11', 1, 4, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ hour: 2 }), 1911, 11, 'M11', 10, 2, 5, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ minute: 3 }), 1911, 11, 'M11', 10, 4, 3, 6, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ second: 4 }), 1911, 11, 'M11', 10, 4, 5, 4, 7, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ millisecond: 5 }), 1911, 11, 'M11', 10, 4, 5, 6, 5, 8, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ microsecond: 6 }), 1911, 11, 'M11', 10, 4, 5, 6, 7, 6, 9); -TemporalHelpers.assertPlainDateTime(d1.with({ nanosecond: 7 }), 1911, 11, 'M11', 10, 4, 5, 6, 7, 8, 7); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-json.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-to-json.js deleted file mode 100644 index 33d91980b00e8..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-json.js +++ /dev/null @@ -1,24 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-to-json test - in V8's mjsunit test plain-date-to-json.js -features: [Temporal] ----*/ - -assert.sameValue(new Temporal.PlainDate(2021, 7, 1).toJSON(), '2021-07-01'); -assert.sameValue(new Temporal.PlainDate(9999, 12, 31).toJSON(), '9999-12-31'); -assert.sameValue(new Temporal.PlainDate(1000, 1, 1).toJSON(), '1000-01-01'); -assert.sameValue(new Temporal.PlainDate(99, 8, 1).toJSON(), '0099-08-01'); -assert.sameValue(new Temporal.PlainDate(999, 12, 31).toJSON(), '0999-12-31'); -assert.sameValue(new Temporal.PlainDate(10000, 1, 1).toJSON(), '+010000-01-01'); -assert.sameValue(new Temporal.PlainDate(25021, 7, 1).toJSON(), '+025021-07-01'); -assert.sameValue(new Temporal.PlainDate(-20, 9, 30).toJSON(), '-000020-09-30'); -assert.sameValue(new Temporal.PlainDate(-2021, 7, 1).toJSON(), '-002021-07-01'); -assert.sameValue(new Temporal.PlainDate(-22021, 7, 1).toJSON(), '-022021-07-01'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-date-time.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-date-time.js deleted file mode 100644 index aa1b030a6c422..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-date-time.js +++ /dev/null @@ -1,43 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-to-plain-date-time test - in V8's mjsunit test plain-date-to-plain-date-time.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(2021, 12, 11); -let badDate = { toPlainDateTime: d1.toPlainDateTime }; -assert.throws(TypeError, () => badDate.toPlainDateTime()); -assert.throws(TypeError, () => d1.toPlainDateTime(null)); -assert.throws(RangeError, () => d1.toPlainDateTime('string is invalid')); -assert.throws(TypeError, () => d1.toPlainDateTime(true)); -assert.throws(TypeError, () => d1.toPlainDateTime(false)); -assert.throws(TypeError, () => d1.toPlainDateTime(NaN)); -assert.throws(TypeError, () => d1.toPlainDateTime(Infinity)); -assert.throws(TypeError, () => d1.toPlainDateTime(123)); -assert.throws(TypeError, () => d1.toPlainDateTime(456n)); -assert.throws(TypeError, () => d1.toPlainDateTime(Symbol())); -assert.throws(TypeError, () => d1.toPlainDateTime({})); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ hour: 23 }), 2021, 12, 'M12', 11, 23, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ minute: 23 }), 2021, 12, 'M12', 11, 0, 23, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ second: 23 }), 2021, 12, 'M12', 11, 0, 0, 23, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ millisecond: 23 }), 2021, 12, 'M12', 11, 0, 0, 0, 23, 0, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ microsecond: 23 }), 2021, 12, 'M12', 11, 0, 0, 0, 0, 23, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ nanosecond: 23 }), 2021, 12, 'M12', 11, 0, 0, 0, 0, 0, 23); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime(), 2021, 12, 'M12', 11, 0, 0, 0, 0, 0, 0); -TemporalHelpers.assertPlainDateTime(d1.toPlainDateTime({ - hour: 9, - minute: 8, - second: 7, - millisecond: 6, - microsecond: 5, - nanosecond: 4 -}), 2021, 12, 'M12', 11, 9, 8, 7, 6, 5, 4); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-month-day.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-month-day.js deleted file mode 100644 index 0875b4ef2f34e..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-month-day.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-to-plain-month-day test - in V8's mjsunit test plain-date-to-plain-month-day.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(2021, 12, 11); -let badDateTime = { toPlainMonthDay: d1.toPlainMonthDay }; -assert.throws(TypeError, () => badDateTime.toPlainMonthDay()); -TemporalHelpers.assertPlainMonthDay(d1.toPlainMonthDay(), 'M12', 11); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-year-month.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-year-month.js deleted file mode 100644 index 46922af42c0ee..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-to-plain-year-month.js +++ /dev/null @@ -1,19 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-to-plain-year-month test - in V8's mjsunit test plain-date-to-plain-year-month.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(2021, 12, 11); -let badDate = { toPlainYearMonth: d1.toPlainYearMonth }; -assert.throws(TypeError, () => badDate.toPlainYearMonth()); -TemporalHelpers.assertPlainYearMonth(d1.toPlainYearMonth(), 2021, 12, 'M12'); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-value-of.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-value-of.js deleted file mode 100644 index 060254eae1384..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-value-of.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-valueOf test - in V8's mjsunit test plain-date-valueOf.js -features: [Temporal] ----*/ - -let d1 = Temporal.Now.plainDateISO(); -assert.throws(TypeError, () => d1.valueOf()); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-with-calendar.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-with-calendar.js deleted file mode 100644 index 7102c381234cd..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-with-calendar.js +++ /dev/null @@ -1,25 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-with-calendar test - in V8's mjsunit test plain-date-with-calendar.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(1911, 10, 10); -let badDate = { withCalendar: d1.withCalendar }; -assert.throws(TypeError, () => badDate.withCalendar('iso8601')); - -let d2 = d1.withCalendar('roc'); -assert.sameValue('roc', d2.calendarId); -TemporalHelpers.assertPlainDate(d2, 0, 10, 'M10', 10, '', 'broc', 1); -let d3 = d2.withCalendar('iso8601'); -assert.sameValue('iso8601', d3.calendarId); -TemporalHelpers.assertPlainDate(d3, 1911, 10, 'M10', 10); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/plain-date-with.js b/js/src/tests/test262/staging/Temporal/v8/plain-date-with.js deleted file mode 100644 index 9f811f23f7cce..0000000000000 --- a/js/src/tests/test262/staging/Temporal/v8/plain-date-with.js +++ /dev/null @@ -1,61 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright 2021 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: pending -description: > - Automatically ported from plain-date-with test - in V8's mjsunit test plain-date-with.js -includes: [temporalHelpers.js] -features: [Temporal] ----*/ - -let d1 = new Temporal.PlainDate(1911, 10, 10); -TemporalHelpers.assertPlainDate(d1.with({ year: 2021 }), 2021, 10, 'M10', 10); -TemporalHelpers.assertPlainDate(d1.with({ month: 11 }), 1911, 11, 'M11', 10); -TemporalHelpers.assertPlainDate(d1.with({ monthCode: 'M05' }), 1911, 5, 'M05', 10); -TemporalHelpers.assertPlainDate(d1.with({ day: 30 }), 1911, 10, 'M10', 30); -TemporalHelpers.assertPlainDate(d1.with({ - year: 2021, - hour: 30 -}), 2021, 10, 'M10', 10); -TemporalHelpers.assertPlainDate(d1.with({ - month: 11, - minute: 71 -}), 1911, 11, 'M11', 10); -TemporalHelpers.assertPlainDate(d1.with({ - monthCode: 'M05', - second: 90 -}), 1911, 5, 'M05', 10); -TemporalHelpers.assertPlainDate(d1.with({ - day: 30, - era: 'BC' -}), 1911, 10, 'M10', 30); - -let d2 = new Temporal.PlainDate(2021, 7, 20, 'roc'); -TemporalHelpers.assertPlainDate(d2, 110, 7, 'M07', 20, '', 'roc', 110); -TemporalHelpers.assertPlainDate(d2.with({ year: 1912 }), 1912, 7, 'M07', 20, '', 'roc', 1912); -TemporalHelpers.assertPlainDate(d2.with({ year: 1987 }), 1987, 7, 'M07', 20, '', 'roc', 1987); -assert.throws(TypeError, () => d1.with(new Temporal.PlainDate(2021, 7, 1))); -assert.throws(TypeError, () => d1.with(new Temporal.PlainDateTime(2021, 7, 1, 12, 13))); -assert.throws(TypeError, () => d1.with(new Temporal.PlainTime(1, 12, 13))); -assert.throws(TypeError, () => d1.with(new Temporal.PlainYearMonth(1991, 12))); -assert.throws(TypeError, () => d1.with(new Temporal.PlainMonthDay(5, 12))); -assert.throws(TypeError, () => d1.with('2012-05-13')); -assert.throws(TypeError, () => d1.with({ calendar: 'iso8601' })); -assert.throws(TypeError, () => d1.with({ timeZone: 'UTC' })); -assert.throws(TypeError, () => d1.with(true)); -assert.throws(TypeError, () => d1.with(false)); -assert.throws(TypeError, () => d1.with(NaN)); -assert.throws(TypeError, () => d1.with(Infinity)); -assert.throws(TypeError, () => d1.with(1234)); -assert.throws(TypeError, () => d1.with(567n)); -assert.throws(TypeError, () => d1.with(Symbol())); -assert.throws(TypeError, () => d1.with('string')); -assert.throws(TypeError, () => d1.with({})); -assert.throws(TypeError, () => d1.with([])); -let badDate = { with: d1.with }; -assert.throws(TypeError, () => badDate.with({ day: 3 })); - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/v8/shell.js b/js/src/tests/test262/staging/Temporal/v8/shell.js index 73e5d07407594..e69de29bb2d1d 100644 --- a/js/src/tests/test262/staging/Temporal/v8/shell.js +++ b/js/src/tests/test262/staging/Temporal/v8/shell.js @@ -1,1259 +0,0 @@ -// GENERATED, DO NOT EDIT -// file: temporalHelpers.js -// Copyright (C) 2021 Igalia, S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. -/*--- -description: | - This defines helper objects and functions for testing Temporal. -defines: [TemporalHelpers] -features: [Symbol.species, Symbol.iterator, Temporal] ----*/ - -const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; - -function formatPropertyName(propertyKey, objectName = "") { - switch (typeof propertyKey) { - case "symbol": - if (Symbol.keyFor(propertyKey) !== undefined) { - return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; - } else if (propertyKey.description.startsWith("Symbol.")) { - return `${objectName}[${propertyKey.description}]`; - } else { - return `${objectName}[Symbol('${propertyKey.description}')]`; - } - case "string": - if (propertyKey !== String(Number(propertyKey))) { - if (ASCII_IDENTIFIER.test(propertyKey)) { - return objectName ? `${objectName}.${propertyKey}` : propertyKey; - } - return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`; - } - // fall through - default: - // integer or string integer-index - return `${objectName}[${propertyKey}]`; - } -} - -const SKIP_SYMBOL = Symbol("Skip"); - -var TemporalHelpers = { - /* - * Codes and maximum lengths of months in the ISO 8601 calendar. - */ - ISOMonths: [ - { month: 1, monthCode: "M01", daysInMonth: 31 }, - { month: 2, monthCode: "M02", daysInMonth: 29 }, - { month: 3, monthCode: "M03", daysInMonth: 31 }, - { month: 4, monthCode: "M04", daysInMonth: 30 }, - { month: 5, monthCode: "M05", daysInMonth: 31 }, - { month: 6, monthCode: "M06", daysInMonth: 30 }, - { month: 7, monthCode: "M07", daysInMonth: 31 }, - { month: 8, monthCode: "M08", daysInMonth: 31 }, - { month: 9, monthCode: "M09", daysInMonth: 30 }, - { month: 10, monthCode: "M10", daysInMonth: 31 }, - { month: 11, monthCode: "M11", daysInMonth: 30 }, - { month: 12, monthCode: "M12", daysInMonth: 31 } - ], - - /* - * List of known calendar eras and their possible aliases. - * - * https://tc39.es/proposal-intl-era-monthcode/#table-eras - */ - CalendarEras: { - buddhist: [ - { era: "be" }, - ], - coptic: [ - { era: "am" }, - ], - ethiopic: [ - { era: "aa", aliases: ["mundi"] }, - { era: "am", aliases: ["incar"] }, - ], - ethioaa: [ - { era: "aa", aliases: ["mundi"] }, - ], - gregory: [ - { era: "bce", aliases: ["bc"] }, - { era: "ce", aliases: ["ad"] }, - ], - hebrew: [ - { era: "am" }, - ], - indian: [ - { era: "shaka" }, - ], - islamic: [ - { era: "ah" }, - { era: "bh" }, - ], - "islamic-civil": [ - { era: "bh" }, - { era: "ah" }, - ], - "islamic-rgsa": [ - { era: "bh" }, - { era: "ah" }, - ], - "islamic-tbla": [ - { era: "bh" }, - { era: "ah" }, - ], - "islamic-umalqura": [ - { era: "bh" }, - { era: "ah" }, - ], - japanese: [ - { era: "bce", aliases: ["bc"] }, - { era: "ce", aliases: ["ad"] }, - { era: "heisei" }, - { era: "meiji" }, - { era: "reiwa" }, - { era: "showa" }, - { era: "taisho" }, - ], - persian: [ - { era: "ap" }, - ], - roc: [ - { era: "roc", aliases: ["minguo"] }, - { era: "broc", aliases: ["before-roc", "minguo-qian"] }, - ], - }, - - /* - * Return the canonical era code. - */ - canonicalizeCalendarEra(calendarId, eraName) { - assert.sameValue(typeof calendarId, "string", "calendar must be string in canonicalizeCalendarEra"); - - if (!Object.prototype.hasOwnProperty.call(TemporalHelpers.CalendarEras, calendarId)) { - assert.sameValue(eraName, undefined); - return undefined; - } - - assert.sameValue(typeof eraName, "string", "eraName must be string or undefined in canonicalizeCalendarEra"); - - for (let {era, aliases = []} of TemporalHelpers.CalendarEras[calendarId]) { - if (era === eraName || aliases.includes(eraName)) { - return era; - } - } - throw new Test262Error(`Unsupported era name: ${eraName}`); - }, - - /* - * assertDuration(duration, years, ..., nanoseconds[, description]): - * - * Shorthand for asserting that each field of a Temporal.Duration is equal to - * an expected value. - */ - assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); - assert.sameValue(duration.years, years, `${prefix}years result:`); - assert.sameValue(duration.months, months, `${prefix}months result:`); - assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); - assert.sameValue(duration.days, days, `${prefix}days result:`); - assert.sameValue(duration.hours, hours, `${prefix}hours result:`); - assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); - assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); - assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); - assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); - assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); - }, - - /* - * assertDateDuration(duration, years, months, weeks, days, [, description]): - * - * Shorthand for asserting that each date field of a Temporal.Duration is - * equal to an expected value. - */ - assertDateDuration(duration, years, months, weeks, days, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); - assert.sameValue(duration.years, years, `${prefix}years result:`); - assert.sameValue(duration.months, months, `${prefix}months result:`); - assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); - assert.sameValue(duration.days, days, `${prefix}days result:`); - assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); - assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); - assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); - assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); - assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); - assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); - }, - - /* - * assertDurationsEqual(actual, expected[, description]): - * - * Shorthand for asserting that each field of a Temporal.Duration is equal to - * the corresponding field in another Temporal.Duration. - */ - assertDurationsEqual(actual, expected, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); - TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); - }, - - /* - * assertInstantsEqual(actual, expected[, description]): - * - * Shorthand for asserting that two Temporal.Instants are of the correct type - * and equal according to their equals() methods. - */ - assertInstantsEqual(actual, expected, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); - assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); - assert(actual.equals(expected), `${prefix}equals method`); - }, - - /* - * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): - * - * Shorthand for asserting that each field of a Temporal.PlainDate is equal to - * an expected value. (Except the `calendar` property, since callers may want - * to assert either object equality with an object they put in there, or the - * value of date.calendarId.) - */ - assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { - const prefix = description ? `${description}: ` : ""; - assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); - assert.sameValue( - TemporalHelpers.canonicalizeCalendarEra(date.calendarId, date.era), - TemporalHelpers.canonicalizeCalendarEra(date.calendarId, era), - `${prefix}era result:` - ); - assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); - assert.sameValue(date.year, year, `${prefix}year result:`); - assert.sameValue(date.month, month, `${prefix}month result:`); - assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); - assert.sameValue(date.day, day, `${prefix}day result:`); - }, - - /* - * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): - * - * Shorthand for asserting that each field of a Temporal.PlainDateTime is - * equal to an expected value. (Except the `calendar` property, since callers - * may want to assert either object equality with an object they put in there, - * or the value of datetime.calendarId.) - */ - assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { - const prefix = description ? `${description}: ` : ""; - assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); - assert.sameValue( - TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, datetime.era), - TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, era), - `${prefix}era result:` - ); - assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); - assert.sameValue(datetime.year, year, `${prefix}year result:`); - assert.sameValue(datetime.month, month, `${prefix}month result:`); - assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); - assert.sameValue(datetime.day, day, `${prefix}day result:`); - assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); - assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); - assert.sameValue(datetime.second, second, `${prefix}second result:`); - assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); - assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); - assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); - }, - - /* - * assertPlainDateTimesEqual(actual, expected[, description]): - * - * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct - * type, equal according to their equals() methods, and additionally that - * their calendar internal slots are the same value. - */ - assertPlainDateTimesEqual(actual, expected, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); - assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); - assert(actual.equals(expected), `${prefix}equals method`); - assert.sameValue( - actual.calendarId, - expected.calendarId, - `${prefix}calendar same value:` - ); - }, - - /* - * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): - * - * Shorthand for asserting that each field of a Temporal.PlainMonthDay is - * equal to an expected value. (Except the `calendar` property, since callers - * may want to assert either object equality with an object they put in there, - * or the value of monthDay.calendarId().) - */ - assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { - const prefix = description ? `${description}: ` : ""; - assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); - assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); - assert.sameValue(monthDay.day, day, `${prefix}day result:`); - const isoYear = Number(monthDay.toString({ calendarName: "always" }).split("-")[0]); - assert.sameValue(isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); - }, - - /* - * assertPlainTime(time, hour, ..., nanosecond[, description]): - * - * Shorthand for asserting that each field of a Temporal.PlainTime is equal to - * an expected value. - */ - assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); - assert.sameValue(time.hour, hour, `${prefix}hour result:`); - assert.sameValue(time.minute, minute, `${prefix}minute result:`); - assert.sameValue(time.second, second, `${prefix}second result:`); - assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); - assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); - assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); - }, - - /* - * assertPlainTimesEqual(actual, expected[, description]): - * - * Shorthand for asserting that two Temporal.PlainTimes are of the correct - * type and equal according to their equals() methods. - */ - assertPlainTimesEqual(actual, expected, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); - assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); - assert(actual.equals(expected), `${prefix}equals method`); - }, - - /* - * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): - * - * Shorthand for asserting that each field of a Temporal.PlainYearMonth is - * equal to an expected value. (Except the `calendar` property, since callers - * may want to assert either object equality with an object they put in there, - * or the value of yearMonth.calendarId.) - * - * Pass null as the referenceISODay if you don't want to give it explicitly. - * In that case, the expected referenceISODay will be computed using PlainDate - * and only verified for consistency, not for equality with a specific value. - */ - assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { - const prefix = description ? `${description}: ` : ""; - assert(typeof referenceISODay === "number" || referenceISODay === null, - `TemporalHelpers.assertPlainYearMonth() referenceISODay argument should be a number or null, not ${referenceISODay}`); - assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); - assert.sameValue( - TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, yearMonth.era), - TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, era), - `${prefix}era result:` - ); - assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); - assert.sameValue(yearMonth.year, year, `${prefix}year result:`); - assert.sameValue(yearMonth.month, month, `${prefix}month result:`); - assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); - const isoDay = Number(yearMonth.toString({ calendarName: "always" }).slice(1).split("-")[2].slice(0, 2)); - const expectedISODay = referenceISODay ?? yearMonth.toPlainDate({ day: 1 }).withCalendar("iso8601").day; - assert.sameValue(isoDay, expectedISODay, `${prefix}referenceISODay result:`); - }, - - /* - * assertZonedDateTimesEqual(actual, expected[, description]): - * - * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct - * type, equal according to their equals() methods, and additionally that - * their time zones and calendar internal slots are the same value. - */ - assertZonedDateTimesEqual(actual, expected, description = "") { - const prefix = description ? `${description}: ` : ""; - assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); - assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); - assert(actual.equals(expected), `${prefix}equals method`); - assert.sameValue(actual.timeZoneId, expected.timeZoneId, `${prefix}time zone same value:`); - assert.sameValue( - actual.calendarId, - expected.calendarId, - `${prefix}calendar same value:` - ); - }, - - /* - * assertUnreachable(description): - * - * Helper for asserting that code is not executed. - */ - assertUnreachable(description) { - let message = "This code should not be executed"; - if (description) { - message = `${message}: ${description}`; - } - throw new Test262Error(message); - }, - - /* - * checkPlainDateTimeConversionFastPath(func): - * - * ToTemporalDate and ToTemporalTime should both, if given a - * Temporal.PlainDateTime instance, convert to the desired type by reading the - * PlainDateTime's internal slots, rather than calling any getters. - * - * func(datetime) is the actual operation to test, that must - * internally call the abstract operation ToTemporalDate or ToTemporalTime. - * It is passed a Temporal.PlainDateTime instance. - */ - checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { - const actual = []; - const expected = []; - - const calendar = "iso8601"; - const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); - const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); - ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { - Object.defineProperty(datetime, property, { - get() { - actual.push(`get ${formatPropertyName(property)}`); - const value = prototypeDescrs[property].get.call(this); - return { - toString() { - actual.push(`toString ${formatPropertyName(property)}`); - return value.toString(); - }, - valueOf() { - actual.push(`valueOf ${formatPropertyName(property)}`); - return value; - }, - }; - }, - }); - }); - Object.defineProperty(datetime, "calendar", { - get() { - actual.push("get calendar"); - return calendar; - }, - }); - - func(datetime); - assert.compareArray(actual, expected, `${message}: property getters not called`); - }, - - /* - * Check that an options bag that accepts units written in the singular form, - * also accepts the same units written in the plural form. - * func(unit) should call the method with the appropriate options bag - * containing unit as a value. This will be called twice for each element of - * validSingularUnits, once with singular and once with plural, and the - * results of each pair should be the same (whether a Temporal object or a - * primitive value.) - */ - checkPluralUnitsAccepted(func, validSingularUnits) { - const plurals = { - year: "years", - month: "months", - week: "weeks", - day: "days", - hour: "hours", - minute: "minutes", - second: "seconds", - millisecond: "milliseconds", - microsecond: "microseconds", - nanosecond: "nanoseconds", - }; - - validSingularUnits.forEach((unit) => { - const singularValue = func(unit); - const pluralValue = func(plurals[unit]); - const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; - if (singularValue instanceof Temporal.Duration) { - TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); - } else if (singularValue instanceof Temporal.Instant) { - TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); - } else if (singularValue instanceof Temporal.PlainDateTime) { - TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); - } else if (singularValue instanceof Temporal.PlainTime) { - TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); - } else if (singularValue instanceof Temporal.ZonedDateTime) { - TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); - } else { - assert.sameValue(pluralValue, singularValue); - } - }); - }, - - /* - * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): - * - * Checks the type handling of the roundingIncrement option. - * checkFunc(roundingIncrement) is a function which takes the value of - * roundingIncrement to test, and calls the method under test with it, - * returning the result. assertTrueResultFunc(result, description) should - * assert that result is the expected result with roundingIncrement: true, and - * assertObjectResultFunc(result, description) should assert that result is - * the expected result with roundingIncrement being an object with a valueOf() - * method. - */ - checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { - // null converts to 0, which is out of range - assert.throws(RangeError, () => checkFunc(null), "null"); - // Booleans convert to either 0 or 1, and 1 is allowed - const trueResult = checkFunc(true); - assertTrueResultFunc(trueResult, "true"); - assert.throws(RangeError, () => checkFunc(false), "false"); - // Symbols and BigInts cannot convert to numbers - assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); - assert.throws(TypeError, () => checkFunc(2n), "bigint"); - - // Objects prefer their valueOf() methods when converting to a number - assert.throws(RangeError, () => checkFunc({}), "plain object"); - - const expected = [ - "get roundingIncrement.valueOf", - "call roundingIncrement.valueOf", - ]; - const actual = []; - const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); - const objectResult = checkFunc(observer); - assertObjectResultFunc(objectResult, "object with valueOf"); - assert.compareArray(actual, expected, "order of operations"); - }, - - /* - * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): - * - * Checks the type handling of a string option, of which there are several in - * Temporal. - * propertyName is the name of the option, and value is the value that - * assertFunc should expect it to have. - * checkFunc(value) is a function which takes the value of the option to test, - * and calls the method under test with it, returning the result. - * assertFunc(result, description) should assert that result is the expected - * result with the option value being an object with a toString() method - * which returns the given value. - */ - checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { - // null converts to the string "null", which is an invalid string value - assert.throws(RangeError, () => checkFunc(null), "null"); - // Booleans convert to the strings "true" or "false", which are invalid - assert.throws(RangeError, () => checkFunc(true), "true"); - assert.throws(RangeError, () => checkFunc(false), "false"); - // Symbols cannot convert to strings - assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); - // Numbers convert to strings which are invalid - assert.throws(RangeError, () => checkFunc(2), "number"); - // BigInts convert to strings which are invalid - assert.throws(RangeError, () => checkFunc(2n), "bigint"); - - // Objects prefer their toString() methods when converting to a string - assert.throws(RangeError, () => checkFunc({}), "plain object"); - - const expected = [ - `get ${propertyName}.toString`, - `call ${propertyName}.toString`, - ]; - const actual = []; - const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); - const result = checkFunc(observer); - assertFunc(result, "object with toString"); - assert.compareArray(actual, expected, "order of operations"); - }, - - /* - * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, - * resultAssertions): - * - * Methods of Temporal classes that return a new instance of the same class, - * must not take the constructor of a subclass into account, nor the @@species - * property. This helper runs tests to ensure this. - * - * construct(...constructArgs) must yield a valid instance of the Temporal - * class. instance[method](...methodArgs) is the method call under test, which - * must also yield a valid instance of the same Temporal class, not a - * subclass. See below for the individual tests that this runs. - * resultAssertions() is a function that performs additional assertions on the - * instance returned by the method under test. - */ - checkSubclassingIgnored(...args) { - this.checkSubclassConstructorNotObject(...args); - this.checkSubclassConstructorUndefined(...args); - this.checkSubclassConstructorThrows(...args); - this.checkSubclassConstructorNotCalled(...args); - this.checkSubclassSpeciesInvalidResult(...args); - this.checkSubclassSpeciesNotAConstructor(...args); - this.checkSubclassSpeciesNull(...args); - this.checkSubclassSpeciesUndefined(...args); - this.checkSubclassSpeciesThrows(...args); - }, - - /* - * Checks that replacing the 'constructor' property of the instance with - * various primitive values does not affect the returned new instance. - */ - checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { - function check(value, description) { - const instance = new construct(...constructArgs); - instance.constructor = value; - const result = instance[method](...methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); - resultAssertions(result); - } - - check(null, "null"); - check(true, "true"); - check("test", "string"); - check(Symbol(), "Symbol"); - check(7, "number"); - check(7n, "bigint"); - }, - - /* - * Checks that replacing the 'constructor' property of the subclass with - * undefined does not affect the returned new instance. - */ - checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { - let called = 0; - - class MySubclass extends construct { - constructor() { - ++called; - super(...constructArgs); - } - } - - const instance = new MySubclass(); - assert.sameValue(called, 1); - - MySubclass.prototype.constructor = undefined; - - const result = instance[method](...methodArgs); - assert.sameValue(called, 1); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Checks that making the 'constructor' property of the instance throw when - * called does not affect the returned new instance. - */ - checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { - function CustomError() {} - const instance = new construct(...constructArgs); - Object.defineProperty(instance, "constructor", { - get() { - throw new CustomError(); - } - }); - const result = instance[method](...methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Checks that when subclassing, the subclass constructor is not called by - * the method under test. - */ - checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { - let called = 0; - - class MySubclass extends construct { - constructor() { - ++called; - super(...constructArgs); - } - } - - const instance = new MySubclass(); - assert.sameValue(called, 1); - - const result = instance[method](...methodArgs); - assert.sameValue(called, 1); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Check that the constructor's @@species property is ignored when it's a - * constructor that returns a non-object value. - */ - checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { - function check(value, description) { - const instance = new construct(...constructArgs); - instance.constructor = { - [Symbol.species]: function() { - return value; - }, - }; - const result = instance[method](...methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); - resultAssertions(result); - } - - check(undefined, "undefined"); - check(null, "null"); - check(true, "true"); - check("test", "string"); - check(Symbol(), "Symbol"); - check(7, "number"); - check(7n, "bigint"); - check({}, "plain object"); - }, - - /* - * Check that the constructor's @@species property is ignored when it's not a - * constructor. - */ - checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { - function check(value, description) { - const instance = new construct(...constructArgs); - instance.constructor = { - [Symbol.species]: value, - }; - const result = instance[method](...methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); - resultAssertions(result); - } - - check(true, "true"); - check("test", "string"); - check(Symbol(), "Symbol"); - check(7, "number"); - check(7n, "bigint"); - check({}, "plain object"); - }, - - /* - * Check that the constructor's @@species property is ignored when it's null. - */ - checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { - let called = 0; - - class MySubclass extends construct { - constructor() { - ++called; - super(...constructArgs); - } - } - - const instance = new MySubclass(); - assert.sameValue(called, 1); - - MySubclass.prototype.constructor = { - [Symbol.species]: null, - }; - - const result = instance[method](...methodArgs); - assert.sameValue(called, 1); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Check that the constructor's @@species property is ignored when it's - * undefined. - */ - checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { - let called = 0; - - class MySubclass extends construct { - constructor() { - ++called; - super(...constructArgs); - } - } - - const instance = new MySubclass(); - assert.sameValue(called, 1); - - MySubclass.prototype.constructor = { - [Symbol.species]: undefined, - }; - - const result = instance[method](...methodArgs); - assert.sameValue(called, 1); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Check that the constructor's @@species property is ignored when it throws, - * i.e. it is not called at all. - */ - checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { - function CustomError() {} - - const instance = new construct(...constructArgs); - instance.constructor = { - get [Symbol.species]() { - throw new CustomError(); - }, - }; - - const result = instance[method](...methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - }, - - /* - * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): - * - * Static methods of Temporal classes that return a new instance of the class, - * must not use the this-value as a constructor. This helper runs tests to - * ensure this. - * - * construct[method](...methodArgs) is the static method call under test, and - * must yield a valid instance of the Temporal class, not a subclass. See - * below for the individual tests that this runs. - * resultAssertions() is a function that performs additional assertions on the - * instance returned by the method under test. - */ - checkSubclassingIgnoredStatic(...args) { - this.checkStaticInvalidReceiver(...args); - this.checkStaticReceiverNotCalled(...args); - this.checkThisValueNotCalled(...args); - }, - - /* - * Check that calling the static method with a receiver that's not callable, - * still calls the intrinsic constructor. - */ - checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { - function check(value, description) { - const result = construct[method].apply(value, methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - } - - check(undefined, "undefined"); - check(null, "null"); - check(true, "true"); - check("test", "string"); - check(Symbol(), "symbol"); - check(7, "number"); - check(7n, "bigint"); - check({}, "Non-callable object"); - }, - - /* - * Check that calling the static method with a receiver that returns a value - * that's not callable, still calls the intrinsic constructor. - */ - checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { - function check(value, description) { - const receiver = function () { - return value; - }; - const result = construct[method].apply(receiver, methodArgs); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - } - - check(undefined, "undefined"); - check(null, "null"); - check(true, "true"); - check("test", "string"); - check(Symbol(), "symbol"); - check(7, "number"); - check(7n, "bigint"); - check({}, "Non-callable object"); - }, - - /* - * Check that the receiver isn't called. - */ - checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { - let called = false; - - class MySubclass extends construct { - constructor(...args) { - called = true; - super(...args); - } - } - - const result = MySubclass[method](...methodArgs); - assert.sameValue(called, false); - assert.sameValue(Object.getPrototypeOf(result), construct.prototype); - resultAssertions(result); - }, - - /* - * Check that any calendar-carrying Temporal object has its [[Calendar]] - * internal slot read by ToTemporalCalendar, and does not fetch the calendar - * by calling getters. - */ - checkToTemporalCalendarFastPath(func) { - const plainDate = new Temporal.PlainDate(2000, 5, 2, "iso8601"); - const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601"); - const plainMonthDay = new Temporal.PlainMonthDay(5, 2, "iso8601"); - const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, "iso8601"); - const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); - - [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { - Object.defineProperty(temporalObject, "calendar", { - get() { - throw new Test262Error("should not get 'calendar' property"); - }, - }); - Object.defineProperty(temporalObject, "calendarId", { - get() { - throw new Test262Error("should not get 'calendarId' property"); - }, - }); - - func(temporalObject); - }); - }, - - checkToTemporalInstantFastPath(func) { - const actual = []; - const expected = []; - - const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); - Object.defineProperty(datetime, "toString", { - get() { - actual.push("get toString"); - return function (options) { - actual.push("call toString"); - return Temporal.ZonedDateTime.prototype.toString.call(this, options); - }; - }, - }); - - func(datetime); - assert.compareArray(actual, expected, "toString not called"); - }, - - checkToTemporalPlainDateTimeFastPath(func) { - const actual = []; - const expected = []; - - const date = new Temporal.PlainDate(2000, 5, 2, "iso8601"); - const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); - ["year", "month", "monthCode", "day"].forEach((property) => { - Object.defineProperty(date, property, { - get() { - actual.push(`get ${formatPropertyName(property)}`); - const value = prototypeDescrs[property].get.call(this); - return TemporalHelpers.toPrimitiveObserver(actual, value, property); - }, - }); - }); - ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { - Object.defineProperty(date, property, { - get() { - actual.push(`get ${formatPropertyName(property)}`); - return undefined; - }, - }); - }); - Object.defineProperty(date, "calendar", { - get() { - actual.push("get calendar"); - return "iso8601"; - }, - }); - - func(date); - assert.compareArray(actual, expected, "property getters not called"); - }, - - /* - * observeProperty(calls, object, propertyName, value): - * - * Defines an own property @object.@propertyName with value @value, that - * will log any calls to its accessors to the array @calls. - */ - observeProperty(calls, object, propertyName, value, objectName = "") { - Object.defineProperty(object, propertyName, { - get() { - calls.push(`get ${formatPropertyName(propertyName, objectName)}`); - return value; - }, - set() { - calls.push(`set ${formatPropertyName(propertyName, objectName)}`); - } - }); - }, - - /* - * observeMethod(calls, object, propertyName, value): - * - * Defines an own property @object.@propertyName with value @value, that - * will log any calls of @value to the array @calls. - */ - observeMethod(calls, object, propertyName, objectName = "") { - const method = object[propertyName]; - object[propertyName] = function () { - calls.push(`call ${formatPropertyName(propertyName, objectName)}`); - return method.apply(object, arguments); - }; - }, - - /* - * Used for substituteMethod to indicate default behavior instead of a - * substituted value - */ - SUBSTITUTE_SKIP: SKIP_SYMBOL, - - /* - * substituteMethod(object, propertyName, values): - * - * Defines an own property @object.@propertyName that will, for each - * subsequent call to the method previously defined as - * @object.@propertyName: - * - Call the method, if no more values remain - * - Call the method, if the value in @values for the corresponding call - * is SUBSTITUTE_SKIP - * - Otherwise, return the corresponding value in @value - */ - substituteMethod(object, propertyName, values) { - let calls = 0; - const method = object[propertyName]; - object[propertyName] = function () { - if (calls >= values.length) { - return method.apply(object, arguments); - } else if (values[calls] === SKIP_SYMBOL) { - calls++; - return method.apply(object, arguments); - } else { - return values[calls++]; - } - }; - }, - - /* - * propertyBagObserver(): - * Returns an object that behaves like the given propertyBag but tracks Get - * and Has operations on any of its properties, by appending messages to an - * array. If the value of a property in propertyBag is a primitive, the value - * of the returned object's property will additionally be a - * TemporalHelpers.toPrimitiveObserver that will track calls to its toString - * and valueOf methods in the same array. This is for the purpose of testing - * order of operations that are observable from user code. objectName is used - * in the log. - * If skipToPrimitive is given, it must be an array of property keys. Those - * properties will not have a TemporalHelpers.toPrimitiveObserver returned, - * and instead just be returned directly. - */ - propertyBagObserver(calls, propertyBag, objectName, skipToPrimitive) { - return new Proxy(propertyBag, { - ownKeys(target) { - calls.push(`ownKeys ${objectName}`); - return Reflect.ownKeys(target); - }, - getOwnPropertyDescriptor(target, key) { - calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); - return Reflect.getOwnPropertyDescriptor(target, key); - }, - get(target, key, receiver) { - calls.push(`get ${formatPropertyName(key, objectName)}`); - const result = Reflect.get(target, key, receiver); - if (result === undefined) { - return undefined; - } - if ((result !== null && typeof result === "object") || typeof result === "function") { - return result; - } - if (skipToPrimitive && skipToPrimitive.indexOf(key) >= 0) { - return result; - } - return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); - }, - has(target, key) { - calls.push(`has ${formatPropertyName(key, objectName)}`); - return Reflect.has(target, key); - }, - }); - }, - - /* - * Returns an object that will append logs of any Gets or Calls of its valueOf - * or toString properties to the array calls. Both valueOf and toString will - * return the actual primitiveValue. propertyName is used in the log. - */ - toPrimitiveObserver(calls, primitiveValue, propertyName) { - return { - get valueOf() { - calls.push(`get ${propertyName}.valueOf`); - return function () { - calls.push(`call ${propertyName}.valueOf`); - return primitiveValue; - }; - }, - get toString() { - calls.push(`get ${propertyName}.toString`); - return function () { - calls.push(`call ${propertyName}.toString`); - if (primitiveValue === undefined) return undefined; - return primitiveValue.toString(); - }; - }, - }; - }, - - /* - * An object containing further methods that return arrays of ISO strings, for - * testing parsers. - */ - ISO: { - /* - * PlainMonthDay strings that are not valid. - */ - plainMonthDayStringsInvalid() { - return [ - "11-18junk", - "11-18[u-ca=gregory]", - "11-18[u-ca=hebrew]", - "11-18[U-CA=iso8601]", - "11-18[u-CA=iso8601]", - "11-18[FOO=bar]", - "-999999-01-01[u-ca=gregory]", - "-999999-01-01[u-ca=chinese]", - "+999999-01-01[u-ca=gregory]", - "+999999-01-01[u-ca=chinese]", - ]; - }, - - /* - * PlainMonthDay strings that are valid and that should produce October 1st. - */ - plainMonthDayStringsValid() { - return [ - "10-01", - "1001", - "1965-10-01", - "1976-10-01T152330.1+00:00", - "19761001T15:23:30.1+00:00", - "1976-10-01T15:23:30.1+0000", - "1976-10-01T152330.1+0000", - "19761001T15:23:30.1+0000", - "19761001T152330.1+00:00", - "19761001T152330.1+0000", - "+001976-10-01T152330.1+00:00", - "+0019761001T15:23:30.1+00:00", - "+001976-10-01T15:23:30.1+0000", - "+001976-10-01T152330.1+0000", - "+0019761001T15:23:30.1+0000", - "+0019761001T152330.1+00:00", - "+0019761001T152330.1+0000", - "1976-10-01T15:23:00", - "1976-10-01T15:23", - "1976-10-01T15", - "1976-10-01", - "--10-01", - "--1001", - "-999999-10-01", - "-999999-10-01[u-ca=iso8601]", - "+999999-10-01", - "+999999-10-01[u-ca=iso8601]", - ]; - }, - - /* - * PlainTime strings that may be mistaken for PlainMonthDay or - * PlainYearMonth strings, and so require a time designator. - */ - plainTimeStringsAmbiguous() { - const ambiguousStrings = [ - "2021-12", // ambiguity between YYYY-MM and HHMM-UU - "2021-12[-12:00]", // ditto, TZ does not disambiguate - "1214", // ambiguity between MMDD and HHMM - "0229", // ditto, including MMDD that doesn't occur every year - "1130", // ditto, including DD that doesn't occur in every month - "12-14", // ambiguity between MM-DD and HH-UU - "12-14[-14:00]", // ditto, TZ does not disambiguate - "202112", // ambiguity between YYYYMM and HHMMSS - "202112[UTC]", // ditto, TZ does not disambiguate - ]; - // Adding a calendar annotation to one of these strings must not cause - // disambiguation in favour of time. - const stringsWithCalendar = ambiguousStrings.map((s) => s + "[u-ca=iso8601]"); - return ambiguousStrings.concat(stringsWithCalendar); - }, - - /* - * PlainTime strings that are of similar form to PlainMonthDay and - * PlainYearMonth strings, but are not ambiguous due to components that - * aren't valid as months or days. - */ - plainTimeStringsUnambiguous() { - return [ - "2021-13", // 13 is not a month - "202113", // ditto - "2021-13[-13:00]", // ditto - "202113[-13:00]", // ditto - "0000-00", // 0 is not a month - "000000", // ditto - "0000-00[UTC]", // ditto - "000000[UTC]", // ditto - "1314", // 13 is not a month - "13-14", // ditto - "1232", // 32 is not a day - "0230", // 30 is not a day in February - "0631", // 31 is not a day in June - "0000", // 0 is neither a month nor a day - "00-00", // ditto - ]; - }, - - /* - * PlainYearMonth-like strings that are not valid. - */ - plainYearMonthStringsInvalid() { - return [ - "2020-13", - "1976-11[u-ca=gregory]", - "1976-11[u-ca=hebrew]", - "1976-11[U-CA=iso8601]", - "1976-11[u-CA=iso8601]", - "1976-11[FOO=bar]", - "+999999-01", - "-999999-01", - ]; - }, - - /* - * PlainYearMonth-like strings that are valid and should produce November - * 1976 in the ISO 8601 calendar. - */ - plainYearMonthStringsValid() { - return [ - "1976-11", - "1976-11-10", - "1976-11-01T09:00:00+00:00", - "1976-11-01T00:00:00+05:00", - "197611", - "+00197611", - "1976-11-18T15:23:30.1-02:00", - "1976-11-18T152330.1+00:00", - "19761118T15:23:30.1+00:00", - "1976-11-18T15:23:30.1+0000", - "1976-11-18T152330.1+0000", - "19761118T15:23:30.1+0000", - "19761118T152330.1+00:00", - "19761118T152330.1+0000", - "+001976-11-18T152330.1+00:00", - "+0019761118T15:23:30.1+00:00", - "+001976-11-18T15:23:30.1+0000", - "+001976-11-18T152330.1+0000", - "+0019761118T15:23:30.1+0000", - "+0019761118T152330.1+00:00", - "+0019761118T152330.1+0000", - "1976-11-18T15:23", - "1976-11-18T15", - "1976-11-18", - ]; - }, - - /* - * PlainYearMonth-like strings that are valid and should produce November of - * the ISO year -9999. - */ - plainYearMonthStringsValidNegativeYear() { - return [ - "-009999-11", - ]; - }, - } -}; diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese-leap-month-uncommon.js b/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese-leap-month-uncommon.js deleted file mode 100644 index dd80a574bc69b..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese-leap-month-uncommon.js +++ /dev/null @@ -1,67 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -includes: [sm/non262-Temporal-PlainMonthDay-shell.js] -features: - - Temporal -description: | - pending -esid: pending ----*/ - -// Uncommon leap month with 30 days can be far into the past. Computing the -// results can take its time, therefore the test is marked as "slow". -// -// Month -> ISO year -// -// M01L 1461 -// M02L -// M03L -// M04L -// M05L -// M06L -// M07L -// M08L -// M09L -6482 -// M10L -4633 -// M11L -2172 -// M12L -179 -// -// See also "The Mathematics of the Chinese Calendar", Table 21 [1] for a -// distribution of leap months. -// -// [1] https://www.xirugu.com/CHI500/Dates_Time/Chinesecalender.pdf - -const monthCodes = [ - "M01L", - // M02L..M08L are common leap months. - "M09L", - "M10L", - "M11L", - "M12L", -]; - -const calendar = "chinese"; - -// Months can have up to 30 days. -const day = 30; - -for (let monthCode of monthCodes) { - let pmd = Temporal.PlainMonthDay.from({calendar, monthCode, day}); - assert.sameValue(pmd.monthCode, monthCode); - assert.sameValue(pmd.day, day); - - let constrain = Temporal.PlainMonthDay.from({calendar, monthCode, day: day + 1}, {overflow: "constrain"}); - assert.sameValue(constrain.monthCode, monthCode); - assert.sameValue(constrain.day, day); - assertSameISOFields(constrain, pmd); - - assert.throws(RangeError, () => { - Temporal.PlainMonthDay.from({calendar, monthCode, day: day + 1}, {overflow: "reject"}); - }); -} - - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese.js b/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese.js deleted file mode 100644 index dc6aad0efd169..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-chinese.js +++ /dev/null @@ -1,67 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -includes: [sm/non262-Temporal-PlainMonthDay-shell.js] -features: - - Temporal -description: | - pending -esid: pending ----*/ - -// Non-leap month should find a result in years around 1972. -// -// Month -> ISO year -// -// M01 1970 -// M02 1972 -// M03 1966 -// M04 1970 -// M05 1972 -// M06 1971 -// M07 1972 -// M08 1971 -// M09 1972 -// M10 1972 -// M11 1970 -// M12 1972 - -const monthCodes = [ - "M01", - "M02", - "M03", - "M04", - "M05", - "M06", - "M07", - "M08", - "M09", - "M10", - "M11", - "M12", -]; - -const calendar = "chinese"; - -// Months can have up to 30 days. -const day = 30; - -for (let monthCode of monthCodes) { - let pmd = Temporal.PlainMonthDay.from({calendar, monthCode, day}); - assert.sameValue(pmd.monthCode, monthCode); - assert.sameValue(pmd.day, day); - - let constrain = Temporal.PlainMonthDay.from({calendar, monthCode, day: day + 1}, {overflow: "constrain"}); - assert.sameValue(constrain.monthCode, monthCode); - assert.sameValue(constrain.day, day); - assertSameISOFields(constrain, pmd); - - assert.throws(RangeError, () => { - Temporal.PlainMonthDay.from({calendar, monthCode, day: day + 1}, {overflow: "reject"}); - }); -} - - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-coptic.js b/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-coptic.js deleted file mode 100644 index 6f8be5a4f1967..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/from-coptic.js +++ /dev/null @@ -1,40 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -includes: [sm/non262-Temporal-PlainMonthDay-shell.js] -features: - - Temporal -description: | - pending -esid: pending ----*/ - -// Input which has to skip the first two candidates when starting the search from -// December 31, 1972. -// -// Note: December 31, 1972 is year 1689 in the coptic calendar. -// -// 1. First candidate 1689-M13-05 ("1973-09-10[u-ca=coptic]") is after December 31, 1972. -// 2. Second candidate 1688-M13-05 ("1972-09-10[u-ca=coptic]") is before December 31, 1972, -// but day doesn't match. -// 3. Third candidate 1687-M13-06 ("1971-09-11[u-ca=coptic]") is a full match. -{ - let pmd = Temporal.PlainMonthDay.from({ - calendar: "coptic", - monthCode: "M13", - day: 7, - }); - assert.sameValue(pmd.monthCode, "M13"); - assert.sameValue(pmd.day, 6); - - let fields = ISOFields(pmd); - assert.sameValue(fields.calendar, "coptic"); - assert.sameValue(fields.isoYear, 1971); - assert.sameValue(fields.isoMonth, 9); - assert.sameValue(fields.isoDay, 11); -} - - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/result-not-after-1972-dec-31.js b/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/result-not-after-1972-dec-31.js deleted file mode 100644 index 385c7d6a42964..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/result-not-after-1972-dec-31.js +++ /dev/null @@ -1,86 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2024 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -includes: [sm/non262-Temporal-PlainMonthDay-shell.js] -features: - - Temporal -description: | - pending -esid: pending ----*/ - -const monthCodes = [ - "M01", - "M02", - "M03", - "M04", - "M05", - "M06", - "M07", - "M08", - "M09", - "M10", - "M11", - "M12", - - // Short epagomenal month. - "M13", - - // Leap months. - "M01L", - "M02L", - "M03L", - "M04L", - "M05L", - "M06L", - "M07L", - "M08L", - "M09L", - "M10L", - "M11L", - "M12L", -]; - -// Chinese and Dangi are excluded to avoid unnecessary slow-downs, see also -// from-chinese*.js tests. -const calendars = [ - "buddhist", - // "chinese", - "coptic", - // "dangi", - "ethiopic", - "ethioaa", - "gregory", - "hebrew", - "indian", - // Islamic calendars are broken in ICU4X 1.5. - // https://github.com/unicode-org/icu4x/issues/5069 - // "islamic", - "islamic-civil", - // "islamic-rgsa", - "islamic-tbla", - // "islamic-umalqura", - "japanese", - "persian", - "roc", -]; - -for (let calendar of calendars) { - const day = 31; - for (let monthCode of monthCodes) { - let pmd; - try { - pmd = Temporal.PlainMonthDay.from({calendar, monthCode, day}); - } catch { - continue; - } - assert.sameValue(pmd.monthCode, monthCode); - assert.sameValue(pmd.day <= day, true); - assert.sameValue(ISOFields(pmd).isoYear <= 1972, true); - } -} - - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/shell.js b/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/shell.js deleted file mode 100644 index ec75bf58f723d..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/PlainMonthDay/shell.js +++ /dev/null @@ -1,50 +0,0 @@ -// GENERATED, DO NOT EDIT -// file: non262-Temporal-PlainMonthDay-shell.js -/*--- -defines: [ISOFields, assertSameISOFields] ----*/ - -function ISOFields(monthDay) { - let re = /^(?-?\d{4,6})-(?\d{2})-(?\d{2})\[u-ca=(?[\w\-]+)\]$/; - - let str = monthDay.toString({calendarName: "always"}); - let match = str.match(re); - assert.sameValue(match !== null, true, `can't match: ${str}`); - - let {year, month, day, calendar} = match.groups; - let isoYear = Number(year); - let isoMonth = Number(month); - let isoDay = Number(day); - - let date = Temporal.PlainDate.from(str); - let isoDate = date.withCalendar("iso8601"); - - assert.sameValue(calendar, date.calendarId); - assert.sameValue(isoYear, isoDate.year); - assert.sameValue(isoMonth, isoDate.month); - assert.sameValue(isoDay, isoDate.day); - - return { - isoYear, - isoMonth, - isoDay, - calendar, - }; -} - -function assertSameISOFields(actual, expected) { - let actualFields = ISOFields(actual); - let expectedFields = ISOFields(expected); - - assert.sameValue(typeof actualFields.isoYear, "number"); - assert.sameValue(typeof actualFields.isoMonth, "number"); - assert.sameValue(typeof actualFields.isoDay, "number"); - - assert.sameValue(actualFields.isoMonth > 0, true); - assert.sameValue(actualFields.isoDay > 0, true); - - assert.sameValue(actualFields.isoYear, expectedFields.isoYear); - assert.sameValue(actualFields.isoMonth, expectedFields.isoMonth); - assert.sameValue(actualFields.isoDay, expectedFields.isoDay); - assert.sameValue(actualFields.calendar, expectedFields.calendar); -} diff --git a/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/browser.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/shell.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/zones-and-links.js b/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/zones-and-links.js deleted file mode 100644 index a302857029daa..0000000000000 --- a/js/src/tests/test262/staging/sm/Temporal/ZonedDateTime/zones-and-links.js +++ /dev/null @@ -1,674 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally -// Copyright (C) 2025 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -features: - - Temporal -description: | - pending -esid: pending ----*/ - -// Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2024b -const zones = [ - "Africa/Abidjan", - "Africa/Accra", - "Africa/Addis_Ababa", - "Africa/Algiers", - "Africa/Asmara", - "Africa/Bamako", - "Africa/Bangui", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Blantyre", - "Africa/Brazzaville", - "Africa/Bujumbura", - "Africa/Cairo", - "Africa/Casablanca", - "Africa/Ceuta", - "Africa/Conakry", - "Africa/Dakar", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Douala", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Kigali", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Lome", - "Africa/Luanda", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Malabo", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - "Africa/Mogadishu", - "Africa/Monrovia", - "Africa/Nairobi", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Porto-Novo", - "Africa/Sao_Tome", - "Africa/Tripoli", - "Africa/Tunis", - "Africa/Windhoek", - "America/Adak", - "America/Anchorage", - "America/Anguilla", - "America/Antigua", - "America/Araguaina", - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Aruba", - "America/Asuncion", - "America/Atikokan", - "America/Bahia", - "America/Bahia_Banderas", - "America/Barbados", - "America/Belem", - "America/Belize", - "America/Blanc-Sablon", - "America/Boa_Vista", - "America/Bogota", - "America/Boise", - "America/Cambridge_Bay", - "America/Campo_Grande", - "America/Cancun", - "America/Caracas", - "America/Cayenne", - "America/Cayman", - "America/Chicago", - "America/Chihuahua", - "America/Ciudad_Juarez", - "America/Costa_Rica", - "America/Creston", - "America/Cuiaba", - "America/Curacao", - "America/Danmarkshavn", - "America/Dawson", - "America/Dawson_Creek", - "America/Denver", - "America/Detroit", - "America/Dominica", - "America/Edmonton", - "America/Eirunepe", - "America/El_Salvador", - "America/Fort_Nelson", - "America/Fortaleza", - "America/Glace_Bay", - "America/Goose_Bay", - "America/Grand_Turk", - "America/Grenada", - "America/Guadeloupe", - "America/Guatemala", - "America/Guayaquil", - "America/Guyana", - "America/Halifax", - "America/Havana", - "America/Hermosillo", - "America/Indiana/Indianapolis", - "America/Indiana/Knox", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Tell_City", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Inuvik", - "America/Iqaluit", - "America/Jamaica", - "America/Juneau", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Kralendijk", - "America/La_Paz", - "America/Lima", - "America/Los_Angeles", - "America/Lower_Princes", - "America/Maceio", - "America/Managua", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Matamoros", - "America/Mazatlan", - "America/Menominee", - "America/Merida", - "America/Metlakatla", - "America/Mexico_City", - "America/Miquelon", - "America/Moncton", - "America/Monterrey", - "America/Montevideo", - "America/Montserrat", - "America/Nassau", - "America/New_York", - "America/Nome", - "America/Noronha", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Nuuk", - "America/Ojinaga", - "America/Panama", - "America/Paramaribo", - "America/Phoenix", - "America/Port-au-Prince", - "America/Port_of_Spain", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Punta_Arenas", - "America/Rankin_Inlet", - "America/Recife", - "America/Regina", - "America/Resolute", - "America/Rio_Branco", - "America/Santarem", - "America/Santiago", - "America/Santo_Domingo", - "America/Sao_Paulo", - "America/Scoresbysund", - "America/Sitka", - "America/St_Barthelemy", - "America/St_Johns", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Thule", - "America/Tijuana", - "America/Toronto", - "America/Tortola", - "America/Vancouver", - "America/Whitehorse", - "America/Winnipeg", - "America/Yakutat", - "Antarctica/Casey", - "Antarctica/Davis", - "Antarctica/DumontDUrville", - "Antarctica/Macquarie", - "Antarctica/Mawson", - "Antarctica/McMurdo", - "Antarctica/Palmer", - "Antarctica/Rothera", - "Antarctica/Syowa", - "Antarctica/Troll", - "Antarctica/Vostok", - "Arctic/Longyearbyen", - "Asia/Aden", - "Asia/Almaty", - "Asia/Amman", - "Asia/Anadyr", - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Atyrau", - "Asia/Baghdad", - "Asia/Bahrain", - "Asia/Baku", - "Asia/Bangkok", - "Asia/Barnaul", - "Asia/Beirut", - "Asia/Bishkek", - "Asia/Brunei", - "Asia/Chita", - "Asia/Colombo", - "Asia/Damascus", - "Asia/Dhaka", - "Asia/Dili", - "Asia/Dubai", - "Asia/Dushanbe", - "Asia/Famagusta", - "Asia/Gaza", - "Asia/Hebron", - "Asia/Ho_Chi_Minh", - "Asia/Hong_Kong", - "Asia/Hovd", - "Asia/Irkutsk", - "Asia/Jakarta", - "Asia/Jayapura", - "Asia/Jerusalem", - "Asia/Kabul", - "Asia/Kamchatka", - "Asia/Karachi", - "Asia/Kathmandu", - "Asia/Khandyga", - "Asia/Kolkata", - "Asia/Krasnoyarsk", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Asia/Kuwait", - "Asia/Macau", - "Asia/Magadan", - "Asia/Makassar", - "Asia/Manila", - "Asia/Muscat", - "Asia/Nicosia", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Oral", - "Asia/Phnom_Penh", - "Asia/Pontianak", - "Asia/Pyongyang", - "Asia/Qatar", - "Asia/Qostanay", - "Asia/Qyzylorda", - "Asia/Riyadh", - "Asia/Sakhalin", - "Asia/Samarkand", - "Asia/Seoul", - "Asia/Shanghai", - "Asia/Singapore", - "Asia/Srednekolymsk", - "Asia/Taipei", - "Asia/Tashkent", - "Asia/Tbilisi", - "Asia/Tehran", - "Asia/Thimphu", - "Asia/Tokyo", - "Asia/Tomsk", - "Asia/Ulaanbaatar", - "Asia/Urumqi", - "Asia/Ust-Nera", - "Asia/Vientiane", - "Asia/Vladivostok", - "Asia/Yakutsk", - "Asia/Yangon", - "Asia/Yekaterinburg", - "Asia/Yerevan", - "Atlantic/Azores", - "Atlantic/Bermuda", - "Atlantic/Canary", - "Atlantic/Cape_Verde", - "Atlantic/Faroe", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/South_Georgia", - "Atlantic/St_Helena", - "Atlantic/Stanley", - "Australia/Adelaide", - "Australia/Brisbane", - "Australia/Broken_Hill", - "Australia/Darwin", - "Australia/Eucla", - "Australia/Hobart", - "Australia/Lindeman", - "Australia/Lord_Howe", - "Australia/Melbourne", - "Australia/Perth", - "Australia/Sydney", - "Etc/GMT+1", - "Etc/GMT+10", - "Etc/GMT+11", - "Etc/GMT+12", - "Etc/GMT+2", - "Etc/GMT+3", - "Etc/GMT+4", - "Etc/GMT+5", - "Etc/GMT+6", - "Etc/GMT+7", - "Etc/GMT+8", - "Etc/GMT+9", - "Etc/GMT-1", - "Etc/GMT-10", - "Etc/GMT-11", - "Etc/GMT-12", - "Etc/GMT-13", - "Etc/GMT-14", - "Etc/GMT-2", - "Etc/GMT-3", - "Etc/GMT-4", - "Etc/GMT-5", - "Etc/GMT-6", - "Etc/GMT-7", - "Etc/GMT-8", - "Etc/GMT-9", - "Europe/Amsterdam", - "Europe/Andorra", - "Europe/Astrakhan", - "Europe/Athens", - "Europe/Belgrade", - "Europe/Berlin", - "Europe/Bratislava", - "Europe/Brussels", - "Europe/Bucharest", - "Europe/Budapest", - "Europe/Busingen", - "Europe/Chisinau", - "Europe/Copenhagen", - "Europe/Dublin", - "Europe/Gibraltar", - "Europe/Guernsey", - "Europe/Helsinki", - "Europe/Isle_of_Man", - "Europe/Istanbul", - "Europe/Jersey", - "Europe/Kaliningrad", - "Europe/Kirov", - "Europe/Kyiv", - "Europe/Lisbon", - "Europe/Ljubljana", - "Europe/London", - "Europe/Luxembourg", - "Europe/Madrid", - "Europe/Malta", - "Europe/Mariehamn", - "Europe/Minsk", - "Europe/Monaco", - "Europe/Moscow", - "Europe/Oslo", - "Europe/Paris", - "Europe/Podgorica", - "Europe/Prague", - "Europe/Riga", - "Europe/Rome", - "Europe/Samara", - "Europe/San_Marino", - "Europe/Sarajevo", - "Europe/Saratov", - "Europe/Simferopol", - "Europe/Skopje", - "Europe/Sofia", - "Europe/Stockholm", - "Europe/Tallinn", - "Europe/Tirane", - "Europe/Ulyanovsk", - "Europe/Vaduz", - "Europe/Vatican", - "Europe/Vienna", - "Europe/Vilnius", - "Europe/Volgograd", - "Europe/Warsaw", - "Europe/Zagreb", - "Europe/Zurich", - "Indian/Antananarivo", - "Indian/Chagos", - "Indian/Christmas", - "Indian/Cocos", - "Indian/Comoro", - "Indian/Kerguelen", - "Indian/Mahe", - "Indian/Maldives", - "Indian/Mauritius", - "Indian/Mayotte", - "Indian/Reunion", - "Pacific/Apia", - "Pacific/Auckland", - "Pacific/Bougainville", - "Pacific/Chatham", - "Pacific/Chuuk", - "Pacific/Easter", - "Pacific/Efate", - "Pacific/Fakaofo", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Galapagos", - "Pacific/Gambier", - "Pacific/Guadalcanal", - "Pacific/Guam", - "Pacific/Honolulu", - "Pacific/Kanton", - "Pacific/Kiritimati", - "Pacific/Kosrae", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Marquesas", - "Pacific/Midway", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Norfolk", - "Pacific/Noumea", - "Pacific/Pago_Pago", - "Pacific/Palau", - "Pacific/Pitcairn", - "Pacific/Pohnpei", - "Pacific/Port_Moresby", - "Pacific/Rarotonga", - "Pacific/Saipan", - "Pacific/Tahiti", - "Pacific/Tarawa", - "Pacific/Tongatapu", - "Pacific/Wake", - "Pacific/Wallis", - "UTC", -]; -const links = { - "Africa/Asmera": "Africa/Asmara", - "Africa/Timbuktu": "Africa/Bamako", - "America/Argentina/ComodRivadavia": "America/Argentina/Catamarca", - "America/Atka": "America/Adak", - "America/Buenos_Aires": "America/Argentina/Buenos_Aires", - "America/Catamarca": "America/Argentina/Catamarca", - "America/Coral_Harbour": "America/Atikokan", - "America/Cordoba": "America/Argentina/Cordoba", - "America/Ensenada": "America/Tijuana", - "America/Fort_Wayne": "America/Indiana/Indianapolis", - "America/Godthab": "America/Nuuk", - "America/Indianapolis": "America/Indiana/Indianapolis", - "America/Jujuy": "America/Argentina/Jujuy", - "America/Knox_IN": "America/Indiana/Knox", - "America/Louisville": "America/Kentucky/Louisville", - "America/Mendoza": "America/Argentina/Mendoza", - "America/Montreal": "America/Toronto", - "America/Nipigon": "America/Toronto", - "America/Pangnirtung": "America/Iqaluit", - "America/Porto_Acre": "America/Rio_Branco", - "America/Rainy_River": "America/Winnipeg", - "America/Rosario": "America/Argentina/Cordoba", - "America/Santa_Isabel": "America/Tijuana", - "America/Shiprock": "America/Denver", - "America/Thunder_Bay": "America/Toronto", - "America/Virgin": "America/St_Thomas", - "America/Yellowknife": "America/Edmonton", - "Antarctica/South_Pole": "Antarctica/McMurdo", - "Asia/Ashkhabad": "Asia/Ashgabat", - "Asia/Calcutta": "Asia/Kolkata", - "Asia/Choibalsan": "Asia/Ulaanbaatar", - "Asia/Chongqing": "Asia/Shanghai", - "Asia/Chungking": "Asia/Shanghai", - "Asia/Dacca": "Asia/Dhaka", - "Asia/Harbin": "Asia/Shanghai", - "Asia/Istanbul": "Europe/Istanbul", - "Asia/Kashgar": "Asia/Urumqi", - "Asia/Katmandu": "Asia/Kathmandu", - "Asia/Macao": "Asia/Macau", - "Asia/Rangoon": "Asia/Yangon", - "Asia/Saigon": "Asia/Ho_Chi_Minh", - "Asia/Tel_Aviv": "Asia/Jerusalem", - "Asia/Thimbu": "Asia/Thimphu", - "Asia/Ujung_Pandang": "Asia/Makassar", - "Asia/Ulan_Bator": "Asia/Ulaanbaatar", - "Atlantic/Faeroe": "Atlantic/Faroe", - "Atlantic/Jan_Mayen": "Arctic/Longyearbyen", - "Australia/ACT": "Australia/Sydney", - "Australia/Canberra": "Australia/Sydney", - "Australia/Currie": "Australia/Hobart", - "Australia/LHI": "Australia/Lord_Howe", - "Australia/NSW": "Australia/Sydney", - "Australia/North": "Australia/Darwin", - "Australia/Queensland": "Australia/Brisbane", - "Australia/South": "Australia/Adelaide", - "Australia/Tasmania": "Australia/Hobart", - "Australia/Victoria": "Australia/Melbourne", - "Australia/West": "Australia/Perth", - "Australia/Yancowinna": "Australia/Broken_Hill", - "Brazil/Acre": "America/Rio_Branco", - "Brazil/DeNoronha": "America/Noronha", - "Brazil/East": "America/Sao_Paulo", - "Brazil/West": "America/Manaus", - "CET": "Europe/Brussels", - "CST6CDT": "America/Chicago", - "Canada/Atlantic": "America/Halifax", - "Canada/Central": "America/Winnipeg", - "Canada/Eastern": "America/Toronto", - "Canada/Mountain": "America/Edmonton", - "Canada/Newfoundland": "America/St_Johns", - "Canada/Pacific": "America/Vancouver", - "Canada/Saskatchewan": "America/Regina", - "Canada/Yukon": "America/Whitehorse", - "Chile/Continental": "America/Santiago", - "Chile/EasterIsland": "Pacific/Easter", - "Cuba": "America/Havana", - "EET": "Europe/Athens", - "EST": "America/Panama", - "EST5EDT": "America/New_York", - "Egypt": "Africa/Cairo", - "Eire": "Europe/Dublin", - "Etc/GMT": "UTC", - "Etc/GMT+0": "UTC", - "Etc/GMT-0": "UTC", - "Etc/GMT0": "UTC", - "Etc/Greenwich": "UTC", - "Etc/UCT": "UTC", - "Etc/UTC": "UTC", - "Etc/Universal": "UTC", - "Etc/Zulu": "UTC", - "Europe/Belfast": "Europe/London", - "Europe/Kiev": "Europe/Kyiv", - "Europe/Nicosia": "Asia/Nicosia", - "Europe/Tiraspol": "Europe/Chisinau", - "Europe/Uzhgorod": "Europe/Kyiv", - "Europe/Zaporozhye": "Europe/Kyiv", - "GB": "Europe/London", - "GB-Eire": "Europe/London", - "GMT": "UTC", - "GMT+0": "UTC", - "GMT-0": "UTC", - "GMT0": "UTC", - "Greenwich": "UTC", - "HST": "Pacific/Honolulu", - "Hongkong": "Asia/Hong_Kong", - "Iceland": "Atlantic/Reykjavik", - "Iran": "Asia/Tehran", - "Israel": "Asia/Jerusalem", - "Jamaica": "America/Jamaica", - "Japan": "Asia/Tokyo", - "Kwajalein": "Pacific/Kwajalein", - "Libya": "Africa/Tripoli", - "MET": "Europe/Brussels", - "MST": "America/Phoenix", - "MST7MDT": "America/Denver", - "Mexico/BajaNorte": "America/Tijuana", - "Mexico/BajaSur": "America/Mazatlan", - "Mexico/General": "America/Mexico_City", - "NZ": "Pacific/Auckland", - "NZ-CHAT": "Pacific/Chatham", - "Navajo": "America/Denver", - "PRC": "Asia/Shanghai", - "PST8PDT": "America/Los_Angeles", - "Pacific/Enderbury": "Pacific/Kanton", - "Pacific/Johnston": "Pacific/Honolulu", - "Pacific/Ponape": "Pacific/Pohnpei", - "Pacific/Samoa": "Pacific/Pago_Pago", - "Pacific/Truk": "Pacific/Chuuk", - "Pacific/Yap": "Pacific/Chuuk", - "Poland": "Europe/Warsaw", - "Portugal": "Europe/Lisbon", - "ROC": "Asia/Taipei", - "ROK": "Asia/Seoul", - "Singapore": "Asia/Singapore", - "Turkey": "Europe/Istanbul", - "UCT": "UTC", - "US/Alaska": "America/Anchorage", - "US/Aleutian": "America/Adak", - "US/Arizona": "America/Phoenix", - "US/Central": "America/Chicago", - "US/East-Indiana": "America/Indiana/Indianapolis", - "US/Eastern": "America/New_York", - "US/Hawaii": "Pacific/Honolulu", - "US/Indiana-Starke": "America/Indiana/Knox", - "US/Michigan": "America/Detroit", - "US/Mountain": "America/Denver", - "US/Pacific": "America/Los_Angeles", - "US/Samoa": "Pacific/Pago_Pago", - "Universal": "UTC", - "W-SU": "Europe/Moscow", - "WET": "Europe/Lisbon", - "Zulu": "UTC", -}; - -let epochNanoseconds = [ - new Temporal.PlainDate(1900, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(1950, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(1960, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(1970, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(1980, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(1990, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(2000, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(2010, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(2020, 1, 1).toZonedDateTime("UTC").epochNanoseconds, - new Temporal.PlainDate(2030, 1, 1).toZonedDateTime("UTC").epochNanoseconds, -]; - -function timeZoneId(zdt) { - let str = zdt.toString(); - let m = str.match(/(?<=\[)[\w\/_+-]+(?=\])/); - assert.sameValue(m !== null, true, str); - return m[0]; -} - -for (let zone of zones) { - let zdt = new Temporal.ZonedDateTime(0n, zone); - - assert.sameValue(zdt.timeZoneId, zone); - assert.sameValue(timeZoneId(zdt), zone); -} - -for (let [link, zone] of Object.entries(links)) { - assert.sameValue(link === zone, false, `link=${link}, zone=${zone}`); - assert.sameValue(zones.includes(zone), true, `zone=${zone}`); - - let zdtLink = new Temporal.ZonedDateTime(0n, link); - let zdtZone = new Temporal.ZonedDateTime(0n, zone); - - assert.sameValue(zdtLink.timeZoneId, link); - assert.sameValue(timeZoneId(zdtLink), link); - - assert.sameValue(zdtZone.timeZoneId, zone); - assert.sameValue(timeZoneId(zdtZone), zone); - - assert.sameValue(zdtLink.equals(zdtZone), true, `link=${link}, zone=${zone}`); - - assert.sameValue( - zdtLink.offsetNanoseconds, - zdtZone.offsetNanoseconds, - `link=${link}, zone=${zone}` - ); - - for (let epochNs of epochNanoseconds) { - assert.sameValue( - new Temporal.ZonedDateTime(epochNs, link).offsetNanoseconds, - new Temporal.ZonedDateTime(epochNs, zone).offsetNanoseconds, - `link=${link}, zone=${zone}, epochNs=${epochNs}` - ); - } -} - - -reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/sm/Temporal/browser.js b/js/src/tests/test262/staging/sm/Temporal/browser.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/js/src/tests/test262/staging/sm/Temporal/shell.js b/js/src/tests/test262/staging/sm/Temporal/shell.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/js/src/tests/test262/staging/sm/TypedArray/toLocaleString-detached.js b/js/src/tests/test262/staging/sm/TypedArray/toLocaleString-detached.js index 79066fc6ee48c..05e0588b42fac 100644 --- a/js/src/tests/test262/staging/sm/TypedArray/toLocaleString-detached.js +++ b/js/src/tests/test262/staging/sm/TypedArray/toLocaleString-detached.js @@ -10,6 +10,8 @@ esid: pending const originalNumberToLocaleString = Number.prototype.toLocaleString; +const separator = ["", ""].toLocaleString(); + // Throws if array buffer is detached. for (let constructor of typedArrayConstructors) { let typedArray = new constructor(42); @@ -37,7 +39,7 @@ for (let constructor of typedArrayConstructors) { // And no error if more than one element is present. detached = false; typedArray = new constructor(2); - assert.sameValue(typedArray.toLocaleString(), "0,"); + assert.sameValue(typedArray.toLocaleString(), "0" + separator); assert.sameValue(detached, true); } Number.prototype.toLocaleString = originalNumberToLocaleString; diff --git a/js/src/tests/test262/staging/sm/extensions/regress-645160.js b/js/src/tests/test262/staging/sm/extensions/regress-645160.js deleted file mode 100644 index 4971f3df40e89..0000000000000 --- a/js/src/tests/test262/staging/sm/extensions/regress-645160.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/*--- -flags: - - noStrict -description: | - pending -esid: pending ----*/ -function potatoMasher(obj, arg) { this.eval(arg); } -potatoMasher(this, "var s = Error().stack"); -assert.sameValue(/potatoMasher/.exec(s) instanceof Array, true); - - -reportCompare(0, 0); diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp index 27624fb264eb6..da9c2511a0fa1 100644 --- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -44,6 +44,7 @@ #include "gc/StoreBuffer-inl.h" #include "vm/NativeObject-inl.h" #include "vm/PlainObject-inl.h" // js::PlainObject::createWithTemplate +#include "vm/Shape-inl.h" // js::GetPropertyAttributes using namespace js; @@ -1808,10 +1809,9 @@ static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni, } // Check whether id is still to come. - Rooted idStr(cx); IteratorProperty* cursor = ni->nextProperty(); for (; cursor < ni->propertiesEnd(); ++cursor) { - idStr = cursor->asString(); + JSLinearString* idStr = cursor->asString(); // Common case: both strings are atoms. if (idStr->isAtom() && str->isAtom()) { if (idStr != str) { @@ -1825,27 +1825,25 @@ static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni, // Check whether another property along the prototype chain became // visible as a result of this deletion. - RootedObject proto(cx); - if (!GetPrototype(cx, obj, &proto)) { - return false; - } - if (proto) { - RootedId id(cx); - RootedValue idv(cx, StringValue(idStr)); - if (!PrimitiveValueToId(cx, idv, &id)) { - return false; - } - - Rooted> desc(cx); - RootedObject holder(cx); - if (!GetPropertyDescriptor(cx, proto, id, &desc, &holder)) { - return false; - } - - // If deletion just made something up the chain visible, no need to - // do anything. - if (desc.isSome() && desc->enumerable()) { - return true; + if (obj->hasStaticPrototype()) { + JSObject* proto = obj->staticPrototype(); + if (proto) { + JSAtom* atom = AtomizeString(cx, str); + if (!atom) { + return false; + } + PropertyKey key = AtomToId(atom); + NativeObject* holder = nullptr; + PropertyResult prop; + if (LookupPropertyPure(cx, proto, key, &holder, &prop) && + prop.isFound()) { + // If deletion just made something up the chain visible, no need to + // do anything. + JS::PropertyAttributes attrs = GetPropertyAttributes(holder, prop); + if (attrs.enumerable()) { + return true; + } + } } } diff --git a/layout/style/CachedInheritingStyles.cpp b/layout/style/CachedInheritingStyles.cpp index da6e4d24e66ff..faffcddc940fc 100644 --- a/layout/style/CachedInheritingStyles.cpp +++ b/layout/style/CachedInheritingStyles.cpp @@ -7,54 +7,70 @@ #include "mozilla/CachedInheritingStyles.h" #include "mozilla/ComputedStyle.h" +#include "mozilla/PseudoStyleType.h" #include "nsCOMPtr.h" #include "nsWindowSizes.h" namespace mozilla { -void CachedInheritingStyles::Insert(ComputedStyle* aStyle) { +void CachedInheritingStyles::Insert(ComputedStyle* aStyle, + nsAtom* aFunctionalPseudoParameter) { MOZ_ASSERT(aStyle); MOZ_ASSERT(aStyle->IsInheritingAnonBox() || aStyle->IsLazilyCascadedPseudoElement()); - if (IsEmpty()) { + // For entries with a functional parameter, we always use indirect storage + // since direct mode only stores a ComputedStyle* without the parameter. + const bool needsIndirect = aFunctionalPseudoParameter != nullptr; + + if (IsEmpty() && !needsIndirect) { RefPtr s = aStyle; mBits = reinterpret_cast(s.forget().take()); MOZ_ASSERT(!IsEmpty() && !IsIndirect()); } else if (IsIndirect()) { - AsIndirect()->AppendElement(aStyle); + AsIndirect()->AppendElement( + CachedStyleEntry{aStyle, aFunctionalPseudoParameter}); } else { IndirectCache* cache = new IndirectCache(); - cache->AppendElement(dont_AddRef(AsDirect())); - cache->AppendElement(aStyle); + if (!IsEmpty()) { + cache->AppendElement(CachedStyleEntry{dont_AddRef(AsDirect()), nullptr}); + } + cache->AppendElement(CachedStyleEntry{aStyle, aFunctionalPseudoParameter}); mBits = reinterpret_cast(cache) | 1; MOZ_ASSERT(IsIndirect()); } } -ComputedStyle* CachedInheritingStyles::Lookup(PseudoStyleType aType) const { - MOZ_ASSERT(PseudoStyle::IsPseudoElement(aType) || - PseudoStyle::IsInheritingAnonBox(aType)); +ComputedStyle* CachedInheritingStyles::Lookup( + const PseudoStyleRequest& aRequest) const { + MOZ_ASSERT(PseudoStyle::IsPseudoElement(aRequest.mType) || + PseudoStyle::IsInheritingAnonBox(aRequest.mType)); if (IsIndirect()) { - for (auto& style : *AsIndirect()) { - if (style->GetPseudoType() == aType) { - return style; + for (const auto& entry : *AsIndirect()) { + if (entry.mStyle->GetPseudoType() == aRequest.mType && + entry.mFunctionalPseudoParameter == aRequest.mIdentifier) { + return entry.mStyle; } } return nullptr; } + // Direct mode only stores non-functional entries. + if (aRequest.mIdentifier) { + return nullptr; + } + ComputedStyle* direct = AsDirect(); - return direct && direct->GetPseudoType() == aType ? direct : nullptr; + return direct && direct->GetPseudoType() == aRequest.mType ? direct : nullptr; } void CachedInheritingStyles::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const { if (IsIndirect()) { - for (auto& style : *AsIndirect()) { - if (!aSizes.mState.HaveSeenPtr(style)) { - style->AddSizeOfIncludingThis(aSizes, aCVsSize); + for (const auto& entry : *AsIndirect()) { + if (!aSizes.mState.HaveSeenPtr(entry.mStyle)) { + entry.mStyle->AddSizeOfIncludingThis(aSizes, aCVsSize); } } diff --git a/layout/style/CachedInheritingStyles.h b/layout/style/CachedInheritingStyles.h index cfe86594ca500..27896ebb992db 100644 --- a/layout/style/CachedInheritingStyles.h +++ b/layout/style/CachedInheritingStyles.h @@ -15,20 +15,30 @@ class nsWindowSizes; namespace mozilla { +struct PseudoStyleRequest; enum class PseudoStyleType : uint8_t; class ComputedStyle; +// Entry in the cached inheriting styles cache. Stores the style and, for +// functional pseudo-elements like ::highlight(name), the functional parameter. +struct CachedStyleEntry { + RefPtr mStyle; + RefPtr mFunctionalPseudoParameter; +}; + // Cache of anonymous box and lazy pseudo styles that inherit from a given // style. // // To minimize memory footprint, the cache is word-sized with a tagged pointer -// If there is only one entry, it's stored inline. If there are more, they're -// stored in an out-of-line buffer. See bug 1429126 comment 0 and comment 1 for -// the measurements and rationale that influenced the design. +// If there is only one entry without a functional parameter, it's stored +// inline. If there are more entries, or the entry has a functional parameter, +// they're stored in an out-of-line buffer. See bug 1429126 comment 0 and +// comment 1 for the measurements and rationale that influenced the design. class CachedInheritingStyles { public: - void Insert(ComputedStyle* aStyle); - ComputedStyle* Lookup(PseudoStyleType) const; + void Insert(ComputedStyle* aStyle, + nsAtom* aFunctionalPseudoParameter = nullptr); + ComputedStyle* Lookup(const PseudoStyleRequest& aRequest) const; CachedInheritingStyles() : mBits(0) {} ~CachedInheritingStyles() { @@ -43,7 +53,7 @@ class CachedInheritingStyles { private: // See bug 1429126 comment 1 for the choice of four here. - typedef AutoTArray, 4> IndirectCache; + using IndirectCache = AutoTArray; bool IsEmpty() const { return !mBits; } bool IsIndirect() const { return (mBits & 1); } diff --git a/layout/style/ComputedStyle.cpp b/layout/style/ComputedStyle.cpp index fc30f5350f8d0..1c593ac061e55 100644 --- a/layout/style/ComputedStyle.cpp +++ b/layout/style/ComputedStyle.cpp @@ -391,14 +391,15 @@ Maybe ComputedStyle::LookupStruct(const nsACString& aName) { #endif // DEBUG ComputedStyle* ComputedStyle::GetCachedLazyPseudoStyle( - PseudoStyleType aPseudo) const { - MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo)); + const PseudoStyleRequest& aRequest) const { + MOZ_ASSERT(PseudoStyle::IsPseudoElement(aRequest.mType)); - if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) { + if (nsCSSPseudoElements::PseudoElementSupportsUserActionState( + aRequest.mType)) { return nullptr; } - return mCachedInheritingStyles.Lookup(aPseudo); + return mCachedInheritingStyles.Lookup(aRequest); } MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf) diff --git a/layout/style/ComputedStyle.h b/layout/style/ComputedStyle.h index 6a800b7a6b14c..7e04dc80d4518 100644 --- a/layout/style/ComputedStyle.h +++ b/layout/style/ComputedStyle.h @@ -184,6 +184,11 @@ class ComputedStyle { return bool(Flags() & Flag::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE); } + // Whether the style uses container query units (cqw, cqh, etc.). + bool UsesContainerUnits() const { + return bool(Flags() & Flag::USES_CONTAINER_UNITS); + } + // Is the only link whose visitedness is allowed to influence the // style of the node this ComputedStyle is for (which is that element // or its nearest ancestor that is a link) visited? @@ -207,18 +212,20 @@ class ComputedStyle { ComputedStyle* GetCachedInheritingAnonBoxStyle( PseudoStyleType aPseudoType) const { MOZ_ASSERT(PseudoStyle::IsInheritingAnonBox(aPseudoType)); - return mCachedInheritingStyles.Lookup(aPseudoType); + return mCachedInheritingStyles.Lookup(PseudoStyleRequest(aPseudoType)); } void SetCachedInheritedAnonBoxStyle(ComputedStyle* aStyle) { mCachedInheritingStyles.Insert(aStyle); } - ComputedStyle* GetCachedLazyPseudoStyle(PseudoStyleType aPseudo) const; + ComputedStyle* GetCachedLazyPseudoStyle(const PseudoStyleRequest&) const; - void SetCachedLazyPseudoStyle(ComputedStyle* aStyle) { + void SetCachedLazyPseudoStyle(ComputedStyle* aStyle, + nsAtom* aFunctionalPseudoParameter) { MOZ_ASSERT(aStyle->IsPseudoElement()); - MOZ_ASSERT(!GetCachedLazyPseudoStyle(aStyle->GetPseudoType())); + MOZ_ASSERT(!GetCachedLazyPseudoStyle( + {aStyle->GetPseudoType(), aFunctionalPseudoParameter})); MOZ_ASSERT(aStyle->IsLazilyCascadedPseudoElement()); // Since we're caching lazy pseudo styles on the ComputedValues of the @@ -235,7 +242,13 @@ class ComputedStyle { return; } - mCachedInheritingStyles.Insert(aStyle); + // Styles using container query units can become stale when container sizes + // change without the parent style changing, so don't cache them. + if (aStyle->UsesContainerUnits()) { + return; + } + + mCachedInheritingStyles.Insert(aStyle, aFunctionalPseudoParameter); } #define GENERATE_ACCESSOR(name_) \ @@ -356,6 +369,8 @@ class ComputedStyle { ServoComputedData mSource; // A cache of anonymous box and lazy pseudo styles inheriting from this style. + // For functional pseudo-elements like ::highlight(name), the functional + // parameter is stored alongside the style in the cache. CachedInheritingStyles mCachedInheritingStyles; const PseudoStyleType mPseudoType; diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 023b79c2009a5..410ddfcc91b61 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -455,13 +455,12 @@ already_AddRefed ServoStyleSet::ResolvePseudoElementStyle( UpdateStylistIfNeeded(); MOZ_ASSERT(PseudoStyle::IsPseudoElement(aType)); - // caching is done using `aType` only, therefore results would be wrong for - // pseudos with functional parameters (e.g. `::highlight(foo)`). const bool cacheable = - !aFunctionalPseudoParameter && LazyPseudoIsCacheable(aType, aOriginatingElement, aParentStyle); - RefPtr style = - cacheable ? aParentStyle->GetCachedLazyPseudoStyle(aType) : nullptr; + RefPtr style = cacheable + ? aParentStyle->GetCachedLazyPseudoStyle( + {aType, aFunctionalPseudoParameter}) + : nullptr; const bool isProbe = aIsProbe == IsProbe::Yes; @@ -479,7 +478,7 @@ already_AddRefed ServoStyleSet::ResolvePseudoElementStyle( return nullptr; } if (cacheable) { - aParentStyle->SetCachedLazyPseudoStyle(style); + aParentStyle->SetCachedLazyPseudoStyle(style, aFunctionalPseudoParameter); } } diff --git a/mobile/android/android-components/.buildconfig.yml b/mobile/android/android-components/.buildconfig.yml index a596f1c9b05eb..fbe167746f292 100644 --- a/mobile/android/android-components/.buildconfig.yml +++ b/mobile/android/android-components/.buildconfig.yml @@ -1390,6 +1390,13 @@ projects: path: components/feature/summarize publish: true upstream_dependencies: + - components:concept-base + - components:concept-fetch + - components:lib-publicsuffixlist + - components:lib-state + - components:support-base + - components:support-ktx + - components:support-utils - components:tooling-lint components:feature-syncedtabs: description: Feature that provides access to other devices' tabs in the same account. diff --git a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/MediaSessionFeature.kt b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/MediaSessionFeature.kt index dcc4013fda923..b058fb09f14c2 100644 --- a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/MediaSessionFeature.kt +++ b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/MediaSessionFeature.kt @@ -10,7 +10,9 @@ import android.content.Intent import android.content.ServiceConnection import android.os.IBinder import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.map @@ -30,11 +32,13 @@ import mozilla.components.lib.state.ext.flowScoped * @param applicationContext the application's [Context]. * @param mediaServiceClass the media service class will handle the media playback state * @param store Reference to the browser store where tab state is located. + * @param mainDispatcher Dispatcher used for main thread operations. */ class MediaSessionFeature( val applicationContext: Context, val mediaServiceClass: Class<*>, val store: BrowserStore, + private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main, ) { @VisibleForTesting internal var scope: CoroutineScope? = null @@ -62,7 +66,7 @@ class MediaSessionFeature( * Starts the feature. */ fun start() { - scope = store.flowScoped { flow -> + scope = store.flowScoped(dispatcher = mainDispatcher) { flow -> flow.map { state -> state.findActiveMediaTab() } .distinctUntilChangedBy { tab -> tab?.mediaSessionState } .collect { state -> showMediaStatus(state) } diff --git a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeature.kt b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeature.kt index d9296bb4ae577..b1a6bdcd55b2e 100644 --- a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeature.kt +++ b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeature.kt @@ -7,7 +7,9 @@ package mozilla.components.feature.media.fullscreen import android.app.Activity import android.content.pm.ActivityInfo import android.view.WindowManager +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -25,12 +27,13 @@ class MediaSessionFullscreenFeature( private val activity: Activity, private val store: BrowserStore, private val tabId: String?, + private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main, ) : LifecycleAwareFeature { private var scope: CoroutineScope? = null override fun start() { - scope = store.flowScoped { flow -> + scope = store.flowScoped(dispatcher = mainDispatcher) { flow -> flow.map { it.tabs + it.customTabs }.map { tab -> diff --git a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/service/MediaSessionServiceDelegate.kt b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/service/MediaSessionServiceDelegate.kt index a598745b6a62f..22f373ff6104a 100644 --- a/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/service/MediaSessionServiceDelegate.kt +++ b/mobile/android/android-components/components/feature/media/src/main/java/mozilla/components/feature/media/service/MediaSessionServiceDelegate.kt @@ -69,6 +69,7 @@ internal class MediaSessionServiceDelegate( @get:VisibleForTesting internal val store: BrowserStore, @get:VisibleForTesting internal val crashReporter: CrashReporting?, @get:VisibleForTesting internal val notificationsDelegate: NotificationsDelegate, + @get:VisibleForTesting internal val mainScope: CoroutineScope = MainScope(), ) : MediaSessionDelegate { private val logger = Logger("MediaSessionService") @@ -104,7 +105,7 @@ internal class MediaSessionServiceDelegate( fun onCreate() { logger.debug("Service created") mediaSession.setCallback(MediaSessionCallback(store)) - notificationScope = MainScope() + notificationScope = mainScope } fun onDestroy() { diff --git a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/MediaSessionFeatureTest.kt b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/MediaSessionFeatureTest.kt index 6ff2af6a777a2..da3feae9383bc 100644 --- a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/MediaSessionFeatureTest.kt +++ b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/MediaSessionFeatureTest.kt @@ -8,8 +8,8 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.test.ext.junit.runners.AndroidJUnit4 -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.test.runTest import mozilla.components.browser.state.action.MediaSessionAction import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.MediaSessionState @@ -24,25 +24,21 @@ import mozilla.components.support.test.any import mozilla.components.support.test.argumentCaptor import mozilla.components.support.test.eq import mozilla.components.support.test.mock -import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.anyInt import org.mockito.Mockito.doReturn import org.mockito.Mockito.verify +import kotlin.coroutines.ContinuationInterceptor @RunWith(AndroidJUnit4::class) class MediaSessionFeatureTest { - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - @Test - fun `WHEN the feature starts THEN it starts observing the store`() { + fun `WHEN the feature starts THEN it starts observing the store`() = runTest { val store: BrowserStore = mock() val feature = MediaSessionFeature( mock(), @@ -57,285 +53,346 @@ class MediaSessionFeatureTest { } @Test - fun `GIVEN a started feature WHEN it is stopped THEN the store is not observed for updates anymore`() { - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - mock(), - ) - feature.scope = CoroutineScope(Dispatchers.Default) + fun `GIVEN a started feature WHEN it is stopped THEN the store is not observed for updates anymore`() = + runTest { + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + mock(), + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) - feature.stop() + feature.stop() - assertNull(feature.scope) - } + assertNull(feature.scope) + } @Test - fun `WHEN the media service is bound THEN store this and show the current media playing status`() { - val mediaTab = getMediaTab(PlaybackState.PLAYING) - val initialState = BrowserState(tabs = listOf(mediaTab)) - val store = BrowserStore(initialState) - val mediaServiceClass = MediaSessionServiceDelegate::class.java - val feature = MediaSessionFeature( - mock(), - mediaServiceClass, - store, - ) - val mediaService: MediaSessionServiceDelegate = mock() - val binder = MediaServiceBinder(mediaService) - - feature.mediaServiceConnection.onServiceConnected(mock(), binder) - - assertEquals(mediaService, feature.mediaService) - verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) - } + fun `WHEN the media service is bound THEN store this and show the current media playing status`() = + runTest { + val mediaTab = getMediaTab(PlaybackState.PLAYING) + val initialState = BrowserState(tabs = listOf(mediaTab)) + val store = BrowserStore(initialState) + val mediaServiceClass = MediaSessionServiceDelegate::class.java + val feature = MediaSessionFeature( + mock(), + mediaServiceClass, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + val mediaService: MediaSessionServiceDelegate = mock() + val binder = MediaServiceBinder(mediaService) + + feature.mediaServiceConnection.onServiceConnected(mock(), binder) + + assertEquals(mediaService, feature.mediaService) + verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) + } @Test - fun `GIVEN media service is bound WHEN the service is disconnected THEN cleanup local properties`() { - val mediaService: ComponentName = mock() - val feature = MediaSessionFeature(mock(), mediaService.javaClass, mock()) - feature.mediaService = mock() - - feature.mediaServiceConnection.onServiceDisconnected(mediaService) - - assertNull(feature.mediaService) - } + fun `GIVEN media service is bound WHEN the service is disconnected THEN cleanup local properties`() = + runTest { + val mediaService: ComponentName = mock() + val feature = MediaSessionFeature( + mock(), + mediaService.javaClass, + mock(), + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + feature.mediaService = mock() + + feature.mediaServiceConnection.onServiceDisconnected(mediaService) + + assertNull(feature.mediaService) + } @Test - fun `GIVEN feature is started but media service is not WHEN media starts playing THEN bind to the media service and show the playing status`() { - val mockApplicationContext: Context = mock() - val mediaTab = getMediaTab(PlaybackState.PLAYING) - val initialState = BrowserState(tabs = listOf(mediaTab)) - val store = BrowserStore(initialState) - val mediaServiceClass = MediaSessionServiceDelegate::class.java - val feature = MediaSessionFeature( - mockApplicationContext, - mediaServiceClass, - store, - ) - doReturn(true).`when`(mockApplicationContext).bindService( - any(), - any(), - anyInt(), - ) - val mediaServiceIntentCaptor = argumentCaptor() - - feature.start() - - verify(mockApplicationContext).bindService( - mediaServiceIntentCaptor.capture(), - any(), - eq(Context.BIND_AUTO_CREATE), - ) - assertEquals(mediaServiceClass.name, mediaServiceIntentCaptor.value.component!!.className) - } + fun `GIVEN feature is started but media service is not WHEN media starts playing THEN bind to the media service and show the playing status`() = + runTest { + val mockApplicationContext: Context = mock() + val mediaTab = getMediaTab(PlaybackState.PLAYING) + val initialState = BrowserState(tabs = listOf(mediaTab)) + val store = BrowserStore(initialState) + val mediaServiceClass = MediaSessionServiceDelegate::class.java + val feature = MediaSessionFeature( + mockApplicationContext, + mediaServiceClass, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + doReturn(true).`when`(mockApplicationContext).bindService( + any(), + any(), + anyInt(), + ) + val mediaServiceIntentCaptor = argumentCaptor() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(mockApplicationContext).bindService( + mediaServiceIntentCaptor.capture(), + any(), + eq(Context.BIND_AUTO_CREATE), + ) + assertEquals( + mediaServiceClass.name, + mediaServiceIntentCaptor.value.component!!.className, + ) + } @Test - fun `GIVEN feature and media service are started WHEN media starts playing in a normal tab THEN handle showing the new playing status`() { - val mediaTab = getMediaTab(PlaybackState.PLAYING) - val initialState = BrowserState( - tabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media starts playing in a normal tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getMediaTab(PlaybackState.PLAYING) + val initialState = BrowserState( + tabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN media starts playing in a custom tab THEN handle showing the new playing status`() { - val mediaTab = getCustomTabWithMedia(PlaybackState.PLAYING) - val initialState = BrowserState( - customTabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media starts playing in a custom tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getCustomTabWithMedia(PlaybackState.PLAYING) + val initialState = BrowserState( + customTabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaPlaying(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN media is paused in a normal tab THEN handle showing the new playing status`() { - val mediaTab = getMediaTab(PlaybackState.PAUSED) - val initialState = BrowserState( - tabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaPaused(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media is paused in a normal tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getMediaTab(PlaybackState.PAUSED) + val initialState = BrowserState( + tabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaPaused(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN media is paused in a custom tab THEN handle showing the new playing status`() { - val mediaTab = getCustomTabWithMedia(PlaybackState.PAUSED) - val initialState = BrowserState( - customTabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaPaused(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media is paused in a custom tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getCustomTabWithMedia(PlaybackState.PAUSED) + val initialState = BrowserState( + customTabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaPaused(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN media is stopped in a normal tab THEN handle showing the new playing status`() { - val mediaTab = getMediaTab(PlaybackState.STOPPED) - val initialState = BrowserState( - tabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaStopped(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media is stopped in a normal tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getMediaTab(PlaybackState.STOPPED) + val initialState = BrowserState( + tabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaStopped(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN media is stopped in a custom tab THEN handle showing the new playing status`() { - val mediaTab = getCustomTabWithMedia(PlaybackState.STOPPED) - val initialState = BrowserState( - customTabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mock(), - MediaSessionServiceDelegate::class.java, - store, - ) - feature.mediaService = mock() - - feature.start() - - verify(feature.mediaService)!!.handleMediaStopped(mediaTab) - } + fun `GIVEN feature and media service are started WHEN media is stopped in a custom tab THEN handle showing the new playing status`() = + runTest { + val mediaTab = getCustomTabWithMedia(PlaybackState.STOPPED) + val initialState = BrowserState( + customTabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mock(), + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + feature.mediaService = mock() + + feature.start() + testScheduler.advanceUntilIdle() + + verify(feature.mediaService)!!.handleMediaStopped(mediaTab) + } @Test - fun `GIVEN feature and media service are started WHEN the media status is unknown THEN disconnect the media service and cleanup`() { - val mockApplicationContext: Context = mock() - val mediaTab = getMediaTab(PlaybackState.UNKNOWN) - val initialState = BrowserState( - tabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mockApplicationContext, - MediaSessionServiceDelegate::class.java, - store, - ) - val mediaService: MediaSessionDelegate = mock() - feature.mediaService = mediaService - - feature.start() - - verify(mediaService).handleNoMedia() - verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) - assertNull(feature.mediaService) - } + fun `GIVEN feature and media service are started WHEN the media status is unknown THEN disconnect the media service and cleanup`() = + runTest { + val mockApplicationContext: Context = mock() + val mediaTab = getMediaTab(PlaybackState.UNKNOWN) + val initialState = BrowserState( + tabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mockApplicationContext, + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + val mediaService: MediaSessionDelegate = mock() + feature.mediaService = mediaService + + feature.start() + testScheduler.advanceUntilIdle() + + verify(mediaService).handleNoMedia() + verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) + assertNull(feature.mediaService) + } @Test - fun `GIVEN feature and media service are started WHEN there is no media tab THEN stop the media service and cleanup`() { - val mockApplicationContext: Context = mock() - val initialState = BrowserState() - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mockApplicationContext, - MediaSessionServiceDelegate::class.java, - store, - ) - val mediaService: MediaSessionDelegate = mock() - feature.mediaService = mediaService - - feature.start() - - verify(mediaService).handleNoMedia() - verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) - assertNull(feature.mediaService) - } + fun `GIVEN feature and media service are started WHEN there is no media tab THEN stop the media service and cleanup`() = + runTest { + val mockApplicationContext: Context = mock() + val initialState = BrowserState() + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mockApplicationContext, + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + val mediaService: MediaSessionDelegate = mock() + feature.mediaService = mediaService + + feature.start() + testScheduler.advanceUntilIdle() + + verify(mediaService).handleNoMedia() + verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) + assertNull(feature.mediaService) + } @Test - fun `GIVEN a normal tab is playing media WHEN media is deactivated THEN stop the media service and cleanup`() { - val mockApplicationContext: Context = mock() - val mediaTab = getMediaTab(PlaybackState.PLAYING) - val initialState = BrowserState( - tabs = listOf(mediaTab), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mockApplicationContext, - MediaSessionServiceDelegate::class.java, - store, - ) - val mediaService: MediaSessionDelegate = mock() - feature.mediaService = mediaService - feature.start() - - store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction(store.state.tabs[0].id)) - - verify(mediaService).handleNoMedia() - verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) - assertNull(feature.mediaService) - } + fun `GIVEN a normal tab is playing media WHEN media is deactivated THEN stop the media service and cleanup`() = + runTest { + val mockApplicationContext: Context = mock() + val mediaTab = getMediaTab(PlaybackState.PLAYING) + val initialState = BrowserState( + tabs = listOf(mediaTab), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mockApplicationContext, + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + val mediaService: MediaSessionDelegate = mock() + feature.mediaService = mediaService + + feature.start() + testScheduler.advanceUntilIdle() + + store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction(store.state.tabs[0].id)) + testScheduler.advanceUntilIdle() + + verify(mediaService).handleNoMedia() + verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) + assertNull(feature.mediaService) + } @Test - fun `GIVEN a custom tab is playing media WHEN media is deactivated THEN stop the media service and cleanup`() { - val mockApplicationContext: Context = mock() - val mediaTab = getMediaTab(PlaybackState.UNKNOWN) - val customTabWithMedia = getCustomTabWithMedia(PlaybackState.PLAYING) - val initialState = BrowserState( - tabs = listOf(mediaTab), - customTabs = listOf(customTabWithMedia), - ) - val store = BrowserStore(initialState) - val feature = MediaSessionFeature( - mockApplicationContext, - MediaSessionServiceDelegate::class.java, - store, - ) - val mediaService: MediaSessionDelegate = mock() - feature.mediaService = mediaService - feature.start() - - store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction(store.state.customTabs[0].id)) - - verify(mediaService).handleNoMedia() - verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) - assertNull(feature.mediaService) - } + fun `GIVEN a custom tab is playing media WHEN media is deactivated THEN stop the media service and cleanup`() = + runTest { + val mockApplicationContext: Context = mock() + val mediaTab = getMediaTab(PlaybackState.UNKNOWN) + val customTabWithMedia = getCustomTabWithMedia(PlaybackState.PLAYING) + val initialState = BrowserState( + tabs = listOf(mediaTab), + customTabs = listOf(customTabWithMedia), + ) + val store = BrowserStore(initialState) + val feature = MediaSessionFeature( + mockApplicationContext, + MediaSessionServiceDelegate::class.java, + store, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, + ) + + val mediaService: MediaSessionDelegate = mock() + feature.mediaService = mediaService + + feature.start() + testScheduler.advanceUntilIdle() + + store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction(store.state.customTabs[0].id)) + testScheduler.advanceUntilIdle() + + verify(mediaService).handleNoMedia() + verify(mockApplicationContext).unbindService(feature.mediaServiceConnection) + assertNull(feature.mediaService) + } private fun getMediaTab(playbackState: PlaybackState = PlaybackState.PLAYING) = createTab( "https://www.mozilla.org", @@ -345,11 +402,12 @@ class MediaSessionFeatureTest { ), ) - private fun getCustomTabWithMedia(playbackState: PlaybackState = PlaybackState.PLAYING) = createCustomTab( - "https://www.mozilla.org", - mediaSessionState = MediaSessionState( - mock(), - playbackState = playbackState, - ), - ) + private fun getCustomTabWithMedia(playbackState: PlaybackState = PlaybackState.PLAYING) = + createCustomTab( + "https://www.mozilla.org", + mediaSessionState = MediaSessionState( + mock(), + playbackState = playbackState, + ), + ) } diff --git a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/focus/AudioFocusTest.kt b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/focus/AudioFocusTest.kt index c206d995205cc..cdf513c12a5d4 100644 --- a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/focus/AudioFocusTest.kt +++ b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/focus/AudioFocusTest.kt @@ -18,9 +18,7 @@ import mozilla.components.support.base.android.NotificationsDelegate import mozilla.components.support.test.any import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.doReturn @@ -32,9 +30,6 @@ import org.mockito.Mockito.verifyNoMoreInteractions class AudioFocusTest { private lateinit var audioManager: AudioManager - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - @Before fun setUp() { audioManager = mock() diff --git a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeatureTest.kt b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeatureTest.kt index d39ed64b06445..fa8b11aad9f44 100644 --- a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeatureTest.kt +++ b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/fullscreen/MediaSessionFullscreenFeatureTest.kt @@ -10,6 +10,8 @@ import android.os.Build import android.view.Window import android.view.WindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.test.runTest import mozilla.components.browser.state.action.ContentAction import mozilla.components.browser.state.action.CustomTabListAction import mozilla.components.browser.state.action.MediaSessionAction @@ -22,12 +24,10 @@ import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.mediasession.MediaSession import mozilla.components.support.test.mock -import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.whenever import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertTrue -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations @@ -35,15 +35,13 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.robolectric.Robolectric import org.robolectric.annotation.Config +import kotlin.coroutines.ContinuationInterceptor @RunWith(AndroidJUnit4::class) class MediaSessionFullscreenFeatureTest { - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - @Test - fun `GIVEN the currently selected tab is not in fullscreen WHEN the feature is running THEN orientation is set to default`() { + fun `GIVEN the currently selected tab is not in fullscreen WHEN the feature is running THEN orientation is set to default`() = runTest { val activity: Activity = mock() val elementMetadata = MediaSession.ElementMetadata() val initialState = BrowserState( @@ -66,15 +64,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() verify(activity).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER) } @Test - fun `GIVEN the currently selected tab plays portrait media WHEN the feature is running THEN orientation is set to portrait`() { + fun `GIVEN the currently selected tab plays portrait media WHEN the feature is running THEN orientation is set to portrait`() = runTest { val activity: Activity = mock() val window: Window = mock() whenever(activity.window).thenReturn(window) @@ -100,15 +100,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() verify(activity).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT) } @Test - fun `GIVEN the currently selected tab plays media with no size WHEN the feature is running THEN orientation is unchanged`() { + fun `GIVEN the currently selected tab plays media with no size WHEN the feature is running THEN orientation is unchanged`() = runTest { val activity: Activity = mock() val window: Window = mock() whenever(activity.window).thenReturn(window) @@ -134,15 +136,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() verify(activity).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER) } @Test - fun `GIVEN the currently selected tab plays landscape media WHEN it enters fullscreen THEN set orientation to landscape`() { + fun `GIVEN the currently selected tab plays landscape media WHEN it enters fullscreen THEN set orientation to landscape`() = runTest { val activity: Activity = mock() val window: Window = mock() whenever(activity.window).thenReturn(window) @@ -168,9 +172,11 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() verify(activity).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) } @@ -178,7 +184,7 @@ class MediaSessionFullscreenFeatureTest { @Suppress("Deprecation") @Test @Config(sdk = [Build.VERSION_CODES.O]) - fun `GIVEN the currently selected tab plays landscape media WHEN it enters pip mode THEN set orientation to unspecified`() { + fun `GIVEN the currently selected tab plays landscape media WHEN it enters pip mode THEN set orientation to unspecified`() = runTest { val activity = Robolectric.buildActivity(Activity::class.java).setup().get() val elementMetadata = MediaSession.ElementMetadata(width = 100, height = 100) val initialState = BrowserState( @@ -201,13 +207,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() + activity.enterPictureInPictureMode() assertTrue(activity.isInPictureInPictureMode) store.dispatch(ContentAction.PictureInPictureChangedAction("tab1", true)) + testScheduler.advanceUntilIdle() assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.requestedOrientation) } @@ -215,7 +225,7 @@ class MediaSessionFullscreenFeatureTest { @Suppress("Deprecation") @Test @Config(sdk = [Build.VERSION_CODES.O]) - fun `GIVEN the currently selected tab is in pip mode WHEN an external intent arrives THEN set orientation to default`() { + fun `GIVEN the currently selected tab is in pip mode WHEN an external intent arrives THEN set orientation to default`() = runTest { val activity = Robolectric.buildActivity(Activity::class.java).setup().get() val elementMetadata = MediaSession.ElementMetadata(width = 100, height = 100) val initialState = BrowserState( @@ -238,11 +248,16 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() + activity.enterPictureInPictureMode() store.dispatch(ContentAction.PictureInPictureChangedAction("tab1", true)) + testScheduler.advanceUntilIdle() + assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.requestedOrientation) val tab2 = createTab( @@ -257,6 +272,8 @@ class MediaSessionFullscreenFeatureTest { MediaSession.ElementMetadata(), ), ) + testScheduler.advanceUntilIdle() + assertEquals(ActivityInfo.SCREEN_ORIENTATION_USER, activity.requestedOrientation) assertEquals(tab2.id, store.state.selectedTabId) } @@ -264,7 +281,7 @@ class MediaSessionFullscreenFeatureTest { @Suppress("Deprecation") @Test @Config(sdk = [Build.VERSION_CODES.O]) - fun `GIVEN the currently selected tab is in pip mode WHEN it exits pip mode THEN set orientation to default`() { + fun `GIVEN the currently selected tab is in pip mode WHEN it exits pip mode THEN set orientation to default`() = runTest { val activity = Robolectric.buildActivity(Activity::class.java).setup().get() val elementMetadata = MediaSession.ElementMetadata(width = 100, height = 100) val initialState = BrowserState( @@ -287,13 +304,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() + activity.enterPictureInPictureMode() assertTrue(activity.isInPictureInPictureMode) store.dispatch(ContentAction.PictureInPictureChangedAction("tab1", true)) + testScheduler.advanceUntilIdle() assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.requestedOrientation) @@ -304,6 +325,7 @@ class MediaSessionFullscreenFeatureTest { MediaSession.ElementMetadata(), ), ) + testScheduler.advanceUntilIdle() assertEquals(ActivityInfo.SCREEN_ORIENTATION_USER, activity.requestedOrientation) } @@ -311,7 +333,7 @@ class MediaSessionFullscreenFeatureTest { @Suppress("Deprecation") @Test @Config(sdk = [Build.VERSION_CODES.O]) - fun `GIVEN the currently selected tab is in pip mode WHEN a custom tab loads THEN display custom tab in device's current orientation`() { + fun `GIVEN the currently selected tab is in pip mode WHEN a custom tab loads THEN display custom tab in device's current orientation`() = runTest { val activity = Robolectric.buildActivity(Activity::class.java).setup().get() val elementMetadata = MediaSession.ElementMetadata(width = 100, height = 100) val initialState = BrowserState( @@ -335,12 +357,17 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) feature.start() + testScheduler.advanceUntilIdle() + activity.enterPictureInPictureMode() store.dispatch(ContentAction.PictureInPictureChangedAction("tab1", true)) + testScheduler.advanceUntilIdle() + assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.requestedOrientation) val customTab = createCustomTab( @@ -349,6 +376,8 @@ class MediaSessionFullscreenFeatureTest { id = "tab2", ) store.dispatch(CustomTabListAction.AddCustomTabAction(customTab)) + testScheduler.advanceUntilIdle() + val externalActivity = Robolectric.buildActivity(Activity::class.java).setup().get() assertEquals(1, store.state.customTabs.size) val featureForExternalAppBrowser = MediaSessionFullscreenFeature( @@ -362,7 +391,7 @@ class MediaSessionFullscreenFeatureTest { } @Test - fun `GIVEN the selected tab in fullscreen mode WHEN the media is paused or stopped THEN release the wake lock of the device`() { + fun `GIVEN the selected tab in fullscreen mode WHEN the media is paused or stopped THEN release the wake lock of the device`() = runTest { val activity: Activity = mock() val window: Window = mock() @@ -390,24 +419,34 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) + feature.start() + testScheduler.advanceUntilIdle() + verify(activity.window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) store.dispatch(MediaSessionAction.UpdateMediaPlaybackStateAction("tab1", MediaSession.PlaybackState.PAUSED)) + testScheduler.advanceUntilIdle() + verify(activity.window).clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) clearInvocations(activity.window) store.dispatch(MediaSessionAction.UpdateMediaPlaybackStateAction("tab1", MediaSession.PlaybackState.PLAYING)) + testScheduler.advanceUntilIdle() + verify(activity.window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction("tab1")) + testScheduler.advanceUntilIdle() + verify(activity.window).clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } @Test - fun `GIVEN the selected tab is not in fullscreen mode WHEN it enters fullscreen THEN lock the wake lock of the device`() { + fun `GIVEN the selected tab is not in fullscreen mode WHEN it enters fullscreen THEN lock the wake lock of the device`() = runTest { val activity: Activity = mock() val window: Window = mock() @@ -435,21 +474,29 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) + feature.start() + testScheduler.advanceUntilIdle() + verify(activity.window, never()).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) store.dispatch(MediaSessionAction.UpdateMediaFullscreenAction("tab1", true, elementMetadata)) + testScheduler.advanceUntilIdle() + verify(activity.window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) clearInvocations(activity.window) store.dispatch(MediaSessionAction.UpdateMediaFullscreenAction("tab1", false, elementMetadata)) + testScheduler.advanceUntilIdle() + verify(activity.window).clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } @Test - fun `GIVEN the selected tab in fullscreen mode WHEN the active tab is changed to no media tab THEN release the wake lock of the device`() { + fun `GIVEN the selected tab in fullscreen mode WHEN the active tab is changed to no media tab THEN release the wake lock of the device`() = runTest { val activity: Activity = mock() val window: Window = mock() @@ -477,8 +524,12 @@ class MediaSessionFullscreenFeatureTest { activity, store, null, + mainDispatcher = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher, ) + feature.start() + testScheduler.advanceUntilIdle() + verify(activity.window).addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) val tab2 = createTab( @@ -488,6 +539,8 @@ class MediaSessionFullscreenFeatureTest { clearInvocations(activity.window) store.dispatch(TabListAction.AddTabAction(tab2, select = true)) store.dispatch(MediaSessionAction.UpdateMediaFullscreenAction(store.state.tabs[0].id, false, elementMetadata)) + testScheduler.advanceUntilIdle() + verify(activity.window).clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) assertEquals(tab2.id, store.state.selectedTabId) } diff --git a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/service/MediaSessionServiceDelegateTest.kt b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/service/MediaSessionServiceDelegateTest.kt index 8880f602960a2..e3b42b1e27e28 100644 --- a/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/service/MediaSessionServiceDelegateTest.kt +++ b/mobile/android/android-components/components/feature/media/src/test/java/mozilla/components/feature/media/service/MediaSessionServiceDelegateTest.kt @@ -17,6 +17,7 @@ import android.support.v4.media.session.PlaybackStateCompat import androidx.core.app.NotificationManagerCompat import androidx.test.ext.junit.runners.AndroidJUnit4 import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.test.runTest import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.MediaSessionState import mozilla.components.browser.state.state.createTab @@ -40,8 +41,6 @@ import mozilla.components.support.test.coMock import mozilla.components.support.test.eq import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.test.rule.MainCoroutineRule -import mozilla.components.support.test.rule.runTestOnMain import mozilla.components.support.test.whenever import mozilla.components.support.utils.ext.stopForegroundCompat import org.junit.Assert.assertEquals @@ -50,7 +49,6 @@ import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertSame import org.junit.Assert.assertTrue -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.anyInt @@ -66,14 +64,11 @@ import android.media.session.PlaybackState as AndroidPlaybackState @RunWith(AndroidJUnit4::class) class MediaSessionServiceDelegateTest { - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - private val notificationId = SharedIdsHelper.getIdForTag(testContext, AbstractMediaSessionService.NOTIFICATION_TAG) @Test - fun `WHEN the service is created THEN create a new notification scope audio focus manager`() { - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + fun `WHEN the service is created THEN create a new notification scope audio focus manager`() = runTest { + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.mediaSession = mock() val mediaCallbackCaptor = argumentCaptor() @@ -84,8 +79,8 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN the service is destroyed THEN stop notification updates and abandon audio focus`() { - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + fun `WHEN the service is destroyed THEN stop notification updates and abandon audio focus`() = runTest { + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.audioFocus = mock() delegate.onDestroy() @@ -96,8 +91,8 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN media playing started WHEN a new play command is received THEN resume media and emit telemetry`() { - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + fun `GIVEN media playing started WHEN a new play command is received THEN resume media and emit telemetry`() = runTest { + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.controller = mock() // simulate media already started playing CollectionProcessor.withFactCollection { facts -> @@ -114,8 +109,8 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN media playing started WHEN a new pause command is received THEN pause media and emit telemetry`() { - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + fun `GIVEN media playing started WHEN a new pause command is received THEN pause media and emit telemetry`() = runTest { + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.controller = mock() // simulate media already started playing CollectionProcessor.withFactCollection { facts -> @@ -132,7 +127,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN the task is removed THEN stop media in all tabs and shutdown`() { + fun `WHEN the task is removed THEN stop media in all tabs and shutdown`() = runTest { val notificationManagerCompat: NotificationManagerCompat = mock() val notificationsDelegate: NotificationsDelegate = mock() whenever(notificationsDelegate.notificationManagerCompat).thenReturn(notificationManagerCompat) @@ -156,9 +151,9 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling playing media THEN emit telemetry`() { + fun `WHEN handling playing media THEN emit telemetry`() = runTest { val mediaTab = getMediaTab() - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.audioFocus = mock() CollectionProcessor.withFactCollection { facts -> @@ -174,7 +169,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling playing media THEN setup internal properties`() { + fun `WHEN handling playing media THEN setup internal properties`() = runTest { val mediaTab = getMediaTab() val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock())) delegate.audioFocus = mock() @@ -187,38 +182,40 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN the service is already in foreground WHEN handling playing media THEN setup internal properties`() = runTestOnMain { + fun `GIVEN the service is already in foreground WHEN handling playing media THEN setup internal properties`() = runTest { val mediaTab = getMediaTab() val notificationsDelegate: NotificationsDelegate = mock() - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate, this) delegate.onCreate() delegate.audioFocus = mock() delegate.isForegroundService = true delegate.handleMediaPlaying(mediaTab) + testScheduler.advanceUntilIdle() verify(notificationsDelegate).notify(any(), eq(delegate.notificationId), any(), any(), any(), eq(false)) } @Test - fun `GIVEN the service is not in foreground WHEN handling playing media THEN start the media service as foreground`() { + fun `GIVEN the service is not in foreground WHEN handling playing media THEN start the media service as foreground`() = runTest { val mediaTab = getMediaTab() - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.onCreate() delegate.audioFocus = mock() delegate.isForegroundService = false delegate.handleMediaPlaying(mediaTab) + testScheduler.advanceUntilIdle() verify(delegate.service).startForeground(eq(delegate.notificationId), any()) assertTrue(delegate.isForegroundService) } @Test - fun `WHEN updating the notification for a new media state THEN post a new notification`() = runTestOnMain { + fun `WHEN updating the notification for a new media state THEN post a new notification`() = runTest { val mediaTab = getMediaTab() val notificationsDelegate: NotificationsDelegate = mock() - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate, this) delegate.onCreate() val notification: Notification = mock() delegate.notificationHelper = coMock { @@ -226,28 +223,30 @@ class MediaSessionServiceDelegateTest { } delegate.updateNotification(mediaTab) + testScheduler.advanceUntilIdle() verify(notificationsDelegate).notify(any(), eq(delegate.notificationId), eq(notification), any(), any(), eq(false)) } @Test - fun `WHEN starting the service as foreground THEN use start with a new notification for the current media state`() = runTestOnMain { + fun `WHEN starting the service as foreground THEN use start with a new notification for the current media state`() = runTest { val mediaTab = getMediaTab() - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.onCreate() val notification: Notification = mock() delegate.notificationHelper = coMock { doReturn(notification).`when`(this).create(mediaTab, delegate.mediaSession) } - delegate.startForeground(mediaTab) + delegate.startForeground(mediaTab, coroutineContext) + testScheduler.advanceUntilIdle() verify(delegate.service).startForeground(eq(delegate.notificationId), eq(notification)) assertTrue(delegate.isForegroundService) } @Test - fun `GIVEN media is paused WHEN media is handling resuming media THEN resume the right session`() { + fun `GIVEN media is paused WHEN media is handling resuming media THEN resume the right session`() = runTest { val mediaTab1 = getMediaTab() val mediaTab2 = getMediaTab(PlaybackState.PAUSED) val store = BrowserStore( @@ -269,9 +268,9 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling paused media THEN emit telemetry`() { + fun `WHEN handling paused media THEN emit telemetry`() = runTest { val mediaTab = getMediaTab(PlaybackState.PAUSED) - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) CollectionProcessor.withFactCollection { facts -> delegate.handleMediaPaused(mediaTab) @@ -286,7 +285,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling paused media THEN update internal state and notification and stop the service`() = runTestOnMain { + fun `WHEN handling paused media THEN update internal state and notification and stop the service`() = runTest { val mediaTab = getMediaTab(PlaybackState.PAUSED) val notificationManagerCompat = spy(NotificationManagerCompat.from(testContext)) val notificationsDelegate = spy(NotificationsDelegate(notificationManagerCompat)) @@ -297,7 +296,7 @@ class MediaSessionServiceDelegateTest { val mediaSession: MediaSessionCompat = mock() val notificationId = SharedIdsHelper.getIdForTag(testContext, AbstractMediaSessionService.NOTIFICATION_TAG) - val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate)) + val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate, this)) delegate.isForegroundService = true delegate.mediaSession = mediaSession delegate.notificationHelper = notificationHelper @@ -307,6 +306,7 @@ class MediaSessionServiceDelegateTest { delegate.onCreate() delegate.handleMediaPaused(mediaTab) + testScheduler.advanceUntilIdle() verify(delegate).updateMediaSession(mediaTab) verify(delegate).unregisterBecomingNoisyListenerIfNeeded() @@ -316,9 +316,9 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling stopped media THEN emit telemetry`() { + fun `WHEN handling stopped media THEN emit telemetry`() = runTest { val mediaTab = getMediaTab(PlaybackState.STOPPED) - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) CollectionProcessor.withFactCollection { facts -> delegate.handleMediaStopped(mediaTab) @@ -333,15 +333,16 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN handling stopped media THEN update internal state and notification and stop the service`() = runTestOnMain { + fun `WHEN handling stopped media THEN update internal state and notification and stop the service`() = runTest { val mediaTab = getMediaTab(PlaybackState.STOPPED) val notificationsDelegate: NotificationsDelegate = mock() - val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate)) + val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), notificationsDelegate, this)) delegate.isForegroundService = true delegate.onCreate() delegate.handleMediaStopped(mediaTab) + testScheduler.advanceUntilIdle() verify(delegate).updateMediaSession(mediaTab) verify(delegate).unregisterBecomingNoisyListenerIfNeeded() @@ -351,7 +352,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN there is no media playing THEN stop the media service`() { + fun `WHEN there is no media playing THEN stop the media service`() = runTest { val notificationManagerCompat: NotificationManagerCompat = mock() val notificationsDelegate: NotificationsDelegate = mock() whenever(notificationsDelegate.notificationManagerCompat).thenReturn(notificationManagerCompat) @@ -368,7 +369,7 @@ class MediaSessionServiceDelegateTest { @Suppress("Deprecation") @Test - fun `WHEN updating the media session THEN use the values from the current media session`() { + fun `WHEN updating the media session THEN use the values from the current media session`() = runTest { val bitmap: Bitmap = mock() val getArtwork: (suspend () -> Bitmap?) = { bitmap } val metadata = Metadata("title", "artist", "album", getArtwork) @@ -379,7 +380,7 @@ class MediaSessionServiceDelegateTest { mediaSessionState = MediaSessionState(mock(), metadata = metadata), ) - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.mediaSession = mock() delegate.onCreate() val metadataCaptor = argumentCaptor() @@ -388,6 +389,7 @@ class MediaSessionServiceDelegateTest { val expectedPlaybackState = mediaTab.mediaSessionState!!.toPlaybackState() delegate.updateMediaSession(mediaTab) + testScheduler.advanceUntilIdle() verify(delegate.mediaSession).isActive = true verify(delegate.mediaSession).setPlaybackState(playbackStateCaptor.capture()) @@ -423,8 +425,8 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN stopping running in foreground THEN stop the foreground service`() { - val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock()) + fun `WHEN stopping running in foreground THEN stop the foreground service`() = runTest { + val delegate = MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock(), this) delegate.isForegroundService = true delegate.stopForeground() @@ -434,7 +436,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN a audio noisy receiver is already registered WHEN trying to register a new one THEN return early`() { + fun `GIVEN a audio noisy receiver is already registered WHEN trying to register a new one THEN return early`() = runTest { val context = spy(testContext) val delegate = MediaSessionServiceDelegate(context, mock(), mock(), mock(), mock()) delegate.noisyAudioStreamReceiver = mock() @@ -445,7 +447,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN a audio noisy receiver is not already registered WHEN trying to register a new one THEN register it`() { + fun `GIVEN a audio noisy receiver is not already registered WHEN trying to register a new one THEN register it`() = runTest { val delegate = spy(MediaSessionServiceDelegate(testContext, mock(), mock(), mock(), mock())) val receiverCaptor = argumentCaptor() @@ -456,7 +458,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN a audio noisy receiver is already registered WHEN trying to unregister one THEN unregister it`() { + fun `GIVEN a audio noisy receiver is already registered WHEN trying to unregister one THEN unregister it`() = runTest { val context = spy(testContext) val delegate = MediaSessionServiceDelegate(context, mock(), mock(), mock(), mock()) delegate.noisyAudioStreamReceiver = mock() @@ -475,7 +477,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN a audio noisy receiver is not already registered WHEN trying to unregister one THEN return early`() { + fun `GIVEN a audio noisy receiver is not already registered WHEN trying to unregister one THEN return early`() = runTest { val context = spy(testContext) val delegate = MediaSessionServiceDelegate(context, mock(), mock(), mock(), mock()) @@ -485,7 +487,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `WHEN the delegate is shutdown THEN cleanup resources and stop the media service`() { + fun `WHEN the delegate is shutdown THEN cleanup resources and stop the media service`() = runTest { val notificationManagerCompat: NotificationManagerCompat = mock() val notificationsDelegate: NotificationsDelegate = mock() whenever(notificationsDelegate.notificationManagerCompat).thenReturn(notificationManagerCompat) @@ -501,7 +503,7 @@ class MediaSessionServiceDelegateTest { } @Test - fun `when device is becoming noisy, playback is paused`() { + fun `when device is becoming noisy, playback is paused`() = runTest { val controller: MediaSession.Controller = mock() val initialState = BrowserState( tabs = listOf( @@ -523,10 +525,10 @@ class MediaSessionServiceDelegateTest { } @Test - fun `GIVEN device is at least API level 31 WHEN startForeground throws an exception THEN catch and pass the exception to the crash reporter`() = runTestOnMain { + fun `GIVEN device is at least API level 31 WHEN startForeground throws an exception THEN catch and pass the exception to the crash reporter`() = runTest { val crashReporter: CrashReporting = mock() val service: AbstractMediaSessionService = mock() - val delegate = MediaSessionServiceDelegate(testContext, service, mock(), crashReporter, mock()) + val delegate = MediaSessionServiceDelegate(testContext, service, mock(), crashReporter, mock(), this) delegate.onCreate() val notification: Notification = mock() delegate.notificationHelper = coMock { @@ -537,22 +539,30 @@ class MediaSessionServiceDelegateTest { doThrow(exception).`when`(service).startForeground(anyInt(), any()) setSdkInt(31) - delegate.startForeground(mock()) + delegate.startForeground(mock(), coroutineContext) + testScheduler.advanceUntilIdle() verify(crashReporter).submitCaughtException(exception) } @Test(expected = ForegroundServiceStartNotAllowedException::class) - fun `GIVEN device is less than 31 WHEN startForeground throws an exception THEN rethrow the exception`() { - var throwable: Throwable? = null - val exceptionHandler = CoroutineExceptionHandler { _, t -> - throwable = t - } + fun `GIVEN device is less than 31 WHEN startForeground throws an exception THEN rethrow the exception`() = + runTest { + var throwable: Throwable? = null + val exceptionHandler = CoroutineExceptionHandler { _, t -> + throwable = t + } - runTestOnMain { val crashReporter: CrashReporting = mock() val service: AbstractMediaSessionService = mock() - val delegate = MediaSessionServiceDelegate(testContext, service, mock(), crashReporter, mock()) + val delegate = MediaSessionServiceDelegate( + testContext, + service, + mock(), + crashReporter, + mock(), + this, + ) delegate.onCreate() val notification: Notification = mock() delegate.notificationHelper = coMock { @@ -564,10 +574,9 @@ class MediaSessionServiceDelegateTest { setSdkInt(30) delegate.startForeground(mock(), exceptionHandler) - } - throwable?.let { throw it } - } + throwable?.let { throw it } + } private fun getMediaTab(playbackState: PlaybackState = PlaybackState.PLAYING) = createTab( title = "Mozilla", diff --git a/mobile/android/android-components/components/feature/summarize/build.gradle b/mobile/android/android-components/components/feature/summarize/build.gradle index 76446964bba0c..69e50daf9893e 100644 --- a/mobile/android/android-components/components/feature/summarize/build.gradle +++ b/mobile/android/android-components/components/feature/summarize/build.gradle @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.dependency.analysis) + alias(libs.plugins.kotlin.compose) } apply plugin: 'com.android.library' @@ -16,13 +17,23 @@ android { buildFeatures { buildConfig = true + compose = true } namespace = 'mozilla.components.feature.summarize' } dependencies { + implementation project(':components:lib-state') + testImplementation libs.androidx.test.junit + + implementation libs.androidx.compose.foundation + implementation libs.androidx.compose.ui.tooling.preview + implementation libs.androidx.compose.ui + implementation libs.androidx.compose.material3 + debugImplementation libs.androidx.compose.ui.tooling + } apply from: '../../../common-config.gradle' diff --git a/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationMiddleware.kt b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationMiddleware.kt new file mode 100644 index 0000000000000..f717ccf73d7ab --- /dev/null +++ b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationMiddleware.kt @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.summarize + +import mozilla.components.lib.state.Middleware +import mozilla.components.lib.state.Store + +internal class SummarizationMiddleware : Middleware { + override fun invoke( + store: Store, + next: (SummarizationAction) -> Unit, + action: SummarizationAction, + ) { + TODO("Not yet implemented") + } +} diff --git a/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationReducer.kt b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationReducer.kt new file mode 100644 index 0000000000000..603be90e88bfe --- /dev/null +++ b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationReducer.kt @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.summarize + +internal fun summarizationReducer(state: SummarizationState, action: SummarizationAction) = when (action) { + else -> { state } +} diff --git a/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationScreen.kt b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationScreen.kt new file mode 100644 index 0000000000000..afeeca9ed7097 --- /dev/null +++ b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationScreen.kt @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.summarize + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +/** + * Composable function that renders the summarized text of a webpage. + **/ +@Composable +fun SummarizationUi() { + SummarizationScreen( + SummarizationStore( + initialState = SummarizationState( + pageSummarizationState = PageSummarizationState.Inert, + ), + reducer = ::summarizationReducer, + middleware = listOf(SummarizationMiddleware()), + ), + ) +} + +@Composable +private fun SummarizationScreen(store: SummarizationStore) { + Text(store.state.summarizedText) +} + +private class SummarizationStatePreviewProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + SummarizationState( + PageSummarizationState.Summarizing, + summarizedText = "Lorem Ipsum", + ), + SummarizationState( + PageSummarizationState.Error(SummarizationError.ContentTooLong), + ), + SummarizationState( + PageSummarizationState.WaitingForPermission, + ), + ) +} + +@Preview +@Composable +private fun SummarizationScreenPreview( + @PreviewParameter(SummarizationStatePreviewProvider::class) state: SummarizationState, +) { + SummarizationScreen( + SummarizationStore( + initialState = state, + reducer = ::summarizationReducer, + middleware = listOf(SummarizationMiddleware()), + ), + ) +} diff --git a/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationStore.kt b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationStore.kt new file mode 100644 index 0000000000000..7e187d0a74ed2 --- /dev/null +++ b/mobile/android/android-components/components/feature/summarize/src/main/java/mozilla/components/feature/summarize/SummarizationStore.kt @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.summarize + +import mozilla.components.lib.state.Action +import mozilla.components.lib.state.State +import mozilla.components.lib.state.Store + +internal typealias SummarizationStore = Store + +internal interface SummarizationAction : Action + +internal data class SummarizationState( + val pageSummarizationState: PageSummarizationState, + val summarizedText: String = "", +) : State + +internal sealed class PageSummarizationState { + data object Inert : PageSummarizationState() + data object WaitingForPermission : PageSummarizationState() + data object Summarizing : PageSummarizationState() + data class Summarized(val text: String) : PageSummarizationState() + data class Error(val error: SummarizationError) : PageSummarizationState() +} + +internal sealed class SummarizationError { + object PermissionDenied : SummarizationError() + object ContentUnavailable : SummarizationError() + object ContentTooShort : SummarizationError() + object ContentTooLong : SummarizationError() + object SummarizationFailed : SummarizationError() + object InvalidSummary : SummarizationError() + object NetworkError : SummarizationError() +} diff --git a/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt b/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt index a817fb40552e4..7b56c1fc2b91b 100644 --- a/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt +++ b/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // These lines are generated by android-components/automation/application-services-nightly-bump.py -val VERSION = "149.20260122050249" +val VERSION = "149.20260129050319" val CHANNEL = ApplicationServicesChannel.NIGHTLY object ApplicationServicesConfig { diff --git a/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml b/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml index 89f48ddc33971..54640a7d43632 100644 --- a/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml +++ b/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml @@ -31,11 +31,11 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: e4b27cacf93b0114db3db28a789738509fcf7257 (2026-01-22T05:02:49). + release: 57f37cd43031e44518f0e240f2e0409ba72eae91 (2026-01-29T05:03:19). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: e4b27cacf93b0114db3db28a789738509fcf7257 + revision: 57f37cd43031e44518f0e240f2e0409ba72eae91 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ diff --git a/mobile/android/fenix/app/lint-baseline.xml b/mobile/android/fenix/app/lint-baseline.xml index 0322134e8c230..08c3a166ff45e 100644 --- a/mobile/android/fenix/app/lint-baseline.xml +++ b/mobile/android/fenix/app/lint-baseline.xml @@ -353,6 +353,50 @@ column="20"/> + + + + + + + + + + + + + + + + Unit, ) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - ToolbarPositionImage( - option = option, - isSelected = isSelected, - onClick = { onClick() }, + Column( + modifier = Modifier.clickable( + role = Role.Button, + interactionSource = remember { MutableInteractionSource() }, + indication = null, // Prevents onClick press/ripple animation + onClick = onClick, + ), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(Modifier.height(8.dp)) + + Image( + painter = painterResource(option.toolbarType.imageRes(isSelected)), + contentDescription = null, // Decorative only + modifier = Modifier.height(TOOLBAR_IMAGE_HEIGHT), ) - Spacer(Modifier.height(8.dp)) + Spacer(Modifier.height(26.dp)) Text( text = option.label, modifier = Modifier.align(Alignment.CenterHorizontally), style = FirefoxTheme.typography.headline7, ) - } -} -@Composable -private fun ToolbarPositionImage( - option: ToolbarOption, - isSelected: Boolean, - onClick: () -> Unit, - aspectRatio: Float = 0.7f, -) { - val cardShape = RoundedCornerShape(16.dp) - val imageColors = imageColors(isSelected) + Spacer(Modifier.height(12.dp)) - Box( - modifier = Modifier - .height(CONTENT_IMAGE_HEIGHT) - .aspectRatio(aspectRatio) - .clickable(role = Role.Button, onClick = onClick) - .clip(shape = cardShape) - .background(color = imageColors.cardBackground) - .border(2.dp, imageColors.borderColor, cardShape), - ) { - BrowserIllustration( - imageColors = imageColors, - isTop = option.toolbarType == ToolbarOptionType.TOOLBAR_TOP, - ) + SelectedCheckmark(isSelected) } } -@Composable -private fun BrowserIllustration( - imageColors: ImageColors, - isTop: Boolean, -) { - val browserShape = RoundedCornerShape(6.dp) - Column( - modifier = Modifier - .fillMaxSize() - .padding(32.dp) - .clip(shape = browserShape) - .border(2.dp, imageColors.borderColor, browserShape), - ) { - val (paddingTop, paddingBottom) = if (isTop) 6.dp to 4.dp else 4.dp to 6.dp - - if (isTop) { - ToolbarIllustration( - toolbarBackground = imageColors.toolbarBackground, - addressBarBackground = imageColors.addressBarBackground, - iconTint = imageColors.iconColor, - paddingTop = paddingTop, - paddingBottom = paddingBottom, - ) - } - - TabIllustration(Modifier.weight(1f)) - - if (!isTop) { - ToolbarIllustration( - toolbarBackground = imageColors.toolbarBackground, - addressBarBackground = imageColors.addressBarBackground, - iconTint = imageColors.iconColor, - paddingTop = paddingTop, - paddingBottom = paddingBottom, - ) - } - } -} +@DrawableRes +private fun ToolbarOptionType.imageRes(isSelected: Boolean): Int = + when (this) { + ToolbarOptionType.TOOLBAR_TOP -> + if (isSelected) { + R.drawable.nova_onboarding_toolbar_top_active + } else { + R.drawable.nova_onboarding_toolbar_top_inactive + } -@Composable -private fun ToolbarIllustration( - toolbarBackground: Color, - addressBarBackground: Color, - iconTint: Color, - paddingTop: Dp, - paddingBottom: Dp, -) { - Box(modifier = Modifier.background(toolbarBackground)) { - AddressBarIllustration( - addressBarBackground = addressBarBackground, - iconTint = iconTint, - paddingTop = paddingTop, - paddingBottom = paddingBottom, - ) + ToolbarOptionType.TOOLBAR_BOTTOM -> + if (isSelected) { + R.drawable.nova_onboarding_toolbar_bottom_active + } else { + R.drawable.nova_onboarding_toolbar_bottom_inactive + } } -} @Composable -private fun AddressBarIllustration( - addressBarBackground: Color, - iconTint: Color, - paddingTop: Dp, - paddingBottom: Dp, -) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(24.dp) - .padding(top = paddingTop, bottom = paddingBottom, start = 6.dp, end = 6.dp) - .clip(RoundedCornerShape(28.dp)) - .background(addressBarBackground), - ) { - Icon( - painter = painterResource(iconsR.drawable.mozac_ic_search_24), - tint = iconTint, - contentDescription = null, +private fun SelectedCheckmark(selected: Boolean = false) { + if (selected) { + Box( modifier = Modifier - .padding(start = 4.dp, top = 2.dp, bottom = 2.dp) - .align(Alignment.CenterStart), - ) + .size(24.dp) + .padding(1.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.tertiary), + contentAlignment = Alignment.Center, + ) { + Icon( + painter = painterResource(id = iconsR.drawable.mozac_ic_checkmark_24), + contentDescription = null, // Decorative only. + tint = MaterialTheme.colorScheme.onPrimary, + modifier = Modifier + .size(24.dp) + .padding(2.dp), + ) + } + } else { + Box( + modifier = Modifier + .size(24.dp) + .border( + width = 3.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = CircleShape, + ), + contentAlignment = Alignment.Center, + ) { } } } -@Composable -private fun TabIllustration(modifier: Modifier) { - Box( - modifier = modifier - .fillMaxWidth() - .background(Color.White), - ) -} - -@Composable -private fun imageColors(isSelected: Boolean): ImageColors { - val borderColor by animateColorAsState( - targetValue = if (isSelected) { - MaterialTheme.colorScheme.primary - } else { - MaterialTheme.colorScheme.outlineVariant - }, - label = "borderColor", - ) - val cardBackground by animateColorAsState( - targetValue = if (isSelected) MaterialTheme.colorScheme.primaryContainer else Color.White, - label = "cardBackground", - ) - val addressBarBackground by animateColorAsState( - targetValue = if (isSelected) MaterialTheme.colorScheme.primary else Color.White, - label = "addressBarBackground", - ) - val toolbarBackground by animateColorAsState( - targetValue = if (isSelected) { - MaterialTheme.colorScheme.surfaceDim - } else { - MaterialTheme.colorScheme.outlineVariant - }, - label = "toolbarBackground", - ) - val iconColor by animateColorAsState( - targetValue = if (isSelected) Color.White else MaterialTheme.colorScheme.onSurfaceVariant, - label = "iconColor", - ) - - return ImageColors( - borderColor = borderColor, - cardBackground = cardBackground, - addressBarBackground = addressBarBackground, - toolbarBackground = toolbarBackground, - iconColor = iconColor, - ) -} - -private data class ImageColors( - val borderColor: Color, - val cardBackground: Color, - val addressBarBackground: Color, - val toolbarBackground: Color, - val iconColor: Color, -) - @FlexibleWindowLightDarkPreview @Composable private fun OnboardingPagePreview() { @@ -336,11 +238,11 @@ private fun OnboardingPagePreview() { onboardingStore = OnboardingStore(), pageState = OnboardingPageState( imageRes = R.drawable.ic_onboarding_customize_toolbar, - title = stringResource(id = R.string.onboarding_redesign_customize_toolbar_title), - description = stringResource(id = R.string.onboarding_customize_toolbar_description), + title = stringResource(id = R.string.nova_onboarding_toolbar_selection_title), + description = "", // Unused in redesign primaryButton = Action( text = stringResource( - id = R.string.onboarding_redesign_continue_button, + id = R.string.nova_onboarding_continue_button, ), onClick = {}, ), @@ -348,12 +250,12 @@ private fun OnboardingPagePreview() { ToolbarOption( toolbarType = ToolbarOptionType.TOOLBAR_TOP, imageRes = R.drawable.ic_onboarding_top_toolbar, - label = stringResource(R.string.onboarding_customize_toolbar_top_option), + label = stringResource(R.string.nova_onboarding_toolbar_selection_top_label), ), ToolbarOption( toolbarType = ToolbarOptionType.TOOLBAR_BOTTOM, imageRes = R.drawable.ic_onboarding_bottom_toolbar, - label = stringResource(R.string.onboarding_customize_toolbar_bottom_option), + label = stringResource(R.string.nova_onboarding_toolbar_selection_bottom_label), ), ), onRecordImpressionEvent = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/defaultbrowser/SetToDefaultMainImage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/defaultbrowser/SetToDefaultMainImage.kt index 7ed32c21a08ea..753bd03f1c430 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/defaultbrowser/SetToDefaultMainImage.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/defaultbrowser/SetToDefaultMainImage.kt @@ -5,33 +5,12 @@ package org.mozilla.fenix.onboarding.redesign.view.defaultbrowser import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.PreviewLightDark -import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme -import mozilla.components.ui.icons.R as iconsR /** * Renders the main image for the "Set to Default" onboarding screen. @@ -40,93 +19,10 @@ import mozilla.components.ui.icons.R as iconsR */ @Composable fun SetToDefaultMainImage() { - Column( - modifier = Modifier.padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - BrowserChoiceRow() - - BrowserChoiceRow(selected = true) - - BrowserChoiceRow() - } -} - -@Composable -private fun BrowserChoiceRow(selected: Boolean = false) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(40.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - BrowserDetailsRow(selected) - - SelectedCheckmark(selected) - } -} - -@Composable -private fun RowScope.BrowserDetailsRow(selected: Boolean = false) { - Row( - modifier = Modifier - .fillMaxHeight() - .weight(1f) - .border(3.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(10.dp)) - .padding(start = 12.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - if (selected) { - Image( - painter = painterResource(R.drawable.ic_firefox), - contentDescription = null, // Decorative only. - modifier = Modifier.size(24.dp), - ) - } else { - Box( - modifier = Modifier - .size(20.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.outlineVariant), - ) - } - } -} - -@Composable -private fun SelectedCheckmark(selected: Boolean = false) { - if (selected) { - Box( - modifier = Modifier - .size(24.dp) - .padding(1.dp) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.tertiary), - contentAlignment = Alignment.Center, - ) { - Icon( - painter = painterResource(id = iconsR.drawable.mozac_ic_checkmark_24), - contentDescription = null, // Decorative only. - tint = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier - .size(24.dp) - .padding(2.dp), - ) - } - } else { - Box( - modifier = Modifier - .size(24.dp) - .border( - width = 3.dp, - color = MaterialTheme.colorScheme.outlineVariant, - shape = CircleShape, - ), - contentAlignment = Alignment.Center, - ) { } - } + Image( + painter = painterResource(R.drawable.nova_onboarding_set_to_default), + contentDescription = null, // Decorative only + ) } @PreviewLightDark diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/sync/SyncMainImage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/sync/SyncMainImage.kt index f3edc7e89678e..d999beb59bd6f 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/sync/SyncMainImage.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/redesign/view/sync/SyncMainImage.kt @@ -20,7 +20,7 @@ import org.mozilla.fenix.theme.FirefoxTheme @Composable fun SyncMainImage() { Image( - painter = painterResource(R.drawable.android_sync_illustration), + painter = painterResource(R.drawable.nova_onboarding_sync), contentDescription = null, // Decorative only ) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/widget/SetSearchWidgetMainImage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/widget/SetSearchWidgetMainImage.kt index c35ec743b8f19..0a8eeca3bbbcb 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/widget/SetSearchWidgetMainImage.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/widget/SetSearchWidgetMainImage.kt @@ -25,7 +25,7 @@ private val IMAGE_HEIGHT = 200.dp @Composable fun SetSearchWidgetMainImage() { Image( - painter = painterResource(R.drawable.ic_onboarding_search_widget), + painter = painterResource(R.drawable.nova_onboarding_widget), contentDescription = null, // Decorative only modifier = Modifier.height(IMAGE_HEIGHT), ) diff --git a/mobile/android/fenix/app/src/main/res/drawable-night/nova_onboarding_background.xml b/mobile/android/fenix/app/src/main/res/drawable-night/nova_onboarding_background.xml new file mode 100644 index 0000000000000..b2786a9e63e2a --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable-night/nova_onboarding_background.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/android_sync_illustration.xml b/mobile/android/fenix/app/src/main/res/drawable/android_sync_illustration.xml deleted file mode 100644 index 05b43cf784055..0000000000000 --- a/mobile/android/fenix/app/src/main/res/drawable/android_sync_illustration.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/android/fenix/app/src/main/res/drawable/ic_scan.xml b/mobile/android/fenix/app/src/main/res/drawable/ic_scan.xml deleted file mode 100644 index 47fffd545f348..0000000000000 --- a/mobile/android/fenix/app/src/main/res/drawable/ic_scan.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_background.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_background.xml new file mode 100644 index 0000000000000..d898305133f3b --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_background.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_marketing.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_marketing.xml new file mode 100644 index 0000000000000..3ef91aa771e6c --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_marketing.xml @@ -0,0 +1,528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_notifications.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_notifications.xml new file mode 100644 index 0000000000000..2bcf8159588a2 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_notifications.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_set_to_default.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_set_to_default.xml new file mode 100644 index 0000000000000..bf469b3d8b2b5 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_set_to_default.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync.xml new file mode 100644 index 0000000000000..15ee314cd214e --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync.xml @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync_qr.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync_qr.xml new file mode 100644 index 0000000000000..fb755481e4904 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_sync_qr.xml @@ -0,0 +1,1010 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_active.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_active.xml new file mode 100644 index 0000000000000..6cb227017052c --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_active.xml @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_inactive.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_inactive.xml new file mode 100644 index 0000000000000..868f8872baa1d --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_bottom_inactive.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_active.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_active.xml new file mode 100644 index 0000000000000..33ab78e22e680 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_active.xml @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_inactive.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_inactive.xml new file mode 100644 index 0000000000000..31aa44c3fd80a --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_toolbar_top_inactive.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou.xml new file mode 100644 index 0000000000000..5c56d94f27f6e --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou.xml @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou_2.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou_2.xml new file mode 100644 index 0000000000000..1ae806335ffe7 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_tou_2.xml @@ -0,0 +1,848 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_widget.xml b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_widget.xml new file mode 100644 index 0000000000000..d0a2664477f88 --- /dev/null +++ b/mobile/android/fenix/app/src/main/res/drawable/nova_onboarding_widget.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/fenix/app/src/main/res/layout/fragment_turn_on_sync.xml b/mobile/android/fenix/app/src/main/res/layout/fragment_turn_on_sync.xml index cbac8400062fd..1b51d6e0f7871 100644 --- a/mobile/android/fenix/app/src/main/res/layout/fragment_turn_on_sync.xml +++ b/mobile/android/fenix/app/src/main/res/layout/fragment_turn_on_sync.xml @@ -36,11 +36,11 @@ android:layout_marginTop="16dp" android:adjustViewBounds="true" android:importantForAccessibility="no" - android:scaleType="fitXY" + android:scaleType="fitCenter" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/title_sign_in" - app:srcCompat="@drawable/ic_scan" /> + app:srcCompat="@drawable/nova_onboarding_sync_qr" /> = 0)", - "startDate": "2025-12-10", - "enrollmentEndDate": "2026-01-22", - "endDate": null, - "proposedDuration": 28, - "proposedEnrollment": 7, - "referenceBranch": "control", - "featureValidationOptOut": false, - "localizations": null, - "locales": null, - "publishedDate": "2025-12-10T19:36:16.354540Z", - "isFirefoxLabsOptIn": false, - "firefoxLabsTitle": "", - "firefoxLabsDescription": "", - "firefoxLabsDescriptionLinks": null, - "firefoxLabsGroup": "", - "requiresRestart": false - }, { "schemaVersion": "1.12.0", "slug": "android-ui-redesign-new-users-release-147", diff --git a/mobile/android/fenix/app/src/main/res/values/strings.xml b/mobile/android/fenix/app/src/main/res/values/strings.xml index 2392e162f5747..105f2a888ad44 100644 --- a/mobile/android/fenix/app/src/main/res/values/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values/strings.xml @@ -472,54 +472,54 @@ Manage - Launch the ultimate Android browser + Launch the ultimate Android browser - Load sites lightning fast + Load sites lightning fast - Automatic tracking protection + Automatic tracking protection - Thousands of customizations + Thousands of customizations - By continuing, you agree to the %1$s. + By continuing, you agree to the %1$s. - Firefox Terms of Use + Firefox Terms of Use - Firefox cares about your privacy. Learn more in our %1$s. + Firefox cares about your privacy. Learn more in our %1$s. - Privacy Notice + Privacy Notice - To help improve the browser, Firefox sends diagnostic and interaction data to Mozilla. %1$s + To help improve the browser, Firefox sends diagnostic and interaction data to Mozilla. %1$s - Manage Settings + Manage Settings - Continue + Continue - Where do you want your address bar? + Where do you want your address bar? - Continue + Continue - Help us grow Firefox + Help us grow Firefox - Share how you discovered Firefox, and that you use it, with Mozilla’s marketing partners. This data is never sold. + Share how you discovered Firefox, and that you use it, with Mozilla’s marketing partners. This data is never sold. - How we use the data + How we use the data - Confirm + Confirm - Get automatic tracking protection by default + Get automatic tracking protection by default - One tap helps stop companies spying on your clicks. + One tap helps stop companies spying on your clicks. - Sync everywhere you use Firefox + Sync everywhere you use Firefox - Get bookmarks, tabs, and passwords on any device. All protected with encryption. + Get bookmarks, tabs, and passwords on any device. All protected with encryption. - Start syncing + Start syncing - Not now + Not now Help us grow Firefox @@ -540,55 +540,57 @@ - Continue + Continue - Not now + Not now - Get ready to run free + Get ready to run free - Speedy, safe, and won’t sell you out.\nBrowsing just got better. + Speedy, safe, and won’t sell you out.\nBrowsing just got better. - By continuing, you agree to the %1$s. + By continuing, you agree to the %1$s. - Firefox Terms of Use + Firefox Terms of Use - Firefox cares about your privacy. Learn more in our %1$s. + Firefox cares about your privacy. Learn more in our %1$s. - Privacy Notice + Privacy Notice - To help improve the browser, Firefox sends diagnostic and interaction data to Mozilla. %1$s + To help improve the browser, Firefox sends diagnostic and interaction data to Mozilla. %1$s - Manage settings + Manage settings Say goodbye to creepy trackers + + Open all your links with built-in privacy - We protect your data and automatically block companies from spying on your clicks. + We protect your data and automatically block companies from spying on your clicks. - Set as default browser + Set as default browser - Choose your address bar + Choose your address bar - Top + Top - Bottom + Bottom Pick your theme @@ -603,23 +605,23 @@ Dark - Instantly pick up where you left off + Instantly pick up where you left off - Grab bookmarks, passwords, and more on any device in a snap. Your personal data stays safe and secure with encryption. + Grab bookmarks, passwords, and more on any device in a snap. Your personal data stays safe and secure with encryption. - Start syncing + Start syncing - Help us build a better internet + Help us build a better internet - Share how you discovered Firefox, and that you use it, with Mozilla’s marketing partners. This data is never sold. %1$s + Share how you discovered Firefox, and that you use it, with Mozilla’s marketing partners. This data is never sold. %1$s - How we use the data + How we use the data Notifications help you stay safer with Firefox @@ -628,16 +630,16 @@ Discover the latest privacy features in Firefox so you’re always up to date on how to stay protected. - Turn on notifications + Turn on notifications - Make every search more private + Make every search more private - Start every search from your phone’s home screen and know Firefox’s automatic protections have your back. + Start every search from your phone’s home screen and know Firefox’s automatic protections have your back. - Add Firefox widget + Add Firefox widget diff --git a/mobile/android/fenix/docs/Telemetry-implementation,-reviews,-renewals.md b/mobile/android/fenix/docs/Telemetry-implementation,-reviews,-renewals.md index 81f3ea5825f92..bad3e32668822 100644 --- a/mobile/android/fenix/docs/Telemetry-implementation,-reviews,-renewals.md +++ b/mobile/android/fenix/docs/Telemetry-implementation,-reviews,-renewals.md @@ -1,23 +1,21 @@ # Telemetry - Implementation, Reviews, Renewals -See https://github.com/mozilla-mobile/fenix/wiki/Telemetry-Checklist for the steps to implement new probes. - # Creating Glean Annotations -Glean Annotations repository: https://github.com/mozilla/glean-annotations - -See [Add a Glean Annotation for an event](https://github.com/mozilla-mobile/fenix/wiki/Add-a-Glean-Annotation-for-an-event) for instructions. +Glean Annotations repository: -More info [here](https://mozilla.github.io/glean-annotations/contributing/creating/) +See the documentation on how to [create new annotations](https://mozilla.github.io/glean-annotations/contributing/creating/). # Data review -Data reviews are needed on all PRs that add new telemetry or modify existing telemetry. +Data reviews are needed on all patches that add new telemetry or modify existing telemetry. +Any change that touches metrics will be automatically flagged with a `needs-data-classification` tag by Phabricator. +If a change adds/updates data collection in a way that doesn’t automatically trigger this rule, this tag should be added manually (and if appropriate, please file a bug to update the herald rule so it happens automatically next time). + +More details about this process can be found in the [in-tree docs](https://firefox-source-docs.mozilla.org/contributing/data-review.html) and [wiki](https://wiki.mozilla.org/Data_Collection). +Add a link for the bug adding or changing a metric or ping to the `bugs` and `data_reviews` lists -1. The implementer must complete the forms for [data renewal](https://github.com/mozilla/data-review/blob/main/renewal_request.md) or [a new data request](https://github.com/mozilla/data-review/blob/main/request.md) and put them as a comment in their PR. -2. Once the form is complete, contact a [Data Steward](https://wiki.mozilla.org/Data_Collection) to arrange a review. Note: a data review does not replace code review! The PR should not land without both a data review and a code review. -3. Once the data review is complete, add the link to the approval in the `data_reviews` sub-section of your metric in the `metrics.yaml` file. Example: ``` @@ -27,11 +25,9 @@ download_notification: description: | A user resumed a download in the download notification bugs: - - https://github.com/mozilla-mobile/fenix/issues/5583 + - https://bugzilla.mozilla.org/show_bug.cgi?id=EXAMPLE data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/6554 - - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877 - - https://github.com/mozilla-mobile/fenix/pull/18143 + - https://bugzilla.mozilla.org/show_bug.cgi?id=EXAMPLE data_sensitivity: - interaction notification_emails: @@ -39,71 +35,6 @@ download_notification: expires: "2021-07-01" ``` -When a telemetry probe is being renewed, do not remove the old data review links from `metrics.yaml`. The new approval should be added to the existing list. - -Make sure you are selecting the correct Category of data that is being collected: https://wiki.mozilla.org/Data_Collection#Data_Collection_Categories - -# Renewing existing telemetry - -1. Collect a list of metrics from metrics.yaml file in Fenix that will be expiring in that month - a. Currently compiling these in [this doc](https://docs.google.com/document/d/1NGlnTa9TPyTnd3ciUPbwujbITjkX8p8vJybXcZrrM2w/edit#) - b. This should be done at least a few weeks prior to the events/metrics' expiration date - c. Including metric name, original data review PR link, description (if it’s unclear from the name) -2. Figure out the owner for each of the metrics (who needs to give the OK to renew/remove) - a. Most renewals will need product approval - b. Other approvals could come from the engineering team (e.g. `preferences.remote_debugging_enabled`), GV, App Services, Performance (e.g. `startup.timeline.framework_primary`), etc. -3. Answer any open questions for the metric owners, and get approval from them to: - a. Renew the metric (for how long? 6 months? 1 year?) - b. Choose not to renew (but not delete) - c. Choose to remove the metric - d. Renew the metric and set to never expire (this should only be for business critical metrics) -4. Fill out the [renewal request](https://github.com/mozilla/data-review/blob/main/renewal_request.md) for each metric, with question 3 (“Why was the initial period of collection insufficient?”) being answered by the owner -5. Open a PR for the renewals - a. Sometimes it’s easier to split up the renewals into multiple PRs if there are multiple owners: e.g. [product renewals](https://github.com/mozilla-mobile/fenix/pull/21788), [perf renewals](https://github.com/mozilla-mobile/fenix/pull/21315), [Fission renewals](https://github.com/mozilla-mobile/fenix/pull/21779) for December 2021 -6. Get a data review for your [renewal request](https://github.com/mozilla/data-review/blob/main/renewal_request.md) and update each of the metrics’ data-reviews in metrics.yaml to reflect this - -## Approval process - -For each telemetry probe that we want to renew, the data-review will ask us [these questions](https://github.com/mozilla/data-review/blob/main/renewal_request.md). Each probe/group of related probes should have answers to those questions ([example](https://github.com/mozilla-mobile/fenix/pull/20517#issuecomment-887038794)). - -### Example renewal data request -``` -# Request for Data Collection Renewal - -`search_shortcuts:` - selected -1) Provide a link to the initial Data Collection Review Request for this collection. -- https://github.com/mozilla-mobile/fenix/pull/1202#issuecomment-476870449 -- https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 -- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - -2) When will this collection now expire? 08/01/2022 -3) Why was the initial period of collection insufficient? -Important for revenue tracking and optimization. - -`Toolbar_settings:` - changed_position: -1) Provide a link to the initial Data Collection Review Request for this collection. -- https://github.com/mozilla-mobile/fenix/pull/6608 -- https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 -- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - -2) When will this collection now expire? 08/01/2022 -3) Why was the initial period of collection insufficient? -The data didn’t initially tell us what we wanted (there were bugs), so we want to continue tracking this so we can answer our questions. - -`login_dialog:` - displayed: -cancelled -saved -never_save -1) Provide a link to the initial Data Collection Review Request for this collection. -- https://github.com/mozilla-mobile/fenix/pull/13050 -- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - -2) When will this collection now expire? 08/01/2022 -3) Why was the initial period of collection insufficient? -Still need to optimize this feature and we want trends from 6+mo of data. -``` +When a telemetry metric is being renewed, do not remove the old data review links from `metrics.yaml`. The new approval should be added to the existing list. -For product-defined telemetry, this will involve meeting with a product manager and discussing each probe. There are three options: renew the probe for another length of time (usually 6 months), let the probe expire to evaluate later if the probe is still needed, or remove the probe entirely. +Make sure you are selecting the correct category of data that is being collected: [Data Collection Categories](https://wiki.mozilla.org/Data_Collection#Data_Collection_Categories) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebAuthnUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebAuthnUtils.java index 7403b6cea09b5..57999b74af5ca 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebAuthnUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebAuthnUtils.java @@ -133,6 +133,10 @@ public static class MakeCredentialResponse { public final String[] transports; public final String authenticatorAttachment; public final Boolean credProps; + public final Boolean prfEnabled; + public final byte[] prfFirst; + public final byte[] prfSecond; + public final Boolean largeBlobSupported; public static final class Builder { private byte[] mClientDataJson; @@ -141,6 +145,10 @@ public static final class Builder { private String[] mTransports; private String mAuthenticatorAttachment; private Boolean mCredProps; + private Boolean mPrfEnabled; + private byte[] mPrfFirst; + private byte[] mPrfSecond; + private Boolean mLargeBlobSupported; public Builder() {} @@ -174,6 +182,26 @@ public Builder setCredProps(final boolean credProps) { return this; } + public Builder setPrfEnabled(final boolean prfEnabled) { + this.mPrfEnabled = Boolean.valueOf(prfEnabled); + return this; + } + + public Builder setPrfFirst(final byte[] prfFirst) { + this.mPrfFirst = prfFirst; + return this; + } + + public Builder setPrfSecond(final byte[] prfSecond) { + this.mPrfSecond = prfSecond; + return this; + } + + public Builder setLargeBlobSupported(final boolean largeBlobSupported) { + this.mLargeBlobSupported = Boolean.valueOf(largeBlobSupported); + return this; + } + public MakeCredentialResponse build() { return new MakeCredentialResponse(this); } @@ -187,6 +215,10 @@ protected MakeCredentialResponse(final Builder builder) { this.transports = builder.mTransports; this.authenticatorAttachment = builder.mAuthenticatorAttachment; this.credProps = builder.mCredProps; + this.prfEnabled = builder.mPrfEnabled; + this.prfFirst = builder.mPrfFirst; + this.prfSecond = builder.mPrfSecond; + this.largeBlobSupported = builder.mLargeBlobSupported; } @WrapForJNI(skip = true) @@ -212,6 +244,24 @@ public String toString() { if (this.credProps != null) { sb.append(", credProps=").append(this.credProps.booleanValue()); } + if (this.prfEnabled != null) { + sb.append(", prfEnabled=").append(this.prfEnabled.booleanValue()); + } + if (this.prfFirst != null) { + sb.append(", prfFirst=") + .append( + Base64.encodeToString( + this.prfFirst, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + } + if (this.prfSecond != null) { + sb.append(", prfSecond=") + .append( + Base64.encodeToString( + this.prfSecond, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + } + if (this.largeBlobSupported != null) { + sb.append(", largeBlobSupported=").append(this.largeBlobSupported.booleanValue()); + } sb.append("}"); return sb.toString(); } @@ -225,6 +275,10 @@ public static class GetAssertionResponse { public final byte[] signature; public final byte[] userHandle; public final String authenticatorAttachment; + public final byte[] prfFirst; + public final byte[] prfSecond; + public final byte[] largeBlobBlob; + public final Boolean largeBlobWritten; public static final class Builder { private byte[] mClientDataJson; @@ -233,6 +287,10 @@ public static final class Builder { private byte[] mSignature; private byte[] mUserHandle; private String mAuthenticatorAttachment; + private byte[] mPrfFirst; + private byte[] mPrfSecond; + private byte[] mLargeBlobBlob; + private Boolean mLargeBlobWritten; public Builder() {} @@ -266,6 +324,26 @@ public Builder setAuthenticatorAttachment(final String authenticatorAttachment) return this; } + public Builder setPrfFirst(final byte[] prfFirst) { + this.mPrfFirst = prfFirst; + return this; + } + + public Builder setPrfSecond(final byte[] prfSecond) { + this.mPrfSecond = prfSecond; + return this; + } + + public Builder setLargeBlobBlob(final byte[] largeBlobBlob) { + this.mLargeBlobBlob = largeBlobBlob; + return this; + } + + public Builder setLargeBlobWritten(final boolean largeBlobWritten) { + this.mLargeBlobWritten = Boolean.valueOf(largeBlobWritten); + return this; + } + public GetAssertionResponse build() { return new GetAssertionResponse(this); } @@ -283,6 +361,10 @@ protected GetAssertionResponse(final Builder builder) { this.userHandle = builder.mUserHandle; } this.authenticatorAttachment = builder.mAuthenticatorAttachment; + this.prfFirst = builder.mPrfFirst; + this.prfSecond = builder.mPrfSecond; + this.largeBlobBlob = builder.mLargeBlobBlob; + this.largeBlobWritten = builder.mLargeBlobWritten; } @WrapForJNI(skip = true) @@ -312,8 +394,29 @@ public String toString() { this.userHandle, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING) : "(empty)") .append(", authenticatorAttachment=") - .append(this.authenticatorAttachment) - .append("}"); + .append(this.authenticatorAttachment); + if (this.prfFirst != null) { + sb.append(", prfFirst=") + .append( + Base64.encodeToString( + this.prfFirst, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + } + if (this.prfSecond != null) { + sb.append(", prfSecond=") + .append( + Base64.encodeToString( + this.prfSecond, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + } + if (this.largeBlobBlob != null) { + sb.append(", largeBlobBlob=") + .append( + Base64.encodeToString( + this.largeBlobBlob, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + } + if (this.largeBlobWritten != null) { + sb.append(", largeBlobWritten=").append(this.largeBlobWritten.booleanValue()); + } + sb.append("}"); return sb.toString(); } } @@ -338,8 +441,37 @@ public static MakeCredentialResponse getMakeCredentialResponse(final @NonNull St try { final JSONObject clientExtensionResults = json.getJSONObject("clientExtensionResults"); - final JSONObject credProps = clientExtensionResults.getJSONObject("credProps"); - builder.setCredProps(credProps.getBoolean("rk")); + try { + final JSONObject credProps = clientExtensionResults.getJSONObject("credProps"); + builder.setCredProps(credProps.getBoolean("rk")); + } catch (final JSONException e) { + // credProps is optional + } + try { + final JSONObject prf = clientExtensionResults.getJSONObject("prf"); + if (prf.has("enabled")) { + builder.setPrfEnabled(prf.getBoolean("enabled")); + } + if (prf.has("results")) { + final JSONObject prfResults = prf.getJSONObject("results"); + if (prfResults.has("first")) { + builder.setPrfFirst(Base64.decode(prfResults.getString("first"), Base64.URL_SAFE)); + } + if (prfResults.has("second")) { + builder.setPrfSecond(Base64.decode(prfResults.getString("second"), Base64.URL_SAFE)); + } + } + } catch (final JSONException e) { + // prf is optional + } + try { + final JSONObject largeBlob = clientExtensionResults.getJSONObject("largeBlob"); + if (largeBlob.has("supported")) { + builder.setLargeBlobSupported(largeBlob.getBoolean("supported")); + } + } catch (final JSONException e) { + // largeBlob is optional + } } catch (final JSONException e) { // clientExtensionResults is an optional. Ignore exception if nothing. } @@ -372,6 +504,37 @@ public static GetAssertionResponse getGetAssertionResponse(final @NonNull String // userHandle is an optional. Ignore exception if nothing. } + try { + final JSONObject clientExtensionResults = json.getJSONObject("clientExtensionResults"); + try { + final JSONObject prf = clientExtensionResults.getJSONObject("prf"); + if (prf.has("results")) { + final JSONObject prfResults = prf.getJSONObject("results"); + if (prfResults.has("first")) { + builder.setPrfFirst(Base64.decode(prfResults.getString("first"), Base64.URL_SAFE)); + } + if (prfResults.has("second")) { + builder.setPrfSecond(Base64.decode(prfResults.getString("second"), Base64.URL_SAFE)); + } + } + } catch (final JSONException e) { + // prf is optional + } + try { + final JSONObject largeBlob = clientExtensionResults.getJSONObject("largeBlob"); + if (largeBlob.has("blob")) { + builder.setLargeBlobBlob(Base64.decode(largeBlob.getString("blob"), Base64.URL_SAFE)); + } + if (largeBlob.has("written")) { + builder.setLargeBlobWritten(largeBlob.getBoolean("written")); + } + } catch (final JSONException e) { + // largeBlob is optional + } + } catch (final JSONException e) { + // clientExtensionResults is optional + } + // This response may have clientDataJson value, but signed hash is generated by request // parameter's hash. So we don't use the value into response. diff --git a/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/WebAuthnUtilsTest.java b/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/WebAuthnUtilsTest.java index f59316eb851a0..85a42de77b123 100644 --- a/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/WebAuthnUtilsTest.java +++ b/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/WebAuthnUtilsTest.java @@ -28,7 +28,7 @@ public void responseJSONForMakeCredential() throws Exception { + "\"type\": \"public-key\" ," + "\"authenticatorAttachment\": \"platform\", " + "\"response\": {\"attestationObject\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"transports\": [ \"internal\" ]}," - + "\"clientExtensionResults\": {\"credProps\": {\"rk\": true } }" + + "\"clientExtensionResults\": {\"credProps\": {\"rk\": true }, \"prf\": {\"enabled\": true}, \"largeBlob\": {\"supported\": true}}" + "}"; final WebAuthnUtils.MakeCredentialResponse response = WebAuthnUtils.getMakeCredentialResponse(responseJSON); @@ -45,6 +45,10 @@ public void responseJSONForMakeCredential() throws Exception { Arrays.equals(response.attestationObject, attestationObject)); assertEquals("No clientDataJson in response", response.clientDataJson, null); assertTrue("handle credProps", response.credProps); + assertTrue("prfEnabled should be true", response.prfEnabled); + assertEquals("prfFirst should be null", response.prfFirst, null); + assertEquals("prfSecond should be null", response.prfSecond, null); + assertTrue("largeBlobSupported should be true", response.largeBlobSupported); } @Test(expected = JSONException.class) @@ -69,7 +73,8 @@ public void responseJSONForGetAssertion() throws Exception { + "\"id\": \"AAECAwQFBgcICQoLDA0ODw\" ," + "\"rawId\": \"AAECAwQFBgcICQoLDA0ODw\" ," + "\"authenticatorAttachment\": \"platform\", " - + "\"response\": {\"authenticatorData\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"signature\": \"AgMEBQYHCAkKCwwNDg8QEQ\"}" + + "\"response\": {\"authenticatorData\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"signature\": \"AgMEBQYHCAkKCwwNDg8QEQ\"}," + + "\"clientExtensionResults\": {\"largeBlob\": {\"blob\": \"AAECAwQFBgcICQoLDA0ODw\"}}" + "}"; final WebAuthnUtils.GetAssertionResponse response = WebAuthnUtils.getGetAssertionResponse(responseJSON); @@ -84,12 +89,17 @@ public void responseJSONForGetAssertion() throws Exception { new byte[] { 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11 }; + final byte[] largeBlobBlob = + new byte[] {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; assertTrue("rawId should be matched", Arrays.equals(response.keyHandle, rawId)); assertTrue("authenticatorData should be matched", Arrays.equals(response.authData, authData)); assertTrue("signature should be matched", Arrays.equals(response.signature, signature)); assertEquals( "authenticatorAttachment should be matched", response.authenticatorAttachment, "platform"); assertEquals("No clientDataJson in response", response.clientDataJson, null); + assertTrue( + "largeBlobBlob should be matched", Arrays.equals(response.largeBlobBlob, largeBlobBlob)); + assertEquals("largeBlobWritten should be null", response.largeBlobWritten, null); } @Test(expected = JSONException.class) @@ -127,4 +137,92 @@ public void transportValue() throws Exception { assertEquals("JSON's transport should be matched", array.get(i), expectedJsonTransports[i]); } } + + @Test + public void responseJSONForMakeCredentialWithPrf() throws Exception { + final String responseJSON = + "{" + + "\"id\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"rawId\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"type\": \"public-key\" ," + + "\"authenticatorAttachment\": \"platform\", " + + "\"response\": {\"attestationObject\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"transports\": [ \"internal\" ]}," + + "\"clientExtensionResults\": {" + + " \"prf\": {" + + " \"enabled\": true," + + " \"results\": {" + + " \"first\": \"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8\"," + + " \"second\": \"ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8\"" + + " }" + + " }" + + "}" + + "}"; + final WebAuthnUtils.MakeCredentialResponse response = + WebAuthnUtils.getMakeCredentialResponse(responseJSON); + + final byte[] expectedFirst = + new byte[] { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f + }; + final byte[] expectedSecond = + new byte[] { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f + }; + + assertTrue("prfEnabled should be true", response.prfEnabled); + assertTrue("prfFirst should be matched", Arrays.equals(response.prfFirst, expectedFirst)); + assertTrue("prfSecond should be matched", Arrays.equals(response.prfSecond, expectedSecond)); + } + + @Test + public void responseJSONForGetAssertionWithPrf() throws Exception { + final String responseJSON = + "{" + + "\"id\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"rawId\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"authenticatorAttachment\": \"platform\", " + + "\"response\": {\"authenticatorData\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"signature\": \"AgMEBQYHCAkKCwwNDg8QEQ\"}," + + "\"clientExtensionResults\": {" + + " \"prf\": {" + + " \"results\": {" + + " \"first\": \"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8\"" + + " }" + + " }" + + "}" + + "}"; + final WebAuthnUtils.GetAssertionResponse response = + WebAuthnUtils.getGetAssertionResponse(responseJSON); + + final byte[] expectedFirst = + new byte[] { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f + }; + + assertTrue("prfFirst should be matched", Arrays.equals(response.prfFirst, expectedFirst)); + assertEquals("prfSecond should be null", response.prfSecond, null); + } + + @Test + public void responseJSONForGetAssertionWithLargeBlobWritten() throws Exception { + final String responseJSON = + "{" + + "\"id\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"rawId\": \"AAECAwQFBgcICQoLDA0ODw\" ," + + "\"authenticatorAttachment\": \"platform\", " + + "\"response\": {\"authenticatorData\": \"AQIDBAUGBwgJCgsMDQ4PEA\", \"signature\": \"AgMEBQYHCAkKCwwNDg8QEQ\"}," + + "\"clientExtensionResults\": {\"largeBlob\": {\"written\": true}}" + + "}"; + final WebAuthnUtils.GetAssertionResponse response = + WebAuthnUtils.getGetAssertionResponse(responseJSON); + + assertTrue("largeBlobWritten should be true", response.largeBlobWritten); + assertEquals("largeBlobBlob should be null", response.largeBlobBlob, null); + } } diff --git a/mobile/locales/l10n-changesets.json b/mobile/locales/l10n-changesets.json index 67f3c8317f87b..ae7497e707c72 100644 --- a/mobile/locales/l10n-changesets.json +++ b/mobile/locales/l10n-changesets.json @@ -6,7 +6,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "an": { "pin": false, @@ -15,7 +15,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ar": { "pin": false, @@ -24,7 +24,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ast": { "pin": false, @@ -33,7 +33,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "az": { "pin": false, @@ -42,7 +42,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "be": { "pin": false, @@ -51,7 +51,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bg": { "pin": false, @@ -60,7 +60,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bn": { "pin": false, @@ -69,7 +69,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "br": { "pin": false, @@ -78,7 +78,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "bs": { "pin": false, @@ -87,7 +87,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ca": { "pin": false, @@ -96,7 +96,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cak": { "pin": false, @@ -105,7 +105,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cs": { "pin": false, @@ -114,7 +114,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "cy": { "pin": false, @@ -123,7 +123,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "da": { "pin": false, @@ -132,7 +132,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "de": { "pin": false, @@ -141,7 +141,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "dsb": { "pin": false, @@ -150,7 +150,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "el": { "pin": false, @@ -159,7 +159,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "en-CA": { "pin": false, @@ -168,7 +168,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "en-GB": { "pin": false, @@ -177,7 +177,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "eo": { "pin": false, @@ -186,7 +186,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-AR": { "pin": false, @@ -195,7 +195,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-CL": { "pin": false, @@ -204,7 +204,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-ES": { "pin": false, @@ -213,7 +213,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "es-MX": { "pin": false, @@ -222,7 +222,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "et": { "pin": false, @@ -231,7 +231,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "eu": { "pin": false, @@ -240,7 +240,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fa": { "pin": false, @@ -249,7 +249,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ff": { "pin": false, @@ -258,7 +258,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fi": { "pin": false, @@ -267,7 +267,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fr": { "pin": false, @@ -276,7 +276,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "fy-NL": { "pin": false, @@ -285,7 +285,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ga-IE": { "pin": false, @@ -294,7 +294,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gd": { "pin": false, @@ -303,7 +303,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gl": { "pin": false, @@ -312,7 +312,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gn": { "pin": false, @@ -321,7 +321,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "gu-IN": { "pin": false, @@ -330,7 +330,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "he": { "pin": false, @@ -339,7 +339,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hi-IN": { "pin": false, @@ -348,7 +348,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hr": { "pin": false, @@ -357,7 +357,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hsb": { "pin": false, @@ -366,7 +366,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hu": { "pin": false, @@ -375,7 +375,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "hy-AM": { "pin": false, @@ -384,7 +384,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ia": { "pin": false, @@ -393,7 +393,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "id": { "pin": false, @@ -402,7 +402,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "is": { "pin": false, @@ -411,7 +411,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "it": { "pin": false, @@ -420,7 +420,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ja": { "pin": false, @@ -429,7 +429,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ka": { "pin": false, @@ -438,7 +438,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kab": { "pin": false, @@ -447,7 +447,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kk": { "pin": false, @@ -456,7 +456,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "km": { "pin": false, @@ -465,7 +465,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "kn": { "pin": false, @@ -474,7 +474,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ko": { "pin": false, @@ -483,7 +483,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lij": { "pin": false, @@ -492,7 +492,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lo": { "pin": false, @@ -501,7 +501,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lt": { "pin": false, @@ -510,7 +510,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ltg": { "pin": false, @@ -519,7 +519,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "lv": { "pin": false, @@ -528,7 +528,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "meh": { "pin": false, @@ -537,7 +537,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "mix": { "pin": false, @@ -546,7 +546,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ml": { "pin": false, @@ -555,7 +555,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "mr": { "pin": false, @@ -564,7 +564,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ms": { "pin": false, @@ -573,7 +573,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "my": { "pin": false, @@ -582,7 +582,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nb-NO": { "pin": false, @@ -591,7 +591,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ne-NP": { "pin": false, @@ -600,7 +600,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nl": { "pin": false, @@ -609,7 +609,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "nn-NO": { "pin": false, @@ -618,7 +618,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "oc": { "pin": false, @@ -627,7 +627,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pa-IN": { "pin": false, @@ -636,7 +636,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pl": { "pin": false, @@ -645,7 +645,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pt-BR": { "pin": false, @@ -654,7 +654,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "pt-PT": { "pin": false, @@ -663,7 +663,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "rm": { "pin": false, @@ -672,7 +672,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ro": { "pin": false, @@ -681,7 +681,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ru": { "pin": false, @@ -690,7 +690,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sk": { "pin": false, @@ -699,7 +699,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sl": { "pin": false, @@ -708,7 +708,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "son": { "pin": false, @@ -717,7 +717,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sq": { "pin": false, @@ -726,7 +726,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sr": { "pin": false, @@ -735,7 +735,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "sv-SE": { "pin": false, @@ -744,7 +744,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ta": { "pin": false, @@ -753,7 +753,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "te": { "pin": false, @@ -762,7 +762,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "th": { "pin": false, @@ -771,7 +771,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "tl": { "pin": false, @@ -780,7 +780,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "tr": { "pin": false, @@ -789,7 +789,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "trs": { "pin": false, @@ -798,7 +798,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "uk": { "pin": false, @@ -807,7 +807,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "ur": { "pin": false, @@ -816,7 +816,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "uz": { "pin": false, @@ -825,7 +825,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "vi": { "pin": false, @@ -834,7 +834,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "wo": { "pin": false, @@ -843,7 +843,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "xh": { "pin": false, @@ -852,7 +852,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "zam": { "pin": false, @@ -861,7 +861,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "zh-CN": { "pin": false, @@ -870,7 +870,7 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" }, "zh-TW": { "pin": false, @@ -879,6 +879,6 @@ "android-arm", "android-multilocale" ], - "revision": "a703deb818613cc35e3e8b5d9a9a31880686b901" + "revision": "7c77718c6cc1c3607cb5584c7272359a7a51f120" } } \ No newline at end of file diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index d7ca509958aeb..bcb1cdf7df134 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -5850,6 +5850,12 @@ value: false mirror: always +# Block notifications and revoke permission for sites on Safe Browsing lists. +- name: dom.webnotifications.block_if_on_safebrowsing + type: RelaxedAtomicBool + value: @IS_NIGHTLY_BUILD@ + mirror: always + - name: dom.worker.canceling.timeoutMilliseconds type: RelaxedAtomicUint32 value: 30000 # 30 seconds diff --git a/python/sites/mach.txt b/python/sites/mach.txt index 9c3984e650547..46593aee515cd 100644 --- a/python/sites/mach.txt +++ b/python/sites/mach.txt @@ -58,7 +58,7 @@ pth:xpcom/geckoprocesstypes_generator pth:xpcom/idl-parser # glean-sdk may not be installable if a wheel isn't available # and it has to be built from source. -pypi-optional:glean-sdk==66.3.0:telemetry will not be collected +pypi-optional:glean-sdk==67.0.0:telemetry will not be collected pypi-optional:orjson>=3.10:json operations will be slower in various tools # Mach gracefully handles the case where `psutil` is unavailable. # We aren't (yet) able to pin packages in automation, so we have to diff --git a/security/ct/CTKnownLogs.h b/security/ct/CTKnownLogs.h index 3dd5451f581d4..6fcd6e22011cf 100644 --- a/security/ct/CTKnownLogs.h +++ b/security/ct/CTKnownLogs.h @@ -14,7 +14,7 @@ #include -static const PRTime kCTExpirationTime = INT64_C(1775472842000000); +static const PRTime kCTExpirationTime = INT64_C(1775731982000000); namespace mozilla::ct { diff --git a/security/manager/ssl/StaticHPKPins.h b/security/manager/ssl/StaticHPKPins.h index b33af3766f650..8e95817193752 100644 --- a/security/manager/ssl/StaticHPKPins.h +++ b/security/manager/ssl/StaticHPKPins.h @@ -681,4 +681,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = { static const int32_t kUnknownId = -1; -static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1777892029322000); +static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1778151169377000); diff --git a/security/manager/ssl/nsSTSPreloadList.inc b/security/manager/ssl/nsSTSPreloadList.inc index 5a9aaceff4493..79a517299f8d1 100644 --- a/security/manager/ssl/nsSTSPreloadList.inc +++ b/security/manager/ssl/nsSTSPreloadList.inc @@ -8,7 +8,7 @@ /*****************************************************************************/ #include -const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); +const PRTime gPreloadListExpirationTime = INT64_C(1780570364645000); %% 0--1.de, 1 0-0.io, 1 @@ -1416,7 +1416,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 1xbetkor.com, 1 1xbetkr.com, 1 1xbetkr1.com, 1 -1xbetmm.com, 1 1xbetqq.com, 1 1xbetsd.com, 1 1xbetst.com, 1 @@ -1879,7 +1878,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 2ugaming.com, 1 2url.link, 1 2ustyle.com, 1 -2value.com, 1 2vnews.com, 1 2vp-an.online, 1 2wth.com, 1 @@ -2045,6 +2043,7 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 360stone.com, 1 360system.com, 1 360techpartner.com, 1 +360trust.com, 1 360videoshare.com, 1 360visualmedia.co.uk, 1 360vrs.com, 1 @@ -2675,7 +2674,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 4vf.de, 1 4vio.com, 1 4web-hosting.com, 1 -4woods.eu, 1 4wrd.cc, 1 4x4-27mc.nl, 1 4x4coatingen.nl, 1 @@ -2880,7 +2878,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 55365t.com, 1 5557552.com, 1 555bet86.com, 1 -555btt.com, 1 555kb.com, 1 555w.org, 1 555xl.com, 1 @@ -3544,7 +3541,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1780311224884000); 7kovrikov.ru, 1 7kvadratov.by, 1 7l00p.com, 1 -7matic.net, 1 7money.co, 1 7net.uk, 1 7nets.com, 1 @@ -5093,7 +5089,6 @@ aachc.org, 1 aachen-quiz.de, 1 aachen-umzugsunternehmen.de, 1 aacs-design.com, 1 -aact.org, 1 aad-gp.com, 1 aadl.ga, 1 aadskeuken.nl, 1 @@ -5239,7 +5234,7 @@ abasite.tk, 1 abasky.net, 1 abasteo.mx, 1 abastor.tk, 1 -abaton.es, 0 +abaton.es, 1 abay-today.tk, 1 abbadabbabouncycastles.co.uk, 1 abbas.ch, 1 @@ -6512,7 +6507,6 @@ adihomes.com, 1 adilsabri.tk, 1 adimaja.com, 1 adimco.nl, 0 -adimmo.net, 1 adimo.com.pl, 1 adimplere.com.br, 1 adinaporter.com, 1 @@ -6747,6 +6741,7 @@ adst.dk, 1 adstasin.com, 1 adstop.ga, 1 adstop.ml, 1 +adstune.com, 1 adsviews.gq, 1 adswoo.com, 1 adt.co.za, 1 @@ -7345,7 +7340,6 @@ afterinc.com, 1 afterlifeos.com, 1 aftermagic.com, 1 aftermarketinternational.com, 1 -aftermix.com, 1 afternoonhereyes.tk, 1 afteroblivion.tk, 1 afterpartyme.com, 1 @@ -7945,7 +7939,6 @@ aids.gov, 1 aie.de, 1 aiesecarad.ro, 1 aievaluare.ro, 1 -aifi.or.id, 1 aifieldmanagement.com, 1 aifob.tk, 1 aifriccampbell.com, 1 @@ -8638,7 +8631,6 @@ akaziya.cf, 1 akbam.co.uk, 1 akbarsempoi.tk, 1 akbas.tk, 1 -akblasch.com.au, 1 akbtv.ru, 1 akcenty.com.ua, 1 akcevybuch.cz, 1 @@ -9452,12 +9444,12 @@ alibamu.com, 1 alibamu.org, 1 alibi-ua.com.ua, 1 alibip.de, 1 -alicafe.com, 1 alicante-spain.tk, 1 alice-memorial.de, 1 alice.tw, 1 alicebaldenegro.tk, 1 aliceboyle.net, 1 +aliceforchildren.it, 1 alicehairstyling.tk, 1 alicehartley.com, 1 alicekinkycat.net, 1 @@ -9619,6 +9611,8 @@ all-gsm-solutions.tk, 1 all-mountains.fr, 1 all-music.ml, 1 all-music.tk, 1 +all-payroll-solutions.com, 1 +all-payroll-solutions.de, 1 all-pics.tk, 1 all-rating.tk, 1 all-seo.tk, 1 @@ -10051,6 +10045,7 @@ almasoft.ga, 1 almastabriz.com, 0 almatinki.com, 1 almatytips.com, 1 +almawsoa.com, 1 almayadeen.education, 1 almayoreo.com.co, 1 almaz-host.ml, 1 @@ -10098,7 +10093,6 @@ alodokita.com, 1 aloe-care.tk, 1 aloe-vera-info.tk, 1 aloemeda.de, 1 -aloesbienetre.be, 1 aloesoluciones.com.ar, 1 aloeveralisboa.pt, 1 alofone.vn, 1 @@ -10123,11 +10117,9 @@ alonsoluzgas.es, 1 alonuocsuoi.com, 1 aloo.ga, 1 aloomic.com.au, 1 -aloplay.io, 1 alorimusic.es, 1 aloris-controle.fr, 1 aloro.io, 1 -alotennis.ir, 1 alotso.com, 1 alottajava.com, 1 alov.blog, 1 @@ -10323,7 +10315,6 @@ altcodes.nl, 1 altd.cz, 1 altdating.co.uk, 1 altdforyou.tk, 1 -altdubai.com, 1 alte-wassermuehle-friesoythe.de, 1 altea.it, 1 altec.pl, 1 @@ -10515,7 +10506,7 @@ alyafood.com, 1 alykkelife.com, 1 alyoung.com, 1 alyssamilano.tk, 1 -alyusr.com.sa, 0 +alyusr.com.sa, 1 alyx.sh, 1 alza.at, 1 alza.co.uk, 1 @@ -10552,7 +10543,6 @@ amabiligranilhas.com, 1 amac.tv, 1 amacuk.co.uk, 1 amad-bargh.com, 1 -amadera.com, 1 amaderforum.tk, 1 amadeusproject.cf, 1 amadin.tk, 1 @@ -10940,6 +10930,7 @@ amirarsalan.tk, 1 amirasyraf.com, 0 amirautos.com, 0 amirhanova-a.ru, 1 +amirhosseinabutalebi.com, 1 amirkaraj.tk, 1 amis-du-cinema.com, 1 amis-sh.fr, 1 @@ -11232,7 +11223,6 @@ analystexamers.ga, 1 analysts.com, 1 analytics-media.com, 1 analyticscanvas.com, 1 -analyticsinmotion.com, 1 analyticum.at, 1 analyticum.com, 1 analyticum.de, 1 @@ -12554,7 +12544,6 @@ aotech.tw, 1 aotopo.com, 1 aova.loan, 1 aoyagi-farm.jp, 1 -aoyama-azabu-dc.com, 1 aoyamacc.co.jp, 1 ap-auto.pl, 1 ap-bg.eu, 1 @@ -13271,7 +13260,6 @@ arabstar.tk, 1 arabstreamsystem.tk, 1 arabtones.tk, 1 arabwomen.ml, 1 -aracbul.com, 1 aracelissanchez.com, 1 arachnid.tk, 1 arachnida.ml, 1 @@ -13279,7 +13267,6 @@ arackiralama.name.tr, 1 aradex.de, 1 aradiantlife.org, 1 aradiantlyhealthylife.com, 1 -arados.de, 1 aradpulse.tk, 1 arafatx.com, 1 aragotownshipmn.gov, 1 @@ -14924,8 +14911,6 @@ astral.gq, 1 astral.org.pl, 1 astralrank.com, 1 astralriders.tk, 1 -astralus.com, 0 -astralus.de, 0 astramundo.com, 1 astrapool.ru, 1 astratech.com.ph, 1 @@ -15850,6 +15835,7 @@ auto-arsenal.tk, 1 auto-borse.tk, 1 auto-delchev.com, 1 auto-drpciv.ro, 1 +auto-ecole-du-tursan.fr, 1 auto-ecole-remparts.fr, 1 auto-graph.eu, 1 auto-help.tk, 1 @@ -16221,7 +16207,6 @@ autoschool.ga, 1 autoscoops.tk, 1 autoscreenshot.click, 1 autoscuola.roma.it, 1 -autosdsg.ca, 1 autosecurityfinance.com, 1 autoshopsolutions.com, 0 autosiero.nl, 1 @@ -16242,7 +16227,6 @@ autostop-occasions.be, 1 autostoresystem.com, 1 autostrady.tk, 1 autostramites.com, 1 -autostramites.com.ar, 1 autosupirkimas.tk, 1 autosynthetix.com, 1 autoteplo.org, 1 @@ -16636,7 +16620,6 @@ awakengr.com, 0 awakening-guild.com, 1 awakenplace.tk, 1 awakenwow.ga, 1 -awakewatergh.com, 0 awakinn.co.in, 1 awamally.com, 1 awangardaszkola.pl, 1 @@ -18185,7 +18168,6 @@ baratzegrowshop.com, 1 baravalle.com, 1 baraxolka.ga, 1 baraxolka.ml, 1 -barbara-bertagnolli.co.uk, 1 barbara-fuchs-gruene-fuerth.de, 1 barbarabryce.com, 1 barbaraedanielsantos.ga, 1 @@ -18341,7 +18323,7 @@ barranquillero.tk, 1 barraqueirotransportes.pt, 1 barrasaccess.com, 1 barratennis.com.br, 1 -barrebody.com.au, 1 +barrebody.com.au, 0 barreiroappraisals.com, 1 barrelfish.org, 1 barrencountyky.gov, 1 @@ -18394,7 +18376,6 @@ baruzdin.online, 1 barvarsovia.es, 1 barwave.com, 1 barzallof.com, 1 -barzus.com.ua, 1 barzza.tk, 1 bas.bio, 1 bas.codes, 1 @@ -18500,7 +18481,6 @@ basketballnewz.tk, 1 basketforex.com, 1 basketglucholazy.pl, 1 basketsandmore.bg, 1 -basketskenya.com, 1 baskingalkin.tk, 1 basllp.co.uk, 1 basmacioglu.com, 1 @@ -18912,7 +18892,6 @@ bcansw.com.au, 1 bcarpentrypro.com, 1 bcbsfepvision.com, 1 bcbudonline.com, 1 -bccbank.coop, 1 bcccremeno.it, 1 bccx.eu.org, 1 bccxo.com, 1 @@ -19134,6 +19113,7 @@ beatmalaria.org, 1 beaton.tk, 1 beatquantum.com, 1 beatrice-nightscout.herokuapp.com, 1 +beatrice-raws.org, 1 beatricedailysun.com, 1 beatriz-urbano-vega.tk, 1 beatrizaebischer.ch, 0 @@ -19170,7 +19150,6 @@ beautifulsouth.tk, 1 beauty-blog.gq, 1 beauty-form.ir, 1 beauty-haircare.tk, 1 -beauty-salon-lino.com, 1 beauty-schools.com, 1 beauty-secrets.ir, 1 beauty-stories.tk, 1 @@ -19213,6 +19192,7 @@ beautytherapies.gr, 1 beautywien.at, 1 beaver-creek.ga, 1 beavercityut.gov, 1 +beaverdamautos.com, 1 beaverheadcountymt.gov, 1 beavertails.com, 1 beavertales.ca, 1 @@ -19263,7 +19243,6 @@ bebra.io, 1 bebra.loan, 1 bebra.lu, 1 bebra.ms, 1 -bebra.pw, 1 bebrenok.trade, 1 bebrev.trade, 1 bebrik.men, 1 @@ -19963,6 +19942,7 @@ beosocial.de, 1 beospain.tk, 1 bep.gov, 1 bephoenix.org.uk, 0 +bepick.blog, 1 bepositive.ml, 1 bepxl.art, 1 bepzi.com, 1 @@ -20189,7 +20169,6 @@ besensi.com, 1 besiconstruct.be, 1 besidemetal.tk, 1 besikta.se, 1 -besiktasmtsk.com, 1 besinspain.es, 1 beskar.pro, 1 besnard.me, 1 @@ -20804,7 +20783,6 @@ betterhelporg.com, 1 betterhelpteens.com, 1 betterhome.ml, 1 betteridge.com, 1 -betterify.in, 1 betterjapanese.blog, 1 betterjapanese.org, 1 betterjapanese.xyz, 1 @@ -21073,7 +21051,6 @@ bharat.in, 1 bharatanatyam-dancer.tk, 1 bharatbillpay.com, 1 bharath-g.in, 1 -bharatskills.gov.in, 1 bharatsofttech.com, 1 bhasha.lk, 1 bhat.vn, 1 @@ -21865,7 +21842,6 @@ bioscience.co.uk, 0 biosearch.tk, 1 bioseguridad.gob.pa, 1 bioselect.com.cy, 1 -biosency.com, 1 bioshine.com.sg, 1 bioshome.de, 1 biosignalanalytics.com, 1 @@ -23099,7 +23075,6 @@ blue-python.tk, 1 blue-sky.capital, 1 blue1.com, 1 blue2purple.com, 1 -blue3investimentos.com.br, 1 blue6ix.com.br, 1 blueangel.org.tw, 1 bluearrowrecords.com, 1 @@ -23484,7 +23459,6 @@ bodixite.com, 1 bodkov.com, 1 bodlsc.com, 1 bodrumescmagazin.tk, 1 -bodrus.com, 1 bodsch.com, 1 bodumo.com, 1 bodusod.bg, 1 @@ -24194,6 +24168,7 @@ boulderswap.com, 1 boulderwagonroad.org, 1 boulebar.duckdns.org, 1 boulevard-ruijschenbergh.nl, 1 +boulstory.ru, 1 boulzicourt.fr, 1 bouma.social, 1 bounce-a-roo.co.uk, 1 @@ -24249,7 +24224,6 @@ bouncycastlehirehull.co.uk, 1 bouncycastlehirelouth.co.uk, 1 bouncycastlehiremalvern.co.uk, 1 bouncycastlehiresurrey.co.uk, 1 -bouncycastlehirewinchester.co.uk, 1 bouncycastleman.co.uk, 1 bouncycastlesgalway.com, 1 bouncycastleshire.co.uk, 1 @@ -24539,6 +24513,7 @@ brainsik.net, 1 brainsoftict.nl, 1 brainspawn.tk, 1 brainstache.com, 1 +brainstarling.com, 1 brainstew.tk, 1 brainstorm-audit.bg, 1 brainstormproductions.tk, 1 @@ -25412,7 +25387,7 @@ brunchandmatch.be, 1 brunella.tk, 1 brunetderochebrune.com, 0 brunhilde.ml, 1 -brunnerworks.com, 0 +brunnerworks.com, 1 bruno-pelletier.tk, 1 brunoamaral.eu, 0 brunoamezcua.com, 0 @@ -26015,7 +25990,6 @@ bulutimza.com.tr, 1 bulutkey.com, 1 bulvar.tk, 1 bumastemra.nl, 1 -bumble.com, 1 bumblebee.cf, 1 bumblebeekids.uk, 1 bumbleforfriends.com, 1 @@ -26259,7 +26233,7 @@ business-secreti.cf, 1 business-secreti.ga, 1 business-secreti.gq, 1 business-secreti.tk, 1 -business-swiss.ch, 0 +business-swiss.ch, 1 business.facebook.com, 0 business.gov, 0 business.medbank.com.mt, 1 @@ -27128,7 +27102,6 @@ cabinetvoyance.fr, 1 cable-bg.com, 1 cable360.de, 1 cableatierra.tk, 1 -cablebustersllc.com, 0 cablehighspeed.net, 1 cableiran.com, 1 cablemadrid.tk, 1 @@ -27272,7 +27245,6 @@ cafecentral.tk, 1 cafechesscourt.com, 1 cafechroma.tk, 1 cafecobus.tk, 1 -cafedari.ir, 1 cafedelcielo.co, 1 cafedesignbooks.com, 1 cafedospescadores.tk, 1 @@ -27292,7 +27264,7 @@ cafenoorderzon.tk, 1 cafeobscura.nl, 1 cafesangtao.com, 1 cafeterya.tk, 1 -cafethevibes.com, 0 +cafethevibes.com, 1 cafethrive.co.uk, 1 cafevelo.org, 1 cafevs.com, 0 @@ -27969,7 +27941,7 @@ canidelite-toulouse.fr, 1 canihavesome.coffee, 0 canile.it, 1 canine.tools, 1 -caninecadre.net, 1 +caninecadre.net, 0 caninejournal.com, 1 caninsulin.com, 1 caniphish.com, 1 @@ -27990,6 +27962,7 @@ cannabisdiscounter.ca, 1 cannabisfactsnevada.org, 1 cannabisfactsnv.org, 1 cannabishouseyeg.com, 1 +cannabislegality.info, 1 cannabistraininguniversity.com, 1 cannabiz.tk, 1 cannabytes.net, 1 @@ -28309,7 +28282,6 @@ carbonreal-test.azurewebsites.net, 1 carbonrus.com, 1 carbonswap.exchange, 1 carbonswap.finance, 1 -carbontv.com, 1 carbonvision.cn, 0 carbuzz.com, 1 carcatron.org, 1 @@ -28893,6 +28865,7 @@ casasdeapuestasdeportivas.cl, 1 casasparaperross.com, 1 casasuara.com, 1 casasuleletrodomesticos.com.br, 1 +casatemporada.com, 1 casatendeiro.tk, 1 casavacanze.estate, 1 casavaleria.tk, 1 @@ -28988,7 +28961,6 @@ cash-generator.tk, 1 cash.app, 1 cash.me, 1 cash.nyc, 1 -cash1loans.com, 1 cashadvanceoflebanon.com, 1 cashamerican.tk, 1 cashati.com, 1 @@ -30184,7 +30156,7 @@ cernko.de, 1 ceroresiduo.com, 0 cerovica.tk, 1 cerpus-course.com, 1 -cerqa.cloud, 1 +cerqa.cloud, 0 cerquitamio.com, 1 cerrad.com.ua, 1 cerrajeriaamericadelquindio.com, 1 @@ -30257,7 +30229,6 @@ cervenyjezek.eu, 1 cervera.cat, 1 cervera.com.br, 1 ces-ltd.co.uk, 0 -ces-ltd.com, 1 cesame.ca, 1 cesar-hector.tk, 1 cesarecirugiaplastica.com, 0 @@ -32925,7 +32896,6 @@ clearstep.health, 0 clearstone.au, 1 clearstoneip.com, 0 clearstreamcapital.com, 1 -clearsys.ee, 1 cleartheear.co.uk, 1 clearview-creative.com, 1 clearview-psychiatry.com, 1 @@ -33924,6 +33894,7 @@ codeclub.gq, 1 codecnetworks.com, 1 codecolliders.com, 1 codecommunity.io, 1 +codeconnect.ir, 1 codecool.com, 0 codecreate.co.uk, 1 codecrew.us, 1 @@ -34250,6 +34221,7 @@ coldlasers.org, 1 coldmeat.se, 1 coldpaste.com, 1 coldraven.com, 1 +coldsky.net, 1 coldspegll.gq, 1 coldspringharborcesspools.com, 1 coldspringsrancheria.gov, 1 @@ -34453,7 +34425,6 @@ colorbond.vn, 1 colorbrightongreen.org, 1 colorcodedlyrics.com, 1 colorectalpractice.com, 1 -colorfittings.com, 1 colorfularchive.eu.org, 1 colorfulcloud.eu.org, 1 colorfuldots.com, 1 @@ -34497,7 +34468,6 @@ colquittga.gov, 1 colson-occasions.be, 0 coltellisurvival.com, 1 colterris.com, 1 -colthesweep.co.uk, 0 coltonrb.com, 1 coluit.nl, 1 columbiachronicle.com, 1 @@ -35437,6 +35407,7 @@ contaminatie.nl, 1 contango.xyz, 1 contecgmbh.com, 1 contempfleury.com, 1 +contemplativeeducation.org, 1 contemptevoke.com, 1 contenedoresdereciclaje.online, 1 contentcaching.com, 1 @@ -35668,7 +35639,6 @@ cooscountynh.gov, 1 cooscountyor.gov, 1 copa.cf, 1 copabarena.tk, 1 -copabay.com, 1 copacabanafc.tk, 1 copadata.be, 1 copain.com.mx, 1 @@ -35780,7 +35750,6 @@ core3k.org, 1 core3k.us, 1 coreapm.com, 1 coreapm.org, 1 -coreassurance.com, 1 corebit.nl, 1 corecapital.cz, 1 corecdn.org, 1 @@ -36980,7 +36949,7 @@ crisp.watch, 1 crispinplasticsurgery.com, 1 crispinusphotography.com, 1 crisple.info, 1 -crispregional.org, 1 +crispregional.org, 0 crispsheets.com, 1 crispybacon.ml, 1 crisscrossjazz.com, 1 @@ -37207,12 +37176,12 @@ crtclaims.com, 1 crtevents.co.uk, 1 crtified.me, 1 crucial.com, 1 -crucial.de, 1 -crucial.es, 1 -crucial.fr, 1 -crucial.in, 1 -crucial.jp, 1 -crucial.tw, 1 +crucial.de, 0 +crucial.es, 0 +crucial.fr, 0 +crucial.in, 0 +crucial.jp, 0 +crucial.tw, 0 crucible.io, 1 crudly.ai, 1 cruelalice.net, 1 @@ -37335,6 +37304,7 @@ cryptomining.mobi, 1 cryptomintecho.com, 1 cryptomixer.io, 1 cryptomkt.com, 1 +cryptomonnaies.io, 1 crypton.vercel.app, 1 crypton.xyz, 1 cryptonaire.ga, 1 @@ -38880,7 +38850,7 @@ daltoncraven.com, 0 daltonmi.gov, 1 daltonohio.gov, 1 daltons.tk, 1 -dalux.com, 1 +dalux.com, 0 dalycity.gov, 1 damadam.pk, 1 damades.com, 1 @@ -39221,7 +39191,6 @@ daotaoantoan.org, 1 daoudi.it, 1 dap-systems.de, 1 daphnedietz.com, 1 -daplie.com, 1 dapmalaysia.org, 1 dapoxetinagenerico.cf, 1 dapoxetine.gq, 1 @@ -40300,7 +40269,6 @@ deavita.fr, 1 deavita.net, 1 deavon.tk, 1 debacker.biz, 1 -debarbas.com, 1 debarkader34.tk, 1 debarras-diogene.paris, 1 debarrasantony.com, 1 @@ -40524,7 +40492,6 @@ deepid.com, 1 deepinsight.io, 0 deepl.com, 1 deepmountains.tk, 1 -deepneuro.ai, 1 deepnote.com, 1 deeps.cat, 1 deepserve.info, 1 @@ -40640,7 +40607,6 @@ degreeducation.tk, 1 degreeverify.com, 1 degreeverify.net, 1 degreeverify.org, 1 -degressif.com, 1 degroetenvanrosaline.nl, 1 degrootenslot.nl, 0 degroupnews.com, 1 @@ -40664,7 +40630,6 @@ deimos.gq, 1 deimos.io, 1 dein-baumdienst.de, 1 dein-trueffel.de, 1 -dein.mx, 1 deinballon.de, 1 deindeal.ch, 1 deindustry.com, 1 @@ -41322,7 +41287,7 @@ dermatologyandlasergroup.com, 1 dermax.tk, 1 dermedia.tk, 1 dermediq.nl, 1 -dermko.cz, 1 +dermko.cz, 0 dermnet.ai, 1 dermody.ga, 1 dermopigmentista.it, 1 @@ -41388,7 +41353,6 @@ desertfade.eu, 1 desertfiredesigns.com, 1 desertfury.tk, 1 desertgrove.com, 1 -desertheroesteam.com, 1 deserti.tk, 1 desertlinealuminium.com, 1 desertlinegroup.com, 1 @@ -41528,7 +41492,6 @@ destentor.nl, 1 desterman.ru, 1 desterrada.tk, 1 destguides.com, 1 -destileria.net.br, 1 destilteomarmd.be, 1 destinflow.com, 1 destinia.us, 1 @@ -41897,7 +41860,7 @@ dfctaiwan.org, 1 dffgpro.de, 1 dfg.re, 1 dfiik.com, 1 -dfilucky.com, 1 +dfilucky.com, 0 dfm.ae, 0 dfmn.berlin, 1 dfmvf.org, 1 @@ -42031,7 +41994,6 @@ dia-te.ch, 1 dia.com.br, 1 diaakademi.com, 1 diabetes-te.ch, 1 -diabetes.tools, 1 diabetesdietjournal.com, 1 diabetessucks.net, 1 diableros.tk, 1 @@ -42565,7 +42527,6 @@ digitalinberlin.de, 1 digitalistan.tk, 1 digitalizer.my.id, 1 digitalizzazioneverona.it, 0 -digitaljuliac.com.br, 1 digitalkashmir.ml, 1 digitalkey.pro, 1 digitalkuber.com, 1 @@ -42745,15 +42706,10 @@ dimmersnewburypark.com, 1 dimmersoakpark.com, 1 dimmersthousandoaks.com, 1 dimmerswestlakevillage.com, 1 -dimo-crm.fr, 1 -dimo-dematerialisation.com, 1 -dimo-tresorerie.fr, 1 dimokratikiaristera.gr, 0 -dimomaint.de, 1 dimonz.com, 1 dimoredimara.com, 1 dimory.com, 1 -dimosoftware.fr, 1 dimseklubben.dk, 1 dimtrade.rs, 1 dina.wtf, 1 @@ -43251,7 +43207,6 @@ ditra.ae, 0 ditte-destree.fr, 1 dittvertshus.no, 1 ditxse6.com, 1 -ditxse6.org, 1 div.energy, 1 diva-app.de, 1 divacresent.tk, 1 @@ -43274,7 +43229,6 @@ divernonil.gov, 1 diversant.bg, 1 diverscott.com, 1 diverse-cuts.com, 0 -diverse.systems, 1 diversificarte.com, 1 diversify.cf, 1 diversify.com, 1 @@ -44438,7 +44392,7 @@ domyzitrka.cz, 1 domznak.ru, 1 don-news.tk, 1 don-zalmrol.be, 1 -dona.to, 1 +dona.to, 0 donacarlota.net.br, 1 donacije.rs, 1 donaciondeorganos.gov, 1 @@ -44452,7 +44406,6 @@ donaldwarner.com, 1 donama.co, 1 donamadeira.com.br, 1 donamflor.com, 1 -donarmany.online, 1 donat.cloud, 1 donate-streamer.com, 1 donate.lol, 1 @@ -45705,7 +45658,6 @@ dsgvo-analyse.de, 1 dsgvo.name, 1 dsh.io, 1 dshield.org, 1 -dsigroup.com.tw, 1 dsimonitor.online, 1 dsiteam.in, 1 dsjbvba.be, 1 @@ -46601,7 +46553,6 @@ e-rechnung.digital, 1 e-recruitment.tk, 1 e-referendum.cz, 1 e-repairs.tk, 1 -e-resident.gov.ee, 1 e-resident.me, 1 e-rest.tk, 1 e-rober.tk, 1 @@ -46679,7 +46630,7 @@ e36533.com, 1 e3lan.net, 1 e4.chat, 1 e42.org, 0 -e4a.org, 1 +e4a.org, 0 e4mc.link, 1 e4tools.com, 1 e4work.com.br, 1 @@ -46772,7 +46723,10 @@ eamproperties.com, 1 eamu.fun, 1 eandata.com, 0 eanraig.top, 1 +eaofcarrollton.com, 1 eaofdfw.com, 1 +eaoffrisco.com, 1 +eaofhouston.com, 1 eap.vg, 1 earchi.org, 1 earhealthsolutions.co.uk, 1 @@ -46804,7 +46758,6 @@ earth-syst-sci-data.net, 1 earth-system-dynamics.net, 1 earth-system-science-data.net, 1 earth.gov, 1 -earthai.ai, 1 earthbound.tk, 1 earthcam.com, 1 earthcam.net, 1 @@ -47101,7 +47054,6 @@ ebdaa-business.com, 1 eben18.net, 1 ebenda.org, 1 ebenica.sk, 1 -ebenisterie-de-villenouvelle.fr, 1 ebenvloedaanleggen.nl, 1 eberharter-steine.at, 1 eberls.com, 1 @@ -48020,7 +47972,6 @@ egbert.net, 1 egbertsen.tk, 1 egc.ink, 1 egdsk.ru, 1 -egegesh.ru, 1 egehem.se, 1 egenya.cl, 0 egeozcan.com, 0 @@ -48168,7 +48119,6 @@ ehub.sk, 1 ehuber.info, 1 ehubtt.org, 1 ei-bo.org, 1 -eiacampoguamal.com, 1 eiao.me, 1 eiber.net, 1 eibich.de, 1 @@ -48610,7 +48560,6 @@ electromotor.tk, 1 electronic-ignition-system.com, 1 electronicafacil.net, 1 electronicaripol.es, 1 -electronics.com.bd, 1 electronicshelpcare.net, 1 electronicssrit.tk, 1 electronictucuman.com, 1 @@ -48691,7 +48640,6 @@ elektromicrosystems.pl, 1 elektromobil-zubehoer.de, 1 elektromosev.cz, 1 elektromotor.tk, 1 -elektron-elektrotechnik.de, 1 elektronicka-servisni-knizka.cz, 1 elektronickakancelar.cz, 1 elektroniksigarasehri1.com, 1 @@ -50541,7 +50489,6 @@ equals.com.br, 1 equans-kaelte.de, 1 equate.net.au, 1 equatetechnologies.com.au, 0 -equeim.ru, 0 equestrianvaulting.co.uk, 1 equi.ac, 1 equiac.com, 1 @@ -50561,7 +50508,6 @@ equinoxe.de, 1 equinoxit.co.nz, 1 equinoxtraining.net, 1 equip.cz, 1 -equipamentosparapostos.com.br, 1 equipandoloja.net.br, 1 equipedefrance.tv, 0 equipedefrente.tk, 1 @@ -50932,7 +50878,7 @@ escmatrix.com, 1 escobarservice7000.com, 1 escobeira.pt, 1 escoben.tk, 1 -escolaaberje.com.br, 1 +escolaaberje.com.br, 0 escolaconquer.com.br, 1 escoladejudo.com.br, 1 escoladepilota.tk, 1 @@ -52082,7 +52028,6 @@ evlqa1sp1tzb05zo-reoo0vhj9a1t5pousfudnkg.com, 0 evo-ai.com, 1 evo-exchange.com, 1 evo.moe, 1 -evobox.store, 1 evoco.vc, 0 evodation.com, 1 evodation.org, 1 @@ -52100,6 +52045,7 @@ evolution-host.ga, 1 evolution-x.org, 0 evolution.codes, 1 evolutionbp.es, 1 +evolutionhomecorp.com, 1 evolutioninflatables.co.uk, 1 evolutionlife.za.com, 1 evolutionmining.com, 1 @@ -52152,7 +52098,6 @@ ewa-hayward.co.uk, 1 ewacforum.tk, 1 ewaf.club, 1 ewaipiotr.pl, 1 -ewananga.ac.nz, 1 ewanm89.co.uk, 1 ewanm89.com, 1 ewanm89.uk, 1 @@ -53541,7 +53486,6 @@ faqexchangeest.ga, 1 faqiteam.tk, 1 far-east.tk, 1 far3link.tk, 1 -fara-frunzis.ro, 1 fara.gov, 1 faradome.ws, 1 faradrive.ir, 1 @@ -53744,7 +53688,6 @@ fashionunited.uk, 1 fashionusa.gq, 1 fashionweb.ml, 1 fashionweek.tk, 1 -fashionweekweb.com, 1 fashionworlds.ml, 1 fashionxmas.gq, 1 fasigbrooks.com, 1 @@ -54380,6 +54323,7 @@ fenix-zone.tk, 1 fenixcorporative.tk, 1 fenixmetal.tk, 1 fenixonlinevirtual.com.br, 1 +fenn.moe, 1 fennec.wtf, 1 fennie.onthewifi.com, 0 fenns.co.za, 1 @@ -54944,7 +54888,6 @@ fillo.com.tr, 1 film-colleges.com, 1 film-op-tv.nl, 1 film-rezensionen.de, 1 -film-storyboards.com, 0 film-stream.cz, 1 film-tutorial.com, 1 film.cz, 1 @@ -55011,7 +54954,6 @@ filtr.me, 0 filtrmyfeed.com, 1 filwebasia.com, 1 fimallc.com, 1 -fimc-biodiversityportal.org, 1 fimfiction.net, 1 fimmcyte.com, 1 fimozin.ga, 1 @@ -55262,6 +55204,7 @@ finverse.com, 0 finwarriors.tk, 1 finwe.info, 1 finy.pl, 1 +finzy.com, 1 fionahengartner.ch, 1 fionamcbride.com, 1 fionna.io, 1 @@ -55524,7 +55467,7 @@ fiskalloy.com, 1 fiskaly.com, 1 fiskarsinlaituri.fi, 1 fisophi.com, 1 -fispan.com, 0 +fispan.com, 1 fistingtogether.com, 1 fistwerk.de, 1 fit-4u.ch, 0 @@ -55800,7 +55743,6 @@ flarewalker.net, 1 flarewalker.org, 1 flart.studio, 1 flart.tk, 1 -flarumplus.com, 1 flasaki.gr, 1 flash-games.tk, 1 flash-payments.com, 1 @@ -57065,7 +57007,6 @@ forourselves.com, 1 forowarhammer.tk, 1 forrest79.net, 1 forrestheller.com, 1 -forrestsolutions.com, 1 forrestwalkbarbershop.com.au, 1 forrettabarinn.is, 1 forro.berlin, 1 @@ -57270,7 +57211,6 @@ fotofast.tk, 1 fotoflits.net, 1 fotofofftein.de, 0 fotofon.tk, 1 -fotofreunde-telegram.eu, 1 fotograf-website.de, 1 fotografessa.pl, 1 fotografiadellalucerossa.com, 1 @@ -57306,6 +57246,7 @@ fotostudio-schweiz.ch, 1 fotostudio87.nl, 0 fotostudiobasic.tk, 1 fotosubmarina.tk, 1 +fotosyeventos.es, 1 fototaniej.pl, 1 fototjansterkalmar.se, 1 fototutorial.tk, 1 @@ -57558,7 +57499,6 @@ framboise314.fr, 1 framedog.tk, 1 framegame.ch, 1 framemo.org, 1 -framenails.fr, 1 framer.ai, 1 framer.com, 1 framer.live, 1 @@ -57649,7 +57589,6 @@ frank-america.com, 1 frank-gcc.com, 1 frank-lastia.tk, 1 frank-wendy.tk, 1 -frank.asia, 1 frank.fyi, 1 frankbrennan.art, 1 frankdilusso.com, 1 @@ -58249,7 +58188,6 @@ freundlieb.ch, 1 frexxi.com, 1 freyapearls.com.au, 1 fribourgviking.net, 1 -fricke.pl, 1 frickelboxx.de, 1 frickelmeister.de, 1 frickenate.com, 1 @@ -58519,7 +58457,6 @@ fruitlandmi.gov, 1 fruitlawers.ga, 1 fruitmoose.com, 1 fruitscale.com, 1 -fruitsexpressdelivery.com.sg, 1 fruitsfromchile.com, 1 fruittree.com.my, 1 fruittree.com.sg, 1 @@ -59007,7 +58944,6 @@ funnybee.tk, 1 funnybubu.ro, 1 funnychristianjokes.tk, 1 funnycommercials.ga, 1 -funnycups.org, 1 funnylinks.cf, 1 funnymedia.tk, 1 funnymetals.com, 1 @@ -59523,7 +59459,6 @@ gabrielsteens.nl, 1 gabrielvillarreal.com, 0 gabrielyin.com, 0 gabryjeluk.tk, 1 -gabskent.de, 1 gabtitui.gov.au, 0 gabucho.tk, 1 gabyclark.com, 1 @@ -60190,6 +60125,7 @@ gaspard-ulliel.tk, 1 gaspard12aout.ca, 1 gasparesganga.com, 1 gassafetycerts.com, 1 +gassafetymanchester.co.uk, 1 gasscc.id, 1 gasslerorourke.com, 0 gassouthkenticoqa.azurewebsites.net, 1 @@ -60351,7 +60287,6 @@ gbcindustry.com, 1 gbcomm.tk, 1 gbcsummercamps.com, 1 gbdavisplasticsurgery.com, 1 -gbecreativeagency.com, 1 gbedu360.com, 1 gbenson.net, 1 gbes.com, 1 @@ -60694,7 +60629,6 @@ gemonite.com, 1 gemooi.com, 1 gemour.pl, 1 gemquery.com, 1 -gemsen.com, 1 gemstones.com, 1 gemwerx.com, 1 gen.cn.eu.org, 1 @@ -60951,7 +60885,7 @@ geo-files.tk, 1 geo-industrie.fr, 1 geo-portale.it, 1 geo-television.de, 1 -geoapps.nl, 1 +geoapps.nl, 0 geoarchive.tk, 1 geocaching.hu, 1 geocar.com, 1 @@ -61213,6 +61147,7 @@ gestione-certificazioni.it, 1 gestionth.com, 1 gestorehotel.com, 1 gestorestecnologicos.com, 1 +gestoriaycontratos.com, 1 gestus.tk, 1 gesundheits-fakten.de, 1 gesundheits-tipps.ch, 1 @@ -61374,7 +61309,6 @@ getplus.com.au, 1 getpro.plumbing, 1 getprohealth.com, 1 getpromo.cf, 1 -getpsolid.com, 1 getpublii.com, 1 getraenke-hoffmann.de, 1 getready2dance.tk, 1 @@ -61667,6 +61601,7 @@ giardinaggio.napoli.it, 1 giardinaggio.roma.it, 1 giardiniblog.com, 1 giardiniblog.it, 1 +giardiniere.bologna.it, 1 giardiniere.milano.it, 1 giardiniere.roma.it, 1 giardinoperfetto.com, 1 @@ -62514,7 +62449,6 @@ gmail, 1 gmail.com, 0 gman.bot, 1 gmanlabs.com, 1 -gmao.com, 1 gmasil.de, 1 gmatic.info, 1 gmavsg.org, 1 @@ -62657,7 +62591,6 @@ goatbots.com, 1 goathub.io, 0 goatlord.tk, 1 goatstore.ca, 1 -goaudits.com, 1 gobabes.nl, 1 gobarrelroll.com, 1 gobebra.com, 1 @@ -63390,7 +63323,6 @@ gousto.co.uk, 1 goutsmits-tegelwerken.nl, 1 gouwdata.nl, 0 gov-online.go.jp, 1 -gov.cz, 1 gov.exposed, 1 gov.ky, 1 gov.md, 1 @@ -63707,7 +63639,6 @@ granitebaydermatologyandlaser.com, 1 graniteclub.com, 1 graniteescrow.com, 1 granitehillcapital.com, 1 -graniteind.com, 1 granitestateproductions.tk, 1 granitko.ru, 1 grannynude.tv, 1 @@ -63721,7 +63652,6 @@ grannytube.tv, 1 grannytubes.com, 1 granool.ga, 1 granotamaniacos.tk, 1 -granplaza.eu, 1 granpoder-islacristina.tk, 1 gransfors354.com, 1 granstor.com, 0 @@ -64214,7 +64144,6 @@ gridgames.tk, 1 gridgroup.com.tr, 1 gridsmartercities.com, 1 gridspace.ca, 1 -gridtennis.net, 1 gridvis.cloud, 1 griechische-pfoetchen.de, 1 griefheart.com, 1 @@ -64490,6 +64419,8 @@ grumpy.fr, 1 grumpygamers.com, 1 grumpyguy.ga, 1 grumpyvegan.com, 1 +grunammgames.com, 1 +grunammgames.de, 1 grundig.co.uk, 1 grundlage.fi, 1 grundrauschen-publishing.de, 1 @@ -64738,7 +64669,6 @@ gubernia37.ml, 1 gubka.ga, 1 guc.ci, 1 guccigame.de, 1 -gudangpangan.id, 1 gudao.com, 1 gudbrand.no, 1 guddaff.de, 1 @@ -65706,7 +65636,6 @@ hallmarkbusiness.com, 1 halloffameapartments.com, 1 hallofoddities.tk, 1 hallofworlds.online, 1 -hallopstyling.com, 1 halloway.tk, 1 halloweenmusic.org, 1 hallsonsservices.com, 1 @@ -67581,6 +67510,7 @@ hellocoding.de, 1 hellodesign.tk, 1 helloemailers.ga, 1 helloemailest.ga, 1 +helloexit.com, 1 helloheadsup.com, 1 hellohealthgroup.com, 1 helloindia.tk, 1 @@ -67613,7 +67543,6 @@ helloteen.tk, 1 hellothought.net, 1 hellov.in, 1 helloverify.com, 1 -hellovillam.com, 1 helloworldhost.com, 0 helloyubo.com, 1 hellpc.net, 0 @@ -68488,7 +68417,6 @@ hindustantimes.ml, 1 hinduworld.tk, 1 hindweb.tk, 1 hinesitecoding.com, 1 -hingehealth.io, 1 hingle.me, 1 hinhanhykhoa.com, 1 hinit.no, 1 @@ -68519,7 +68447,6 @@ hiphopall.tk, 1 hiphopbolivia.tk, 1 hiphopdates.de, 1 hiphopfashion.tk, 1 -hipihapi.org, 1 hipnos.net, 1 hipnosecriciuma.com.br, 1 hipnosisyterapias.com, 1 @@ -69335,7 +69262,6 @@ homedollars.ga, 1 homeduck.ga, 1 homeeagle.ga, 1 homeelectricalupdates.com, 1 -homeer.com, 1 homeexperience.tk, 1 homefacialpro.com, 0 homeflag.ga, 1 @@ -69552,6 +69478,7 @@ honestworknmoney.tk, 1 honesty.com.pl, 1 honey.beer, 1 honeyarcus.art, 1 +honeybadger.io, 0 honeycomb.io, 1 honeycome-recruit.com, 0 honeycreeper.com, 1 @@ -69978,7 +69905,6 @@ hotelbiz.cf, 1 hotelbiz.ga, 1 hotelbiz.gq, 1 hotelbretagne.dk, 1 -hotelcasadelmar.com, 1 hotelcharlestonsantateresa.com, 1 hotelcoco.co.uk, 1 hotelconsulado.com.br, 1 @@ -70129,7 +70055,6 @@ hoursofoperation.biz, 1 house-sparrow.com, 1 houseandgarden.co.uk, 1 houseandhounddogwalkers.com, 1 -houseandofficeclearance.co.uk, 1 houseareacanada.tk, 1 houseareaitaly.tk, 1 housebar.tk, 1 @@ -70250,7 +70175,6 @@ howellpolicenj.gov, 1 howgoodwasmysex.com, 1 howiehawkins.us, 1 howieisawesome.com, 1 -howl.uk.com, 1 howlers.tk, 1 howlinhawk.tk, 1 howlonghaswilliambeenwaitingforhislicence.uk, 1 @@ -70310,7 +70234,7 @@ hozio.com, 1 hozio.net, 1 hozkomurcu.com, 1 hozonshoku.com, 1 -hozy.co, 1 +hozy.co, 0 hp-67.com, 1 hp-lexicon.org, 1 hp42.de, 1 @@ -70671,7 +70595,6 @@ hudhomestore.gov, 1 hudless.com, 1 hudnara.se, 1 hudobniny.net, 1 -hudochharmoni.se, 1 hudognik.com, 1 hudsonregional.gov, 1 hudsonsolutions.com, 1 @@ -70693,7 +70616,6 @@ huertoydesamparados.tk, 1 hues-in-lee.de, 1 huesers.de, 1 huesitos.tk, 1 -huesos.pro, 1 huez.tech, 1 hufelare.tk, 1 huffduffer.com, 1 @@ -70775,7 +70697,7 @@ hultrid.hopto.org, 1 huluch.cc, 1 hululkitab.co, 1 humail.link, 1 -human-centricity.com, 1 +human-centricity.com, 0 human-clone.com, 1 human-dev.io, 1 human-parasites.tk, 1 @@ -70871,7 +70793,6 @@ humorcheck.ga, 1 humorcheckers.ga, 1 humorcheckest.ga, 1 humored.gq, 1 -humorojo.com, 1 humpen.se, 1 humphreyscountytn.gov, 1 humphreystnsheriff.gov, 1 @@ -70879,7 +70800,6 @@ humpydumpy.tk, 1 humuluslupulus.de, 1 humus.tk, 1 huna.viajes, 1 -hunaniinfotech.com, 1 hund.io, 1 hund.ml, 1 hundamosantena3.tk, 1 @@ -71165,6 +71085,7 @@ hypemgmt.com, 1 hyper-fit.nl, 1 hyper-matrix.org, 1 hyper-text.org, 0 +hyper.ai, 1 hyper.lol, 1 hyperalgesia.com, 1 hyperautomotive.com.au, 1 @@ -71537,7 +71458,6 @@ ibo-chemnitz.de, 1 iboat.eu, 1 ibodyiq.com, 1 ibon.org, 1 -iboplayer.de, 1 iboysoft.com, 1 ibpegasus.tk, 1 ibpsrecruitment.co.in, 1 @@ -71762,7 +71682,7 @@ icts.gov, 1 icttindia.org, 0 ictv1.com, 1 ictwebsolution.nl, 1 -icuc.social, 1 +icuc.social, 0 iculture.nl, 1 icustomboxes.com, 1 icyapril.com, 1 @@ -71771,7 +71691,6 @@ icydestiny.com, 0 icyhealth.com, 0 icyheroes.com, 1 icyrock.com, 1 -icytales.com, 0 icywhiz.com, 1 iczc.cz, 1 icze4r.co, 1 @@ -72126,6 +72045,7 @@ igglabs.com, 1 iggprivate.com, 1 iggsoft.com, 1 iggsoftware.com, 1 +iggyz.com, 1 igi-2.com, 1 igiftcards.de, 1 igiftcards.nl, 1 @@ -72202,7 +72122,6 @@ igrovoi-klub.tk, 1 igrun.com, 1 igrun80.com, 1 igrun88.com, 1 -igrunplay.com, 1 igry-onlayn.ru, 1 igryalawar.tk, 1 igshop.ir, 1 @@ -73060,7 +72979,6 @@ imtqy.com, 1 imttech.co, 1 imunify360.com, 1 imusionforum.tk, 1 -imwc.me, 0 imy.rs, 0 imyjy.cn, 1 imzhu.com, 1 @@ -73074,6 +72992,7 @@ in-crypto.tk, 1 in-depth.wiki, 1 in-depthoutdoors.com, 1 in-flame-team.com, 1 +in-flames.com, 1 in-line.ru, 1 in-love.tk, 1 in-melbet.com, 1 @@ -73350,7 +73269,6 @@ indnews.ga, 1 indo-wiki.fr, 1 indo4life.tk, 1 indobia-massage-thun.ch, 1 -indobo.com, 0 indobrains.id, 1 indochinatravel.tk, 1 indodax.com, 1 @@ -73798,7 +73716,6 @@ inglesnarede.com.br, 1 inglessantacruz.tk, 1 inglestotal.tk, 1 ingo-schlueter.de, 1 -ingoldingen.de, 1 ingolfsskali.is, 1 ingoschlueter.de, 1 ingrain-ed.com, 1 @@ -76692,7 +76609,6 @@ jainnatory.ca, 1 jainnotary.ca, 1 jaion.tech, 1 jairoenfrancien.tk, 1 -jairsinho.me, 1 jaisiam.co.th, 1 jaispirit.com, 1 jaiswalfertility.com, 1 @@ -77051,6 +76967,7 @@ jasch.tk, 1 jascha.wtf, 1 jaschaa.de, 1 jasco.gov, 1 +jasekeke.co.kr, 1 jaseng.ga, 1 jasewhite.com, 1 jashinchan.cn, 1 @@ -77637,7 +77554,6 @@ jerusalempersonalsest.ga, 1 jerusalemplus.tv, 1 jesec.cn, 1 jesec.io, 1 -jesen.org, 1 jesiensredniowiecza.pl, 1 jesmatboutique.com, 1 jesmh.de, 1 @@ -78394,7 +78310,7 @@ johannes-sprink.de, 0 johannes-zinke.de, 1 johannes.wtf, 1 johannfritsche.de, 1 -johannhson.com, 1 +johannhson.com, 0 johanpeeters.com, 1 johego.org, 1 johjohannsonkaffe.no, 1 @@ -79012,7 +78928,6 @@ jsfleecefabric.com, 1 jsfloydlaw.com, 0 jsg.hk, 1 jsgr.ca, 1 -jsh.marketing, 1 jsheard.co.uk, 1 jsheard.com, 1 jsheard.me.uk, 1 @@ -79509,7 +79424,6 @@ justnu.se, 0 justor.ru, 1 justpass.co.uk, 1 justpass.uk, 1 -justpaste.it, 1 justpdf.cf, 1 justquoteme.ga, 1 justsa.co.za, 1 @@ -79689,7 +79603,6 @@ k88259.com, 1 k886.co, 1 k88670.com, 0 k88672.com, 0 -k8dalao.com, 1 k8didi.com, 1 k8gege.com, 0 k8ja.co.ke, 1 @@ -79899,7 +79812,7 @@ kak-pohudet-legko.ml, 1 kak.ao, 1 kaka.farm, 0 kakabo.vn, 1 -kakacon.nz, 1 +kakacon.nz, 0 kakan.ml, 1 kakaravaara.fi, 1 kakdolgonline.cf, 1 @@ -80328,7 +80241,6 @@ karikatur.ml, 1 karikatur.tk, 1 karimova.tk, 1 karimsaadati.tk, 1 -karimunsejahtera.com, 1 karin-ewald.de, 1 karinahh.net, 1 karinheinenmaassen.nl, 1 @@ -80935,7 +80847,6 @@ kelamb.com, 1 kelantan.tk, 1 kelantanmudah.com, 0 kelax.tk, 1 -kelbillet.com, 1 kelcible.fr, 1 kelcotool.com, 1 kelder.tk, 1 @@ -81165,7 +81076,6 @@ kerrybluephotography.com, 1 kerrydavisguitars.tk, 1 kerrygoldusa.com, 1 kersmexico.com, 1 -kerstboomkantenklaar.nl, 1 kerstpagina.tk, 1 kersvers.agency, 1 kertis.tk, 1 @@ -82044,7 +81954,6 @@ kiseimarriage.com, 1 kisel.org, 1 kish-takhfif.com, 1 kishcar.co, 1 -kishonti.net, 1 kisiselveri.com, 1 kiskeedeesailing.com, 0 kislepesek.net, 1 @@ -82422,7 +82331,6 @@ klokkenluidersvg.nl, 1 kloop.kg, 1 klop.info, 1 klos-kremperler.at, 1 -klose-besser.com, 1 klose.family, 1 klosetestumgebungnextcloud.de, 1 klosko.net, 1 @@ -82678,6 +82586,7 @@ kochamkoszalin.pl, 1 kochbar.de, 1 kochcommunity.com, 1 kochdigital.dk, 1 +kochen-und-design.de, 1 kochereva.com, 1 kocheshkov.cf, 1 kochhar.net, 1 @@ -82786,7 +82695,6 @@ kokteili.tk, 1 koktelparty.tk, 1 kokumoto.com, 1 kokuyo.co.th, 1 -kokuyocamlin.com, 1 kokwatersport.nl, 1 kol7asry.com, 1 kolabtree.com, 1 @@ -83479,7 +83387,7 @@ kreno.tech, 1 krenstetter.at, 1 kreolis.net, 1 kresimir-blazevic.tk, 1 -krestanskydarek.cz, 1 +krestanskydarek.cz, 0 kretaforum.dk, 1 kretschmann.consulting, 1 kretschmann.it, 1 @@ -83978,7 +83886,6 @@ kundesjekk.no, 0 kundo.pl, 1 kundo.se, 1 kunfu.pl, 1 -kungfudirect.com, 1 kungsangen.com, 0 kungsangsgymnasiet.se, 1 kunitomo.jp, 1 @@ -84095,7 +84002,6 @@ kurtschlatzer.com, 1 kurtschleinbeck.com, 1 kurungkurawal.id, 1 kuruppa.xyz, 1 -kurvysf.com, 1 kurvytt.com, 1 kurz.onl, 1 kurz.pw, 1 @@ -84130,6 +84036,7 @@ kutedaki.com, 1 kutekeiki.com, 1 kuti.hu, 1 kutinsoft.com, 1 +kutny.cz, 1 kutsankaplan.com, 1 kuttler.eu, 1 kutvonen.net, 1 @@ -84858,7 +84765,6 @@ lalaya.fr, 1 laled.ch, 0 laleggepertutti.it, 1 lalegria.tk, 1 -lalegroup.com.tr, 1 lalelal.me, 1 lalenteja.net, 1 lalettrepatriote.com, 0 @@ -84868,7 +84774,7 @@ lalokura.tk, 1 lalouviere.fr, 1 lalucepulsata.it, 1 lalucha.tk, 1 -lalucioledigitale.com, 1 +lalucioledigitale.com, 0 lalumiere.org, 1 lalunedangkor.com, 0 lamac.ae, 1 @@ -85708,6 +85614,7 @@ lawstudio.legal, 1 lawsuit.tk, 1 lawsuitconsultanters.ga, 1 lawsuitconsultantest.ga, 1 +lawtests.ir, 1 lawtimesnews.com, 1 lawtrend.in, 1 lawvize.com, 1 @@ -86135,7 +86042,6 @@ lebenpflegen-march.ch, 1 lebenpflegen.ch, 1 lebens-fluss.at, 1 lebensinselparaguay.tk, 1 -lebensmittelwarnung.de, 0 lebesis.tk, 1 lebihan.pl, 1 lebkuchen-schmidt.com, 1 @@ -86803,7 +86709,6 @@ lertsiritravel.net, 1 les-aides.fr, 1 les-ateliers-de-melineo.be, 0 les-epris.fr, 1 -les-inoxydables.com, 1 les-plus.com, 1 les-pros-du-drone.com, 1 les-universalistes.be, 1 @@ -86893,7 +86798,6 @@ lestoilesdemich.fr, 1 lestravauxduparticulier.fr, 1 lestrokeofgenius.com, 1 lestudiopecot.fr, 1 -lesummeira.is, 1 lesvertsmaroc.tk, 1 lesy.eu, 1 lesycr.cz, 1 @@ -87157,7 +87061,6 @@ lezen.tk, 1 leziblog.com, 1 lezzetyurdu.com.tr, 1 lfashion.eu, 1 -lfasiallc.com, 1 lfc.com.pl, 1 lfcnsv.de, 1 lff.club, 1 @@ -87376,7 +87279,6 @@ libraryofcode.us, 1 librarytapes.tk, 1 librarytools.com, 1 libravatar.org, 1 -librazy.org, 1 libre-innovation.org, 1 libre.cr, 1 libre.net.au, 1 @@ -87947,7 +87849,6 @@ lineengraver.com, 1 linefire.com, 1 lineinchina-enterprise.tw, 1 lineinchina.com.ua, 1 -linejuby.dk, 1 linelab.cz, 1 lineru.com, 1 lineshop.ml, 1 @@ -87955,7 +87856,6 @@ linestep.jp, 1 lineto.com, 1 linext.cn, 0 linfamilygc.com, 1 -linge-ma.ro, 1 lingerie-coquine.fr, 1 lingerie.com.br, 1 lingeriebym.nl, 1 @@ -89215,7 +89115,6 @@ lojavirtualdopsicopedagogo.com.br, 1 lojavirtualinfopaper.com.br, 1 lojavisamed.com.br, 1 lojaxo.com.br, 1 -lojazeustech.com.br, 1 lojel.com, 1 lojistaguarani.com.br, 1 lojix.com, 0 @@ -90188,6 +90087,7 @@ ludwig.im, 1 ludwiggrill.de, 1 ludwigjohnson.se, 1 ludwigpro.net, 1 +ludwigsburger-brauhaus.de, 1 luedeke-bremen.eu, 1 lueersen.homedns.org, 1 luehne.de, 1 @@ -90276,9 +90176,6 @@ lukem.eu, 1 lukem.net, 1 lukeplant.me.uk, 1 luker.org, 1 -lukersallamericanstorage.com, 1 -lukersstorage.com, 1 -lukerstorage.com, 1 lukertech.net, 1 lukesbouncycastlehire.com, 1 lukestert.com, 0 @@ -90374,7 +90271,6 @@ lunarlog.com, 1 lunarr.fr, 1 lunartail.nl, 1 lunarum.tk, 1 -lunascope.ru, 1 lunasqu.ee, 1 lunastrail.com, 1 luncfreak.com, 1 @@ -90672,7 +90568,6 @@ lynntownshipmi.gov, 1 lynnvartan.com, 1 lynred.com, 1 lynt.nl, 1 -lynth.io, 1 lynwoodca.gov, 1 lynx-webservice.com, 0 lynx.com.au, 1 @@ -91004,7 +90899,6 @@ maddin.ga, 1 made-to-usb.com, 1 made.md, 1 made2coach.com, 1 -madebydami.com, 1 madebyesmel.com, 1 madebyhand.art, 1 madebyshore.com, 0 @@ -91580,6 +91474,7 @@ maison-auriat.fr, 1 maison-coutin.com, 1 maison-du-mineur.com, 1 maison-eureka.fr, 1 +maison2com-event.fr, 1 maisondelafigue.com, 1 maisondepax.com, 1 maisondidon.com, 1 @@ -91746,7 +91641,6 @@ malagarental.com, 1 malahov.tk, 1 malakye.com, 1 malami.gr, 1 -malamujercomunicacion.com, 1 malamutedoalasca.com.br, 1 malardalenvvs.se, 1 malariaadvice.gq, 1 @@ -92353,6 +92247,7 @@ marbellaclub.com, 1 marbellaoptic.ro, 1 marbermedical.com, 1 marble.com, 1 +marblecapitallp.com, 1 marblecare.ae, 1 marbleceramiccorp.com.au, 1 marbleme.jp, 1 @@ -92493,6 +92388,7 @@ margo-co.ch, 0 margo.ml, 1 margolcia.com.pl, 1 margolis.gq, 1 +margolisphoto.com, 1 margotbworldnews.tk, 1 margotdesign.ovh, 1 margots.biz, 1 @@ -92786,7 +92682,7 @@ markus289.com, 1 markusabraham.com, 1 markusehrlicher.de, 1 markusgran.de, 1 -markushof.it, 1 +markushof.it, 0 markusjanzen.de, 1 markuskeppeler.de, 1 markuskeppeler.no-ip.biz, 1 @@ -93043,7 +92939,6 @@ marvnetforum.gq, 1 marvnetforum.ml, 1 marvnetforum.tk, 1 marvos.se, 1 -marw.org, 1 marwadionline.com, 1 marxist-club.org, 1 marxist.party, 1 @@ -93051,6 +92946,7 @@ marxists.org, 1 marxmyths.org, 1 marxpark.tk, 1 mary-e-kay.tk, 1 +maryamghorbani.com, 1 maryannhaircpa.com, 1 marycowanceramics.com, 1 maryeileen90.party, 1 @@ -93956,7 +93852,6 @@ mbinf.de, 0 mbinformatik.de, 0 mbjeng.com, 0 mbk.net.pl, 1 -mblankhorst.nl, 1 mble.mg, 1 mbm-law.net, 1 mbmassageterapi.se, 1 @@ -94002,7 +93897,7 @@ mcaps-mn.gov, 1 mcar5b.com, 1 mcavallo.com, 1 mcaz.nl, 1 -mcb-bank.com, 0 +mcb-bank.com, 1 mcba.com.br, 1 mcc.edu.ph, 1 mccabes.com.au, 1 @@ -94675,6 +94570,7 @@ medicompany.tk, 1 mediconnect.net, 1 medictools.de, 1 medicus.ai, 1 +medidata.co.nz, 1 medidordehumedad.online, 1 mediengestalter-website.de, 1 medienhelden.de, 1 @@ -94844,7 +94740,6 @@ meezjewelry.vn, 1 meeztech.com, 1 meeztertom.nl, 1 mefano.com, 1 -meffrepatrimoine.com, 1 meg-a-bounce.co.uk, 1 mega-aukcion.ru, 1 mega-byte.nl, 0 @@ -95242,6 +95137,7 @@ meme.com, 1 meme.fi, 1 meme.institute, 1 memecoin.org, 1 +memecoincook.com, 1 mememan.org, 1 memememememememe.me, 1 memento-mori.cf, 1 @@ -95517,7 +95413,6 @@ mercercountypa.gov, 1 mercerisland.gov, 1 merchant-portal.co.kr, 1 merchantaccountsolutions.com, 1 -merchcity.com, 1 merchstudio.ru, 1 merck-animal-health.ca, 1 merck-animal-health.com, 1 @@ -95686,7 +95581,7 @@ meta.ai, 1 meta.com, 1 meta4.be, 1 meta48.live, 1 -metaalshopper.nl, 1 +metaalshopper.nl, 0 metablog.xyz, 1 metabolicmenu.com, 1 metabound.co.uk, 1 @@ -95738,7 +95633,7 @@ metallokonstruktsii.com, 1 metallomania.it, 1 metallosajding.ru, 1 metallrecycling.org, 1 -metallshopper.de, 1 +metallshopper.de, 0 metalmaniac.tk, 1 metalmonocle.com, 0 metalnight.tk, 1 @@ -96452,7 +96347,6 @@ midrocket.com, 1 midspace.co.uk, 1 midspss.lt, 1 midterm.us, 1 -midtownattowncenter.com, 1 midtowndentist5thave.com, 1 midtownsouthcc.org, 1 midvalleysewer.gov, 1 @@ -96992,7 +96886,6 @@ mind-media.com, 1 mind-moves.es, 1 mindandfull.ga, 1 mindask.tk, 1 -mindblown.pl, 1 mindbodybalance.health, 1 mindbodycoaching.no, 1 mindbodysoul.us, 1 @@ -97052,7 +96945,6 @@ mine-craftlife.com, 1 mine-pixl.de, 1 mine-temoin.fr, 1 mine260309.me, 0 -mineads.gg, 1 minebbs.com, 0 minebitcoin.tk, 1 minecity.fun, 1 @@ -97204,7 +97096,6 @@ ministeriodeeducacion.gob.do, 1 ministory.tk, 1 ministryhire.com, 1 ministryofinternet.eu, 1 -ministryofsound.com, 1 ministryofvillas.com, 1 ministudio.ml, 1 ministyle.ro, 1 @@ -97741,6 +97632,7 @@ mjhs.org, 1 mjhsfoundation.org, 1 mjjshow.eu.org, 1 mjlwebdev.pl, 1 +mjmoccasions.com, 1 mjmpartnerlink.com.au, 1 mjniessen.com, 1 mjoldfield.com, 1 @@ -97971,7 +97863,6 @@ mne.moe, 1 mnec.io, 1 mneeb.de, 1 mnemonic.ninja, 1 -mnews.it, 1 mngfam.ddns.net, 1 mnguyen.io, 1 mnhc.gov, 1 @@ -98276,7 +98167,6 @@ modusawperandi.com, 1 modusgames.com, 1 modusit.co.za, 1 modusmundi.com, 1 -modussystems.com, 1 modvigilonlinerx.com, 1 modxvm.com, 1 modzcenter.com, 1 @@ -99217,7 +99107,6 @@ mostlyharmless.at, 1 mostlyoverhead.com, 1 mostmost.tk, 1 mosurist.tk, 1 -moswand.nl, 1 motability.co.uk, 1 motabilitychargeshare.co.uk, 1 motabilitydealer.co.uk, 1 @@ -99336,6 +99225,7 @@ motorzone.od.ua, 1 motoselfservices.fr, 1 motospaya.com, 0 motostorie.blog, 1 +motostyle.ua, 1 mototax.ch, 0 mototeam.tk, 1 mototsi.com, 1 @@ -99539,7 +99429,6 @@ mozgb.ru, 1 mozgovoy.tk, 1 mozilla-russia.org, 1 mozilla.cz, 1 -mozo.com.au, 1 moztopup.com, 1 mozzak.tk, 1 mozzez.de, 1 @@ -99798,7 +99687,6 @@ msar.eu, 1 msbmb.com, 1 msc-fivem.shop, 1 mscc.mu, 1 -msd-animal-health.com, 1 msd-tiergesundheit.de, 1 msda23.gov, 1 msdnicrosoft.cn, 0 @@ -99947,7 +99835,7 @@ mtehe-square.com, 1 mtel.gr, 1 mteleport.net, 1 mtelizabeth.com, 1 -mtex.net.ua, 1 +mtex.net.ua, 0 mtf.party, 1 mtf.rip, 1 mtf.wiki, 1 @@ -100507,7 +100395,6 @@ musselsblog.com, 1 mussila.com, 1 mussonsppe.com, 1 mussonstrading.com, 1 -musta.ch, 1 mustangapparel.ca, 1 mustangmessenger.net, 1 mustasj.no, 1 @@ -100787,7 +100674,6 @@ myanmar.gov.mm, 1 myapexcard.com, 1 myaquaterra.tk, 1 myareaf2a.com, 1 -myarpico.com, 1 myarticlelibrary.cf, 1 myasb.club, 1 myathena.ai, 0 @@ -100933,7 +100819,6 @@ mycrypnet.io, 1 mycts.org, 1 mycues.gov, 1 mycumortgage.com, 1 -mycyberspace.tech, 1 mydarkness.cf, 1 mydarksite.tk, 1 mydarkstar.net, 1 @@ -101515,7 +101400,6 @@ mysimsem.com, 0 mysisterandi.co.za, 1 myslc.gov, 1 mysmallbusinesssidekick.com, 1 -mysmartserve.com, 0 mysmsapp.cn, 1 mysociety.ml, 1 mysockfactory.ch, 1 @@ -101634,7 +101518,6 @@ myvau.lt, 1 myvawic.org, 0 myvc.in, 1 myvcc.ru, 1 -myvegasadvisor.com, 1 myveritext.ca, 1 myveritext.net, 1 myvermont.gov, 1 @@ -102040,7 +101923,7 @@ namastehomecooking.com, 1 namastenaturo.fr, 1 namazon.org, 1 namazvakitleri.com.tr, 0 -namdu.uz, 1 +namdu.uz, 0 name.am, 1 name.ax, 1 nameabusiness.com, 1 @@ -102581,6 +102464,7 @@ nauris.fi, 1 nauseainthemorning.ml, 1 nausempreendimentos.com, 1 nausicaahotel.it, 1 +nauss.edu.sa, 1 naut.ca, 1 nautadutilh.com, 1 nautholl.is, 1 @@ -102643,7 +102527,6 @@ nawroth.info, 1 nawt.pl, 1 nax.io, 0 naxcivan.tk, 1 -naxe.lv, 1 nayakaaerial.com, 1 nayanaas.com, 1 nayapakistan.tk, 1 @@ -102711,7 +102594,6 @@ nbalive.cn, 1 nbalivecn.com, 1 nbalivex.com, 1 nbaot.org, 1 -nbapc.org, 1 nbapwned.com, 1 nbari.com, 1 nbasky.com, 1 @@ -102982,6 +102864,7 @@ neelen-gym.nl, 1 neemo.nz, 1 neenahwi.gov, 1 neenan.com, 1 +neepscambaiting.com, 1 neera.ro, 1 neero.fr, 1 nees.ga, 1 @@ -103125,7 +103008,6 @@ nelnetbank.com, 1 nelosculpteur.fr, 1 nelson-marine.com, 0 nelsoncountyky.gov, 1 -nelsonrecruitmentservices.co.uk, 1 nelsonrodrigues.tk, 1 nelsontwpoh.gov, 1 nelsonworldwide.com, 1 @@ -103342,7 +103224,6 @@ nerven.se, 0 nervi.ga, 1 nesabamedia.com, 1 nesaltin.com, 1 -neseari.com, 1 nesheims.com, 1 nesheimswaterrestoration.com, 1 neshkorowi.gov, 1 @@ -104317,7 +104198,6 @@ nfl.duckdns.org, 1 nfl.zapto.org, 1 nflbox.me, 1 nflchan.org, 1 -nflo.pl, 1 nfls.io, 1 nfmhof.com, 1 nfo.co.il, 1 @@ -104357,6 +104237,7 @@ ngergs.de, 1 nggukbo9lbfadcf5.gq, 1 nghe.net, 1 nghien4banh.com, 1 +nghiengacha.com, 1 ngiemboon.net, 1 nginx.io, 1 nginxtest.ml, 1 @@ -105272,15 +105153,15 @@ noaccess.tk, 1 noacore.ch, 1 noadi-pixels.tk, 1 noagendahr.org, 1 +noahdigital.com.br, 1 noahenco.nl, 1 noahjacobson.com, 1 noahmodas.com.br, 1 noahs.com, 1 noahsaso.com, 1 -noamweb.com, 1 noangel.tk, 1 noart.tk, 1 -noatec.eu, 1 +noatec.eu, 0 noawildschut.com, 1 noawildschut.nl, 1 nob.ro, 1 @@ -105710,6 +105591,7 @@ northcountykiaparts.com, 1 northcreekresort.com, 1 northcreekresortblue.ca, 1 northdakotatitlesearch.com, 1 +northdallasendo.com, 1 northdavisfireut.gov, 1 northdevonbouncycastles.co.uk, 1 northeastconference.org, 1 @@ -106051,7 +105933,6 @@ novel543.com, 1 novelacuba.com, 1 novelas.net.br, 1 noveldadigital.es, 1 -novelenergylighting.com, 1 novelfm.com, 1 novelinglife.net, 0 novelly.in, 1 @@ -106993,7 +106874,6 @@ obelis.ltd, 1 obelisco.tk, 1 obelix05.duckdns.org, 1 oberam.de, 1 -obercodosguardas.pt, 1 oberdachstetten.de, 1 obereg.cf, 1 obereg.ga, 1 @@ -108203,7 +108083,6 @@ omskweb.tk, 1 omstars.com, 1 omtcloud.jp, 1 omundojornalismo.pt, 1 -omva.de, 1 omxz8.com, 1 omxz80.com, 1 omxz9.com, 1 @@ -109095,6 +108974,7 @@ oplata-mvd.ga, 1 oplata-vklike.tk, 1 oplata.uz, 1 oplium.com, 1 +oplop.appspot.com, 1 opm.gov, 1 opmaakonderscheidingen.nl, 1 opnx.dk, 1 @@ -109263,7 +109143,6 @@ opture.ch, 1 optykgill.pl, 1 optymyze.com, 1 opulentdivision.com, 0 -opulentranch.com, 1 opus-codium.fr, 1 opus-labs.fr, 1 opus4.com, 1 @@ -109301,6 +109180,7 @@ orange.bf, 1 orange.legal, 1 orange.md, 1 orangeacademy.cz, 1 +orangeappalam.com, 1 orangecityfl.gov, 1 orangecorners.com, 1 orangefinanse.com.pl, 1 @@ -109369,6 +109249,7 @@ orcamarine.tk, 1 orcas.tk, 1 orcasecurity.io, 1 orchardnh.org, 1 +orchestra-ppm.io, 1 orchestra.tk, 1 orchestremetropolitain.com, 1 orchidee-mariage.com, 1 @@ -109973,7 +109854,6 @@ oudersvannu.nl, 1 ouestacro.fr, 1 ouestlebug.com, 1 ouezzan.tk, 1 -ouglor.com, 1 oui-mais-toscope.info, 1 ouin.land, 1 ouiouibunny.ch, 1 @@ -110629,7 +110509,6 @@ packservice.it, 1 packsquare.in, 1 packtracking.org, 1 packwire.com, 1 -paclease.com.my, 1 pacman.ltd, 1 pacnyc.org, 1 pacobarbera.tk, 1 @@ -110804,7 +110683,6 @@ paintstaintt.com, 1 paio2-rec.com, 1 paipuman.jp, 1 paireepinart.com, 1 -pairin.com, 1 pairsclassifiedads.tk, 1 paisa-dev.azurewebsites.net, 1 paisano-online.com, 1 @@ -111275,7 +111153,7 @@ paradisu.fr, 1 paradoxdesigns.org, 1 paradoxhotels.com, 1 paradoxium.ml, 1 -paradymecompanies.com, 1 +paradymecompanies.com, 0 paraelganzo.tk, 1 parafarmacia.it, 1 paragardmdlportal.com, 1 @@ -111495,7 +111373,7 @@ parkr.io, 0 parkshark.io, 1 parksi.top, 1 parksinta.com, 1 -parkstonegp.com, 1 +parkstonegp.com, 0 parkstreet.com, 1 parktownpatrols.co.za, 1 parkujkladne.cz, 1 @@ -111513,6 +111391,7 @@ parleu2017.ee, 1 parleur.net, 1 parliament.gov.to, 1 parliamentcamp.com, 1 +parmacityfutsal.it, 1 parmartecultura.it, 1 parmatoday.it, 1 parmatwp.gov, 1 @@ -112682,7 +112561,6 @@ pelican.ie, 1 pelicanbaytx.gov, 1 pelicanconveyancing.co.uk, 1 pelicanottertailmn.gov, 1 -pelicanparty.games, 1 pelicans.tk, 1 peliculaonline.tk, 1 peliculaslatino.tk, 1 @@ -113143,7 +113021,6 @@ persian-clan.tk, 1 persianasmodernasluxury.com, 1 persiandating.tk, 1 persianmassagetherapy.com, 1 -persianpool.ir, 1 persianrom.com, 1 persiart.shop, 1 persiennexperten.se, 1 @@ -113303,6 +113180,7 @@ petdish.ga, 1 petdollar.ga, 1 petech.ro, 1 petelew.is, 1 +petemerges.com, 1 petemerges.xyz, 1 peter-r.co.uk, 1 peter-rader.pro, 1 @@ -113660,7 +113538,6 @@ pharmasyncers.ga, 1 pharmasyncest.ga, 1 pharmatender.cl, 1 pharmgkb.org, 1 -pharmica.uk, 1 pharosconsulting.com, 1 pharosiq.com, 1 pharside.dyndns.org, 1 @@ -113922,7 +113799,6 @@ photographerforwedding.tk, 1 photographersdaydream.com, 1 photography-workshops.net, 1 photographyandvideo.tk, 1 -photographybyctware.com, 1 photographybyjamiebrown.com, 1 photographyforchange.org, 1 photographyontherun.com, 1 @@ -114194,7 +114070,6 @@ picrew.me, 1 picsalesers.ga, 1 picsastock.com, 1 picshare.nz, 1 -picsnmore.de, 0 picstar.tk, 1 picsto.re, 0 pictominoes.com, 1 @@ -114594,7 +114469,6 @@ piratejeeptours.com, 1 piraten-basel.ch, 1 piraten-kleinbasel.ch, 1 piraten-recording.tk, 1 -pirateparty.org.uk, 1 piratepay.io, 0 pirates-comic.com, 1 piratesbrewcoffee.net, 1 @@ -114734,7 +114608,6 @@ pixelion.tk, 1 pixelissues.com, 1 pixelkraft.net, 1 pixelmania.se, 1 -pixelmateexhibitionltd.hk, 1 pixelmateexpo.com, 1 pixelmattic.com, 1 pixelmedianetwork.com, 1 @@ -114985,6 +114858,8 @@ planisanin.tk, 1 planiserin.tk, 1 planisware.academy, 1 planisware.cn, 1 +planisware.io, 1 +planisware.live, 1 planisys.net, 1 planit-inc.com, 1 planitz.com, 1 @@ -115240,7 +115115,6 @@ please-uwu.me, 1 pleasesavemypc.com, 1 pleaseuseansnisupportedbrowser.ml, 1 pleasure-science.com, 1 -pleasure.box, 1 pleasured.tk, 1 pleasureplanetbrothel.com.au, 1 pleb.cc, 1 @@ -116692,6 +116566,7 @@ poway.gov, 1 powayiliad.com, 1 powch-dev.com, 1 powch-dev2.com, 1 +powdercoatatl.com, 1 powdercoatingatl.com, 1 powderedcloud.cf, 1 powderedcloud.ga, 1 @@ -117465,7 +117340,6 @@ primalsurvivor.net, 1 primananda.com, 1 primanota.ch, 0 primapak.bg, 1 -primariachisineucris.ro, 1 primarium.info, 1 primary.health, 1 primarycareconnect.com.au, 1 @@ -117478,7 +117352,6 @@ prime, 1 prime-host.ml, 1 prime-likes.com, 1 prime-seo.net, 1 -primeauconsultinggroup.com, 1 primecapsslim.com.br, 1 primechasedata.com, 1 primechef.com.mx, 1 @@ -117911,7 +117784,6 @@ productpeo.pl, 1 products-for-health.tk, 1 products4more.at, 1 productsandfeatures.com, 1 -productupdates.org, 1 produform.it, 1 produits-dantan.com, 1 produkt.cf, 1 @@ -118285,7 +118157,7 @@ proofwiki.org, 1 proon.org, 1 proos.nl, 1 prop365.com, 1 -propaganda.love, 0 +propaganda.love, 1 propagandablog.de, 0 propagationtools.com, 1 propanesale.cf, 1 @@ -118528,6 +118400,7 @@ provisionircd.tk, 1 provlas.se, 1 provo.gov, 1 prowebservices.ca, 1 +prowechsel.de, 1 prowi.se, 1 prowindow.sk, 1 prowise.com, 1 @@ -118762,7 +118635,6 @@ psw-group.de, 1 psw-training.de, 1 psyart.tk, 1 psycenter.tk, 1 -psych2go.net, 1 psychcare.cz, 1 psychcaremd.com, 1 psychedelia.com, 1 @@ -119831,7 +119703,6 @@ quaketips.ga, 1 quakeworld.tk, 1 qualbe.com, 1 qualebroker.com, 1 -qualescegliere.it, 1 qualiacomputers.com, 1 qualifio.com, 1 qualishomes.co.uk, 1 @@ -119975,7 +119846,6 @@ qubitsandbytes.co.uk, 1 qubyte.codes, 1 quchronicle.com, 1 qudini.com, 1 -quebec.ca, 1 quebeccat.com, 1 quebeccats.com, 1 quebecdogs.com, 1 @@ -121298,7 +121168,6 @@ raskruti.ga, 1 raskruti.ml, 1 raskrutka.cf, 1 rasmera.ddns.net, 1 -rasmushaslund.com, 1 rasnyder.com, 1 rasolutions.it.com, 1 rasp.moe, 1 @@ -121512,7 +121381,6 @@ rbcosmetici.it, 1 rbd.events, 1 rbensch.com, 0 rbet.tk, 1 -rbfalcon.com, 1 rbh.co.uk, 1 rbi-admin.com, 1 rbi-umbrella.com, 1 @@ -121602,6 +121470,7 @@ rda.run, 1 rdactive.de, 1 rdap.ss, 1 rdb.vote, 1 +rdc-metalworks.com, 1 rdcdesign.com, 1 rddjapan.info, 1 rded.nl, 1 @@ -122700,7 +122569,6 @@ relaxhavefun.com, 1 relaxmasaz.cz, 1 relaxpointhyncice.cz, 1 relaxti.me, 1 -relaxxed.nl, 1 relaxxxed.com, 1 relaybox.io, 1 relaypay.io, 1 @@ -123073,7 +122941,6 @@ repeatresponse.com.au, 1 repertuarim.com.tr, 1 repettoshoes.tk, 1 repin.in.ua, 1 -repinger.com, 1 repinger.my.id, 1 repintadoautomotriz.com, 1 repl.ga, 1 @@ -123176,6 +123043,7 @@ resalerental.com, 1 resanebartar.tk, 1 resbi.tk, 1 resc.la, 1 +rescatec.com, 1 resch-verlag.com, 1 rescms-secure.com, 1 rescuenode.com, 1 @@ -123279,7 +123147,6 @@ resoundpro.ca, 1 resourceconnect.com, 1 resourceguruapp.com, 1 resources.flowfinity.com, 1 -resourcesab.eu, 1 resourcesmanagementcorp.com, 1 resourcespace.com, 1 respawwn.com, 1 @@ -123722,6 +123589,7 @@ rgdoi.net, 1 rgdt.tk, 1 rgfundraising.com, 1 rggraphics.mx, 1 +rgmdtri.ru, 1 rgnext.com, 1 rgpd-elearning.com, 1 rgservice.ml, 1 @@ -125163,6 +125031,7 @@ rosabellas.co.uk, 1 rosabrasiv.ga, 1 rosacosmos.tn, 1 rosaflorbijoux.com.br, 1 +rosakkreditatsiya-forum.ru, 1 rosalinda.cl, 1 rosalindturner.co.uk, 1 rosalopezcortes.tk, 1 @@ -125861,7 +125730,6 @@ ruhrmobil-e.de, 1 ruhrpott-media.com, 1 ruhrpott.rodeo, 1 rui-vet.com, 1 -ruiduntrading.com, 1 ruihuabao.com.cn, 1 ruileal.pt, 1 ruileitao.pt, 1 @@ -125946,7 +125814,6 @@ runeworldforums.tk, 1 runews.cf, 1 runfitcoaching.com, 1 rungie.com, 0 -rungisinternational.com, 1 rungstedhave.dk, 1 rungutan.com, 1 runharbor.com, 1 @@ -126559,7 +126426,7 @@ saf.earth, 1 safagiza.ml, 1 safalfasalonline.in, 1 safar.sk, 1 -safarilaw.com, 0 +safarilaw.com, 1 safaris-uganda.com, 1 safarisbonafricatours.com, 1 safarisop.com, 1 @@ -126982,6 +126849,7 @@ salfordepc.uk, 1 salfraedingarnir.is, 1 salge.rocks, 1 salgueirocarlos.com, 1 +salianmod.ir, 1 salibandy.tk, 1 salidaswap.com, 1 salient-dialers.com, 1 @@ -127065,7 +126933,7 @@ saltcave.gq, 1 saltedfish.network, 1 saltedfishes.com, 1 saltedge.com, 1 -saltedpasta.com, 0 +saltedpasta.com, 1 saltercane.com, 0 saltlakecounty.gov, 1 saltlakehealth.gov, 1 @@ -127424,6 +127292,7 @@ sangoandmiroku.tk, 1 sangowen.xyz, 1 sangreytinta.tk, 1 sanguanini.farm, 1 +sanguinebio.com, 1 sangwanbeach.tk, 1 sangyo-rock.com, 1 sanhotel.ml, 1 @@ -127580,7 +127449,6 @@ santmark.org, 1 santo.fi, 1 santodelgiorno.it, 1 santong.tk, 1 -santons-fouque.fr, 1 santorinidress.com, 1 santoscarmelitas.tk, 1 santosdecordoba.tk, 1 @@ -127945,7 +127813,6 @@ save-me-aachen.de, 1 save-me-aachen.eu, 1 save-me-koeln.de, 1 save-spb.ru, 1 -save.ch, 1 save.gov, 1 saveaward.gov, 1 savebees.org, 1 @@ -128339,7 +128206,6 @@ schella.network, 1 schellebelle.tk, 1 schellenberg-web.de, 1 schellenberg.ac, 1 -schellevis.net, 0 schenectadycountyny.gov, 1 schenkel.tk, 1 schenkes.de, 0 @@ -128577,7 +128443,6 @@ schtiehve.duckdns.org, 1 schu.be, 1 schuan.cn, 1 schubertgmbh-ingelheim.de, 1 -schubertnest.at, 1 schuelerzeitung-ideenlos.de, 1 schuermannhof.de, 1 schuetzenverein-haselbach.de, 1 @@ -129048,7 +128913,6 @@ se.com, 1 se.gg, 1 se.search.yahoo.com, 0 se2.com, 1 -se6.io, 1 sea-airinternational.tk, 1 sea-force.com.au, 1 sea-godzilla.com, 1 @@ -129336,7 +129200,7 @@ sectv.com, 1 secudoc.nl, 1 secularweb.org, 1 secumailer.com, 0 -secumailer.nl, 1 +secumailer.nl, 0 secundity.com, 1 secundity.nl, 1 secunm.org, 1 @@ -129401,6 +129265,7 @@ securist.nl, 1 securite.jp, 1 securiteincendie.fr, 1 securitelandry.com, 1 +securitepubliqueaem.com, 1 securiti.ai, 1 securitum.pl, 1 security-24-7.com, 1 @@ -129415,7 +129280,6 @@ security.gov.uk, 1 security.pl, 1 security.xn--q9jyb4c, 1 securityabstract.com, 1 -securityanalystservices.com, 1 securityaware.me, 1 securitybasegroup.com, 1 securitybrief.asia, 1 @@ -129802,7 +129666,6 @@ semantics.ga, 1 semaphore-studios.com, 1 semari.or.id, 1 sembosihosting.tk, 1 -semboyan35.com, 1 sembska.de, 1 sembyotic.com, 1 semcensurabrag.com.br, 1 @@ -130019,7 +129882,7 @@ seo-analyse.com, 1 seo-blog12.tk, 1 seo-dr-it.com, 1 seo-dvizh.ru, 1 -seo-forum.nu, 0 +seo-forum.nu, 1 seo-inc.ru, 1 seo-linz.at, 1 seo-obmen.tk, 1 @@ -130144,7 +130007,6 @@ seovisits.tk, 1 seoviziti50.tk, 1 seowerkz.com, 0 seowhizone.com, 1 -seowordpress.pl, 1 seowork.tk, 1 seozel.tk, 1 seozen.top, 1 @@ -130205,7 +130067,6 @@ serc.ac.uk, 1 sercanazizoglu.com, 1 sercasindustry.tk, 1 sercoapprenticeships.uk, 1 -serdarakyildiz.com, 1 serdarwork.com, 1 serdengolpinar.tk, 1 serecoponsillo.it, 1 @@ -130432,7 +130293,6 @@ serviciosfncs.com, 1 servicioskoinonia.org, 1 serviciosprevisionfuneraria.com, 1 serviciotecnicoencomputacion.com.ve, 0 -serviciotecnicosantcugat.es, 1 servier.com, 1 servietten-grosshandel.at, 1 servietten-grosshandel.be, 1 @@ -131200,7 +131060,6 @@ sheehyinfinitioftysonsparts.com, 1 sheekdeveloper.com, 1 sheelyuu.art, 1 sheepfriends.com, 1 -sheepproductions.com, 1 sheeprock.tk, 1 sheepsound.tk, 1 sheerchain.com, 1 @@ -131329,7 +131188,6 @@ shiawasedo.co.jp, 1 shibahara-shika.com, 1 shibainu.com.br, 1 shibakery.com, 1 -shibashake.com, 1 shibayan.jp, 1 shibbydex.com, 1 shibeflix.com, 1 @@ -131367,6 +131225,7 @@ shiga1.jp, 1 shigaben.or.jp, 1 shiganmartialarts.com, 1 shiggles.net, 1 +shiggles.org, 1 shigizemi.com, 1 shigotoarimasu.com, 1 shigotoba.com, 1 @@ -131651,6 +131510,7 @@ shopmalinka.cf, 1 shopmaxilife.com.ph, 1 shopmontrose.com, 1 shopnemp.com, 1 +shopofturkey.com, 1 shoponlinedeals.tk, 1 shopopop.com, 1 shoporangetheory.com, 1 @@ -131874,7 +131734,6 @@ shost.ga, 1 shota-sekkotsuin.com, 1 shota.pictures, 1 shotbow.net, 1 -shotcounter.nl, 1 shotdeck.com, 1 shotoniphone.ga, 1 shotsbyferry.nl, 1 @@ -132333,7 +132192,6 @@ sikkasoft.com, 1 sikkind.com, 0 siku-shop.ch, 1 siku.pro, 1 -sil.box, 1 sil.co.id, 1 sila.qa, 1 silagra.ml, 1 @@ -133033,6 +132891,7 @@ sisqo.tk, 1 sisseastumine.ee, 1 sissyroulette.com, 1 sisteltestserver.com, 1 +sistem-maklumat.com, 1 sistem-maklumat.com.my, 1 sistema-trenirovok.ml, 1 sistema20k.tk, 1 @@ -133225,7 +133084,6 @@ sk-ii.com, 1 sk.tl, 1 sk4y.net, 1 skaala.com, 1 -skaalen.com, 1 skaapkraalonline.co.za, 1 skabour.co.uk, 1 skachat-programmylini.ga, 1 @@ -133532,7 +133390,6 @@ skux.ch, 1 skvele-cesko.cz, 1 skvelecesko.cz, 1 skvot.de, 1 -skvot.io, 1 skwile-cafe.com, 1 skwlkrs.com, 1 sky-aroma.com, 1 @@ -134125,7 +133982,6 @@ smartfaktor.pl, 1 smartfire.pt, 1 smartfirm.com.au, 1 smartfit.cz, 1 -smartfixmarburg.de, 1 smartfons.tk, 1 smartfreedomstrategies.com, 1 smartfridge-ai.com, 1 @@ -134342,7 +134198,6 @@ smithf.red, 1 smithikakart.in, 1 smithmont.com, 1 smithmountainlake.com, 1 -smithrobertsonpromotions.com, 1 smithsonian.gov, 1 smithsstational-fpd.gov, 1 smithsstational.gov, 1 @@ -135767,6 +135622,7 @@ southpeacearchives.org, 1 southphoenixair.tk, 1 southportland.gov, 1 southridgeservices.com, 1 +southrock.com, 1 southsands.com, 1 southshoreautowash.com, 1 southside-crew.com, 1 @@ -137240,7 +137096,6 @@ stail.eu, 1 stainfilm.com, 0 stainhaufen.de, 1 stainless-steel-cookware.tk, 1 -stainlessgames.com, 1 stainternational.com, 1 stairlin.com, 0 stairmaster.fr, 1 @@ -137298,7 +137153,6 @@ stamppadinks.com, 1 stamurai.com, 1 stan.store, 1 stanandjerre.org, 1 -stanard.com, 1 stanbeukers.com, 1 stancer.com, 1 stanchierifamilylaw.com, 1 @@ -137770,7 +137624,7 @@ steelnavi.jp, 1 steeloncall.com, 1 steelpoint.com.pl, 1 steelsheds.biz, 1 -steelstructuresltd.com, 1 +steelsoldiers.com, 1 steelvortex.tk, 1 steelzone.tk, 1 steemit.com, 1 @@ -138259,6 +138113,7 @@ stlouisparkmn.gov, 1 stlouisstabilizing.com, 1 stlpassports.com, 1 stlpoolattendants.com, 1 +stltacticals.com, 1 stlu.de, 1 stlucieclerk.gov, 1 stluciesheriff.gov, 1 @@ -138473,7 +138328,6 @@ storage-books.gq, 1 storageideas.uk, 1 storageioblog.com, 1 storagenewsletter.com, 1 -storageshedsnc.com, 1 storagex.co.uk, 1 storck.com, 1 stordbatlag.no, 1 @@ -139321,7 +139175,6 @@ sublettecountywy.gov, 1 sublimacaoshop.com.br, 1 sublimated.tk, 1 sublimationsouthafrica.com, 1 -sublimesurface.fr, 1 sublimigeek.fr, 1 subliminalrecorder.com, 1 submeet.vet, 1 @@ -140049,7 +139902,6 @@ supportrelatecare.org, 1 supportsave.com, 1 supportyourapp.com, 1 suppos-net.tk, 1 -supra-forge.com, 1 supra.tf, 1 supracube.com, 1 supraelco.com, 1 @@ -140401,7 +140253,6 @@ svseglobal.com, 1 svsewerut.gov, 1 svsg.co, 1 svswebmarketing.com, 1 -svtemplemn.org, 1 svtl.ch, 1 svtr.de, 1 svtv.org, 1 @@ -140840,7 +140691,6 @@ syncspace.live, 1 synctera.com, 1 synder.com, 1 synderapp.com, 1 -syndicats.de, 1 syndigo.com, 1 syndika.co, 1 syndikalismus-im-laendle.tk, 1 @@ -140909,6 +140759,7 @@ syrianair.cf, 1 syrianair.ga, 1 syrianair.ml, 1 syrianet.cf, 1 +syrianskaif.com, 1 syrius.tk, 1 syronex.com, 1 syrover.ru, 1 @@ -141286,7 +141137,6 @@ taconic.com, 1 tacoroumen.nl, 1 tacotown.tk, 1 tacti.cc, 1 -tactical.zone, 1 tacticalavocado.com, 1 tacticalsquare.com, 1 tacticalvote.co.uk, 1 @@ -141615,6 +141465,7 @@ tama.ga, 1 tamagotchicenter.com, 1 tamalcloud.com, 1 tamamlayici.com.tr, 1 +tamamlayicisagliksigortasi.com.tr, 1 tamanlapanganindah.com, 1 tamaoka-eyeclinic.com, 1 tamarac.gov, 1 @@ -142193,7 +142044,6 @@ tb.org.tw, 1 tbahn.de, 1 tbassistant.xyz, 1 tbatr.tk, 1 -tbbank.gov.tm, 1 tbcinteriorismo.com, 1 tbcloud.site, 0 tbejos.com, 1 @@ -142452,7 +142302,7 @@ teampadu.com, 1 teampages.com, 1 teampassword.com, 1 teampatat.nl, 1 -teamphotonetwork.com, 1 +teamphotonetwork.com, 0 teampoison.tk, 1 teampz.com, 1 teamr3set.com, 1 @@ -143079,7 +142929,6 @@ tehplace.club, 1 tehpod.com.ua, 1 tehrabbitt.com, 0 tehrankey.ir, 1 -tehranservicekaran.com, 1 teia.art, 1 teichroeb.net, 1 teier.eu, 1 @@ -143274,7 +143123,6 @@ telework.gov, 1 teleworkouts.com, 1 telexit.de, 1 telexsus.com, 1 -teleyal.blog, 1 telezon.ru, 1 telfaircountyga.gov, 1 telfas.de, 1 @@ -143324,7 +143172,6 @@ temirgaliev.tk, 1 temis.nl, 1 temizim.com, 1 temizlik.ml, 1 -temizmama.com, 1 teml.in, 1 temnacepel.cz, 1 temnikova.tk, 1 @@ -143629,6 +143476,7 @@ terremetisagency.com, 1 terremoto.com.br, 1 terrenal.tk, 1 terrenasparadise.com, 1 +terrenosparainvertir.com, 1 terres-et-territoires.com, 1 terresmagiques.com, 0 terrimcaleerphotography.com, 1 @@ -143887,6 +143735,7 @@ textiles.bg, 1 textiles.tk, 1 textilstore.de, 1 textlinktausch.tk, 1 +textme26.site, 1 textonly.email, 1 textpages.tk, 1 textpattern.com, 1 @@ -144141,7 +143990,6 @@ the5th.nl, 1 the7eye.org.il, 1 the8rules.co.uk, 1 thea-team.net, 1 -theacademicpapers.co.uk, 1 theacc.com, 1 theaccountant-online.com, 1 theaccountingcompanyleeds.co.uk, 1 @@ -144410,6 +144258,7 @@ thechatlinenumbers.com, 1 thechavs.xyz, 1 thecheapairlinetickets.com, 1 thecheat.tk, 1 +thecheese.co.nz, 1 thechels.uk, 1 thechelseadrugstore.ie, 1 thechemistryisdead.tk, 1 @@ -144892,7 +144741,6 @@ thelegionshirley.co.uk, 1 thelematics.com, 1 thelemonlawcalifornia.com, 0 thelencystore.com, 1 -thelevelman.com, 1 thelevelsfinancial.co.uk, 1 thelewispartnership.co.uk, 1 thelibertinephilosophy.ga, 1 @@ -145189,7 +145037,6 @@ thepokerbank.com, 1 thepokerforum.com, 1 thepokerpeople.com, 1 thepoliticsofpesticides.com, 1 -thepollitochicken.com, 1 thepool.tk, 1 thepopcornfactory.com, 1 theportalbroker.com, 1 @@ -145604,7 +145451,6 @@ thewellbeingarchitect.ie, 1 thewellblog.com, 1 thewest.tk, 1 thewhiteboxxx.com, 1 -thewhitedog9487.xyz, 1 thewhitehorses.tk, 1 thewhiterabbit.space, 1 thewhitneypaige.com, 1 @@ -145885,6 +145731,7 @@ thors-hearth.tk, 1 thorsteinsson.com, 1 thorsten-schaefer.com, 1 thorstenschaefer.name, 1 +those.jp, 1 thost3.de, 1 thotcomputed.com, 1 thots.org, 1 @@ -146612,7 +146459,6 @@ tinmarin.org, 1 tinmouthvt.gov, 1 tinneke.tk, 1 tinnhanhvietnam.tk, 1 -tinnitus.tirol, 1 tino-dtb.de, 1 tinselandtwigllc.com, 1 tint.edu.in, 1 @@ -147491,7 +147337,6 @@ toobug.net, 0 tooelecity.gov, 1 tooeleco.gov, 1 tooelecountyvotes.gov, 1 -toofab.com, 1 toohka.org, 1 tooij.com, 1 tookan.tech, 1 @@ -149094,7 +148939,6 @@ travelgirlsclub.com, 1 travelglamour.ga, 1 travelgratis.ga, 1 travelhands.ga, 1 -travelholicworld.com, 1 travelhunter.nl, 1 travelhusky.ga, 1 traveling-thailand.info, 1 @@ -149227,7 +149071,6 @@ treasureboxgreetings.com, 1 treasureboxhomes.org, 1 treasurecoastconnector.com, 1 treasureislandbeads.ga, 1 -treasuremountainmining.com, 1 treasuretrooperguide.tk, 1 treasury.gov.lk, 1 treasurydirect.gov, 1 @@ -149687,8 +149530,6 @@ tronnews.news, 1 tronnews.xyz, 1 tronox.com, 1 troomcafe.com, 1 -troonnorthgolf.com, 0 -troopaid.info, 1 troopers.de, 1 trootech.com, 1 trophcomplewin.ml, 1 @@ -150583,7 +150424,6 @@ tusconsultorex.com, 1 tusdtapi.com, 1 tusharwalaskar.com, 1 tusi.co, 1 -tusic.net, 1 tuslamparasonline.com, 1 tusmedicamentos.com, 1 tusociofinanciero.com, 1 @@ -150843,6 +150683,7 @@ twistapp.com, 1 twistbets.com, 1 twistedfamilies.com, 1 twistedoakonline.com, 1 +twistedservers.com.au, 1 twistedtea.com, 1 twistedwave.com, 1 twistersolutions.com, 1 @@ -150862,7 +150703,6 @@ twitchy.tk, 1 twitcker.com, 1 twittelzie.nl, 1 twitter.ax, 1 -twitter.com, 1 twitterdriver.io, 1 twittpr.com, 1 twizzle.net, 1 @@ -151983,7 +151823,6 @@ unis-pour-la-planete.com, 1 unis-pour-le-climat.com, 1 uniselectweb.com, 1 uniserve.com, 1 -unisnu.ac.id, 1 unison-d.com, 1 unisonglobal.com, 1 unisontech.org, 1 @@ -152343,7 +152182,6 @@ upguard.org, 1 upguardinc.com, 1 uphabit.io, 1 uphold-15-party.com, 1 -upholsterycleanerslondon.co.uk, 1 upholsterydesign.com.au, 1 uphost.be, 1 uphshrine.net, 1 @@ -152912,7 +152750,6 @@ utilityapi.com, 1 utko12.ru, 1 utleg.gov, 1 utloperadora.com.br, 1 -utobo.com, 1 utodyg.ga, 1 utonia.ch, 1 utop.io, 1 @@ -153268,7 +153105,6 @@ valeriansaliou.name, 1 valeriapanarina.com, 0 valerieadolff.com, 0 valerielittlewood.uk, 1 -valeriya.life, 1 valeriymaleev.com, 1 valeryvenom.net, 1 valescarodrigues.com.br, 1 @@ -153548,7 +153384,6 @@ vanyavpn.cz, 1 vanyavpn.ec, 1 vanyavpn.gd, 1 vanyavpn.gg, 1 -vanyavpn.gl, 1 vanyavpn.gs, 1 vanyavpn.hn, 1 vanyavpn.im, 1 @@ -153556,7 +153391,6 @@ vanyavpn.in, 1 vanyavpn.io, 1 vanyavpn.net, 1 vanyavpn.org, 1 -vanyavpn.ru, 1 vaoig.gov, 1 vapebarstore.com, 1 vapebarstore.hu, 1 @@ -153675,6 +153509,7 @@ vatadezahar.com, 1 vatav.eu, 1 vatav.tk, 1 vatazhok.com, 1 +vatc.org, 1 vatcompliance.com, 1 vates.tech, 1 vatikantour.tk, 1 @@ -153716,6 +153551,7 @@ vayaprecio.es, 1 vayavotarcolorado.gov, 1 vaygren.com, 1 vazdemelloconsultoria.com.br, 1 +vazon.pp.ua, 1 vb-barbara-beck.de, 1 vb.com.br, 1 vbanu.com.ua, 1 @@ -154077,7 +153913,6 @@ venstar.com, 1 ventajasdesventajas.com, 1 ventana.kz, 1 ventanillaproveedorespit.com, 1 -ventanita.com.ar, 1 ventasdeseguridad.com, 1 ventasreit.com, 1 ventassantillan.com, 1 @@ -154990,7 +154825,6 @@ vilans.nl, 1 vilantice.cz, 1 vilaonze.com.br, 1 vilasantina.com.br, 1 -vilavilma.si, 1 vilavyhlidka.cz, 1 vilawatt.cat, 1 vilaydin.com, 1 @@ -155123,7 +154957,6 @@ villasintrabali.com, 1 villasupport.it, 1 villaumbrales.tk, 1 villavaltava.fi, 1 -villaville.com, 1 villawirz.it, 1 ville-gennevilliers.fr, 1 ville-ideale.fr, 1 @@ -155739,7 +155572,6 @@ vivaio.roma.it, 1 vivaiocolombo.com, 1 vivalacloud.ru, 1 vivalajack.de, 1 -vivalakidsworld.com, 1 vivaldi.club, 1 vivaldi.com, 1 vivaldi.net, 1 @@ -155958,7 +155790,6 @@ vliegendklokske.com, 1 vliegherrie.nl, 1 vlijmscherrup.tk, 1 vlike.ml, 1 -vlndc.org, 1 vloeck.de, 1 vloggerfaire.com, 1 vloggerspace.tk, 1 @@ -156291,6 +156122,7 @@ voltmalta.org, 1 voltnederland.org, 1 voltnorway.org, 1 voltoesterreich.org, 1 +voltpoland.org, 1 voltpolska.org, 1 voltportugal.org, 1 voltromania.org, 1 @@ -157359,7 +157191,6 @@ wallofclocks.com, 1 wallowacounty.gov, 1 wallpaperstreet.tk, 1 wallpaperswide.com, 1 -wallpapertag.com, 1 walls.io, 0 wallsauce.com, 1 wallsbreaker.top, 1 @@ -157594,7 +157425,6 @@ warrinainnisfail.com.au, 1 warringtonkidsbouncycastles.co.uk, 1 warringtonsownbuses.co.uk, 1 warriorofmars.com, 1 -warrnamboolaero.club, 1 warrock-es.tk, 1 warrs.com, 1 wars.aw, 1 @@ -157860,7 +157690,6 @@ waxlrs.com, 1 way.ac, 1 way2tech.de, 1 wayaberolodge.com, 1 -wayakcomm.com, 1 waybinary.com, 1 waycoolmail.tk, 1 waycraze.com, 1 @@ -158279,6 +158108,7 @@ webemployed.com, 1 webencrypt.org, 1 webengage.com, 1 webepc.it, 1 +weber-immobilienberatung.de, 1 weber.com, 1 weber911.gov, 1 webera.lt, 1 @@ -158943,7 +158773,6 @@ weosx.com, 1 wep.pw, 1 wepa.pe, 1 wepaempowercenter.org, 1 -wepay.com, 1 wepbiz.com, 1 weplan-app.com, 1 weplananalytics.com, 1 @@ -159448,7 +159277,6 @@ whitechristmas.com.au, 1 whitecollar.net, 1 whitecollarfraud.com, 1 whitecreekny.gov, 1 -whitedragonmartialarts.org, 1 whitefieldnhpd.gov, 1 whitefishtownshipmi.gov, 1 whitefordtownshipmi.gov, 1 @@ -159802,7 +159630,6 @@ wikimediacommons.jp.net, 1 wikimediacommons.mobi, 1 wikimediacommons.net, 1 wikimediacommons.org, 1 -wikimediaendowment.org, 1 wikimediaenterprise.com, 1 wikimediafoundation.com, 1 wikimediafoundation.info, 1 @@ -160647,7 +160474,7 @@ woh.org, 0 woheni.de, 1 wohlgefuehl-massagen.de, 1 wohlpa.de, 0 -wohnbau-mobilitaet.ch, 1 +wohnbau-mobilitaet.ch, 0 wohnberatung-wien.at, 1 wohnsitz-ausland.com, 1 wohnungsaufloesung-berlin.net, 1 @@ -161253,6 +161080,7 @@ wowlegacy.ml, 1 wowlifedesignandco.jp, 1 wowlove.tk, 1 wownskportal.tk, 1 +wowowow.com, 1 wowpilates.com, 1 wowra.net.pl, 1 wows-mods.tk, 1 @@ -161591,7 +161419,6 @@ wurstbrot.cf, 1 wurstmineberg.de, 1 wurzelchaos.de, 1 wurzelkanal.de, 1 -wushka.com.au, 1 wusu.tk, 1 wuxian.ml, 0 wuya.eu.org, 1 @@ -161749,10 +161576,10 @@ www.tiaa.org, 1 www.tl, 1 www.torproject.org, 0 www.tumblr.com, 0 +www.twitter.com, 1 www.united.com, 1 www.usaa.com, 0 www.vino.com, 0 -www.wepay.com, 1 www.wordpress.com, 0 www.zdnet.com, 1 www68277.com, 1 @@ -162000,7 +161827,6 @@ xb8861.com, 1 xb9009.com, 1 xb906.com, 1 xb913.com, 1 -xb925.com, 0 xb937.com, 1 xb957.com, 0 xb961.com, 1 @@ -162010,7 +161836,6 @@ xb972.com, 1 xb980.com, 1 xb982.com, 1 xbanner.tk, 1 -xbb.hk, 1 xbb.li, 1 xbc.nz, 1 xbdm.fun, 1 @@ -162163,7 +161988,6 @@ xevietanh.com, 1 xevnet.com, 1 xevolkswagen.com, 1 xexpert.ro, 1 -xfair.com, 0 xfantasy.su, 1 xfcy.me, 1 xfd3.de, 1 @@ -162179,11 +162003,11 @@ xg9gbbbr.nsupdate.info, 1 xgadget.de, 1 xgameshst.com, 1 xgclan.com, 1 +xgeni.us, 1 xgn.es, 1 xgp.pl, 1 xgpu.deals, 1 xgzepto.cn, 1 -xh-ws.com, 1 xh.ax, 1 xhamiadituria.com, 1 xhamster.com, 1 @@ -163102,7 +162926,6 @@ xnopyt.info, 1 xntrik.wtf, 1 xnu.kr, 1 xnxxporns.com, 1 -xo.wtf, 1 xoan.cf, 1 xobot.su, 1 xobotun.com, 1 @@ -163174,6 +162997,7 @@ xpsauto.com, 1 xpsautomation.com, 1 xpsfactory.com, 1 xpsinnovation.com, 1 +xpsnow.net, 1 xpsrobotics.com, 1 xptrack.com, 1 xptrackstaging.com, 1 @@ -163226,6 +163050,7 @@ xsz.jp, 1 xt.om, 1 xt177.com, 1 xt71uc.top, 1 +xtaboo3d.com, 1 xtarget.ru, 1 xtec.eu.org, 1 xtechtecnologia.tec.br, 1 @@ -164591,7 +164416,6 @@ youtubekids.com, 1 youtubelet.com, 1 youtuberis.lt, 1 youtuberus.tk, 1 -youwebcams.org, 1 youwillnever.be, 1 youyuan.rocks, 1 youyuandesign.top, 1 @@ -165126,7 +164950,6 @@ zaloghaz.ro, 1 zalohovaniburian.cz, 1 zalow.com, 1 zaltv.com, 1 -zalvus.com, 1 zam0th.tk, 1 zamadaftar.com, 1 zamalektoday.com, 1 @@ -166072,7 +165895,6 @@ zocode.tk, 1 zocoxx.com, 1 zode.co, 1 zodee.com.au, 1 -zodgame.xyz, 1 zodiacohouses.com, 1 zodiaconline.com, 1 zodiak.tk, 1 diff --git a/security/manager/tools/log_list.json b/security/manager/tools/log_list.json index 6beda476b59bd..d9154dc269c55 100644 --- a/security/manager/tools/log_list.json +++ b/security/manager/tools/log_list.json @@ -1,6 +1,6 @@ { - "version": "82.2", - "log_list_timestamp": "2026-01-25T12:54:32Z", + "version": "82.5", + "log_list_timestamp": "2026-01-28T12:53:32Z", "operators": [ { "name": "Google", diff --git a/security/sandbox/chromium-shim/patches/52_surface_kPolMemPageCount_and_revert_to_14.patch b/security/sandbox/chromium-shim/patches/52_surface_kPolMemPageCount_and_revert_to_14.patch index 688cc1855aa2c..912b7f1b86695 100644 --- a/security/sandbox/chromium-shim/patches/52_surface_kPolMemPageCount_and_revert_to_14.patch +++ b/security/sandbox/chromium-shim/patches/52_surface_kPolMemPageCount_and_revert_to_14.patch @@ -1,11 +1,11 @@ This surfaces the memory page count for the maximum policy size as a constant, -so that we can rely on it in our code. It also reverts it to 14, the value -it was set to before the latest chromium sandbox update. +so that we can rely on it in our code. It also changes it to 22 to allow for +uesrs that need large numbers of GPU process font rules. -diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h +diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h index 4d9d8f270890..bffab2fae890 100644 ---- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h -+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h +--- a/sandbox/win/src/sandbox_policy.h ++++ b/sandbox/win/src/sandbox_policy.h @@ -10,16 +10,19 @@ #include "base/containers/span.h" @@ -16,7 +16,7 @@ index 4d9d8f270890..bffab2fae890 100644 namespace sandbox { +// Number of memory pages to allow for the policy storage. -+constexpr size_t kPolMemPageCount = 14; ++constexpr size_t kPolMemPageCount = 22; + class AppContainer; @@ -26,10 +26,10 @@ index 4d9d8f270890..bffab2fae890 100644 kDefault, // Child is launched using the alternate desktop. kAlternateDesktop, -diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc +diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc index 2559133d772b..3395e9cadc07 100644 ---- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc -+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc +--- a/sandbox/win/src/sandbox_policy_base.cc ++++ b/sandbox/win/src/sandbox_policy_base.cc @@ -44,17 +44,17 @@ namespace sandbox { diff --git a/security/sandbox/chromium-shim/patches/53_adjust_accounting_in_LowLevelPolicy_Done.patch b/security/sandbox/chromium-shim/patches/53_adjust_accounting_in_LowLevelPolicy_Done.patch new file mode 100644 index 0000000000000..442ae73e5bf99 --- /dev/null +++ b/security/sandbox/chromium-shim/patches/53_adjust_accounting_in_LowLevelPolicy_Done.patch @@ -0,0 +1,49 @@ +Bug 1299611: Adjust LowLevelPolicy::Done byte accounting. + +diff --git a/sandbox/win/src/policy_low_level.cc b/sandbox/win/src/policy_low_level.cc +index 0f47c96fbf01..111ab7a37ff1 100644 +--- a/sandbox/win/src/policy_low_level.cc ++++ b/sandbox/win/src/policy_low_level.cc +@@ -89,6 +89,8 @@ bool LowLevelPolicy::Done() { + return false; + } + policy_store_->entry[static_cast(service)] = current_buffer; ++ // Account for the opcode_count in PolicyBuffer. ++ avail_size -= sizeof PolicyBuffer::opcode_count; + + RuleList::iterator rules_it = (*it).second.begin(); + RuleList::iterator rules_it_end = (*it).second.end(); +@@ -103,12 +105,14 @@ bool LowLevelPolicy::Done() { + if (avail_size < opcodes_size) { + return false; + } +- size_t data_size = avail_size - opcodes_size; ++ avail_size -= opcodes_size; ++ size_t data_size = avail_size; + PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; + if (!rule->RebindCopy(opcodes_start, opcodes_size, buffer_end, + &data_size)) { + return false; + } ++ DCHECK(avail_size >= data_size); + size_t used = avail_size - data_size; + buffer_end -= used; + avail_size -= used; +@@ -116,9 +120,14 @@ bool LowLevelPolicy::Done() { + } + + current_buffer->opcode_count = svc_opcode_count; +- size_t policy_buffers_occupied = +- (svc_opcode_count * sizeof(PolicyOpcode)) / sizeof(current_buffer[0]); +- current_buffer = ¤t_buffer[policy_buffers_occupied + 1]; ++ size_t opcode_bytes_used = sizeof PolicyBuffer::opcode_count + ++ (svc_opcode_count * sizeof(PolicyOpcode)); ++ size_t policy_buffer_count = ++ (opcode_bytes_used + sizeof(PolicyBuffer) - 1) / sizeof(PolicyBuffer); ++ size_t byte_padding = ++ (policy_buffer_count * sizeof(PolicyBuffer)) - opcode_bytes_used; ++ avail_size -= byte_padding; ++ current_buffer += policy_buffer_count; + } + + return true; diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc index 0f47c96fbf01d..d68017565c29d 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc @@ -89,6 +89,8 @@ bool LowLevelPolicy::Done() { return false; } policy_store_->entry[static_cast(service)] = current_buffer; + // Account for the opcode_count in PolicyBuffer. + avail_size -= sizeof PolicyBuffer::opcode_count; RuleList::iterator rules_it = (*it).second.begin(); RuleList::iterator rules_it_end = (*it).second.end(); @@ -103,12 +105,14 @@ bool LowLevelPolicy::Done() { if (avail_size < opcodes_size) { return false; } - size_t data_size = avail_size - opcodes_size; + avail_size -= opcodes_size; + size_t data_size = avail_size; PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; if (!rule->RebindCopy(opcodes_start, opcodes_size, buffer_end, &data_size)) { return false; } + DCHECK(avail_size >= data_size); size_t used = avail_size - data_size; buffer_end -= used; avail_size -= used; @@ -116,9 +120,14 @@ bool LowLevelPolicy::Done() { } current_buffer->opcode_count = svc_opcode_count; - size_t policy_buffers_occupied = - (svc_opcode_count * sizeof(PolicyOpcode)) / sizeof(current_buffer[0]); - current_buffer = ¤t_buffer[policy_buffers_occupied + 1]; + size_t opcode_bytes_used = sizeof PolicyBuffer::opcode_count + + (svc_opcode_count * sizeof(PolicyOpcode)); + size_t policy_buffer_count = + (opcode_bytes_used + sizeof(PolicyBuffer) - 1) / sizeof(PolicyBuffer); + size_t byte_padding = + (policy_buffer_count * sizeof(PolicyBuffer)) - opcode_bytes_used; + avail_size -= byte_padding; + current_buffer += policy_buffer_count; } return true; diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h index bffab2fae890e..efc0869d3b873 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h @@ -16,7 +16,7 @@ namespace sandbox { // Number of memory pages to allow for the policy storage. -constexpr size_t kPolMemPageCount = 14; +constexpr size_t kPolMemPageCount = 22; class AppContainer; diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp index f74e2869b451c..fc668765e79b4 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -1321,6 +1321,19 @@ void SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel, NS_ERROR("Failed to get user's SID."); LOG_E("Failed to get user's SID. %lx", ::GetLastError()); } + + // Required for GetUserGeoID, which is used to get timezone information. + bool geoRuleSet = + config->AllowRegistryRead(L"HKEY_CURRENT_USER") == + sandbox::SBOX_ALL_OK && + config->AllowRegistryRead( + L"HKEY_CURRENT_USER\\Control Panel\\International\\Geo") == + sandbox::SBOX_ALL_OK; + if (!geoRuleSet) { + NS_ERROR("Failed to add rule for International Geo."); + LOG_E("Failed (ResultCode %d) to add rule for International Geo.", + result); + } } } @@ -1376,10 +1389,14 @@ void SandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) { config->SetLockdownDefaultDacl(); config->AddRestrictingRandomSid(); - // Policy wrapper to keep track of available rule space. The full policy has - // 14 pages, so 12 allows two pages for generic process rules and to allow for - // padding that occurs in LowLevelPolicy::Done. See bug 2009140. - sandboxing::SizeTrackingConfig trackingConfig(config, 12); + // Policy wrapper to keep track of available rule space. We allow two spare + // pages for generic process rules and to allow for padding that occurs in + // LowLevelPolicy::Done. See bug 2009140. + // Note that we plan to move to a single font access rule in bug 2002995. This + // will remove the need for individual rules and mean that we can reduce + // sandbox::kPolMemPageCount. + sandboxing::SizeTrackingConfig trackingConfig(config, + sandbox::kPolMemPageCount - 2); if (StaticPrefs::security_sandbox_chrome_pipe_rule_enabled()) { // Add the policy for the client side of a pipe. It is just a file diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 3f62da3876808..f87fe39a9484a 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -563,7 +563,7 @@ notes = "Maintained by the Glean and Application Services teams" [[wildcard-audits.uniffi_internal_macros]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -user-id = 111105 +user-id = 111105 # Mark Hammond (mhammond) start = "2025-03-18" end = "2026-03-25" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 86218975dac23..8e153820223e8 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -261,15 +261,15 @@ user-login = "jrmuizel" user-name = "Jeff Muizelaar" [[publisher.glean]] -version = "66.3.0" -when = "2025-12-19" +version = "67.0.0" +when = "2026-01-21" user-id = 48 user-login = "badboy" user-name = "Jan-Erik Rediger" [[publisher.glean-core]] -version = "66.3.0" -when = "2025-12-19" +version = "67.0.0" +when = "2026-01-21" user-id = 48 user-login = "badboy" user-name = "Jan-Erik Rediger" @@ -791,64 +791,74 @@ user-login = "Manishearth" user-name = "Manish Goregaokar" [[publisher.uniffi]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_bindgen]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_build]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_core]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_internal_macros]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_macros]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_meta]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_pipeline]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_testing]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.uniffi_udl]] -version = "0.29.3" -when = "2025-06-06" -user-id = 127697 -user-login = "bendk" +version = "0.31.0" +when = "2026-01-14" +user-id = 111105 +user-login = "mhammond" +user-name = "Mark Hammond" [[publisher.utf8_iter]] version = "1.0.4" @@ -3103,12 +3113,77 @@ who = "Ameer Ghani " criteria = "safe-to-deploy" delta = "0.4.1 -> 0.4.2" -[[audits.mozilla.wildcard-audits.uniffi_internal_macros]] +[[audits.mozilla.wildcard-audits.uniffi]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -user-id = 127697 # bendk -start = "2025-02-06" -end = "2026-03-14" +user-id = 111105 # Mark Hammond (mhammond) +start = "2021-11-22" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_bindgen]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2021-11-22" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_build]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2021-11-22" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_core]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2023-11-20" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_macros]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2021-11-22" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_meta]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2023-11-20" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_pipeline]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2025-10-08" +end = "2027-01-14" +notes = "Maintained by the Glean and Application Services teams" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_testing]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2023-11-20" +end = "2027-01-08" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.wildcard-audits.uniffi_udl]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +user-id = 111105 # Mark Hammond (mhammond) +start = "2023-11-20" +end = "2027-01-08" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" [[audits.mozilla.wildcard-audits.weedle2]] @@ -3129,6 +3204,24 @@ end = "2026-07-02" notes = "Maintained by me" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" +[[audits.mozilla.audits.askama]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.13.1 -> 0.14.0" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.askama_derive]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.13.1 -> 0.14.0" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.askama_parser]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.13.0 -> 0.14.0" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.basic-toml]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy" diff --git a/taskcluster/kinds/searchfox/kind.yml b/taskcluster/kinds/searchfox/kind.yml index 80c8cfc8f0aa5..c420ab42fa7a1 100644 --- a/taskcluster/kinds/searchfox/kind.yml +++ b/taskcluster/kinds/searchfox/kind.yml @@ -69,6 +69,7 @@ tasks: - linux64-pkgconf - sysroot-x86_64-linux-gnu - sysroot-wasm32-wasi + - terser linux64-searchfox/debug: description: "Linux64 Debug Searchfox" @@ -163,6 +164,7 @@ tasks: - macosx64-sdk - sysroot-x86_64-linux-gnu - sysroot-wasm32-wasi + - terser macosx64-aarch64-searchfox/debug: description: "MacOS X aarch64 Debug Cross-compile Searchfox" @@ -267,6 +269,7 @@ tasks: - nsis - sysroot-x86_64-linux-gnu - sysroot-wasm32-wasi + - terser - vs - dxc-x86_64-pc-windows-msvc - winappsdk-x86_64-pc-windows-msvc diff --git a/taskcluster/mach_commands.py b/taskcluster/mach_commands.py index e86834b29a62c..10ba1e6234d7a 100644 --- a/taskcluster/mach_commands.py +++ b/taskcluster/mach_commands.py @@ -301,7 +301,10 @@ def taskgraph_decision(command_context, **options): "name": "decision", "value": end - start, "lowerIsBetter": True, - "shouldAlert": True, + "monitor": True, + "alertNotifyEmails": [ + "release+gecko-decision-alerts@mozilla.com" + ], "subtests": [], } ], diff --git a/testing/mozharness/scripts/release/generate-checksums.py b/testing/mozharness/scripts/release/generate-checksums.py index 8c576cfda4768..99bb4f59caf14 100644 --- a/testing/mozharness/scripts/release/generate-checksums.py +++ b/testing/mozharness/scripts/release/generate-checksums.py @@ -73,7 +73,7 @@ def __init__(self): require_config_file=False, config={ "virtualenv_modules": [ - "boto", + "boto3", ], "virtualenv_path": "venv", }, @@ -140,12 +140,18 @@ def _get_hash_function(self, format_): def _get_bucket(self): self.activate_virtualenv() - from boto import connect_s3 + import boto3 + from botocore import UNSIGNED + from botocore.client import Config self.info("Connecting to S3") - conn = connect_s3(anon=True, host="storage.googleapis.com") + conn = boto3.resource( + "s3", + config=Config(signature_version=UNSIGNED), + endpoint_url="https://storage.googleapis.com", + ) self.info("Connecting to bucket {}".format(self.config["bucket_name"])) - self.bucket = conn.get_bucket(self.config["bucket_name"]) + self.bucket = conn.Bucket(self.config["bucket_name"]) return self.bucket def collect_individual_checksums(self): @@ -160,19 +166,19 @@ def collect_individual_checksums(self): def worker(item): self.debug(f"Downloading {item}") - sums = bucket.get_key(item).get_contents_as_string() - raw_checksums.append(sums) + response = item.get() + raw_checksums.append(response["Body"].read()) def find_checksums_files(): self.info("Getting key names from bucket") checksum_files = {"beets": [], "checksums": []} - for key in bucket.list(prefix=self.file_prefix): + for key in bucket.objects.filter(Prefix=self.file_prefix): if key.key.endswith(".checksums"): self.debug(f"Found checksums file: {key.key}") - checksum_files["checksums"].append(key.key) + checksum_files["checksums"].append(key) elif key.key.endswith(".beet"): self.debug(f"Found beet file: {key.key}") - checksum_files["beets"].append(key.key) + checksum_files["beets"].append(key) else: self.debug(f"Ignoring non-checksums file: {key.key}") if checksum_files["beets"]: diff --git a/testing/web-platform/meta/clear-site-data/clear-cache-partitioning.tentative.https.html.ini b/testing/web-platform/meta/clear-site-data/clear-cache-partitioning.tentative.https.html.ini index e333bda017919..145c727556da6 100644 --- a/testing/web-platform/meta/clear-site-data/clear-cache-partitioning.tentative.https.html.ini +++ b/testing/web-platform/meta/clear-site-data/clear-cache-partitioning.tentative.https.html.ini @@ -23,16 +23,18 @@ if (os == "mac") and not debug: [OK, TIMEOUT] [clear in cross origin iframe doesn't affect embedder] expected: - if (os == "linux") and not debug and not asan: [PASS, FAIL] - if (os == "android") and sessionHistoryInParent and not debug: [PASS, FAIL] - if (os == "android") and not sessionHistoryInParent and not debug: [PASS, FAIL] + if (os == "linux") and not tsan and not debug and not asan: [PASS, FAIL] + if (os == "android") and not debug and sessionHistoryInParent: [PASS, FAIL] + if (os == "android") and not debug and not sessionHistoryInParent: [PASS, FAIL] + if (os == "linux") and tsan: [PASS, FAIL] [cross origin iframe data doesn't get cleared] expected: - if (os == "linux") and not debug and fission: [PASS, FAIL] - if (os == "android") and sessionHistoryInParent and debug: [FAIL, PASS] - if (os == "android") and sessionHistoryInParent and not debug: [PASS, FAIL] - if (os == "android") and not sessionHistoryInParent and debug: [FAIL, PASS] + if (os == "linux") and not debug and not fission: PASS + if (os == "linux") and debug: PASS + if os == "win": PASS + if os == "mac": PASS + [PASS, FAIL] [clear double partitioned context with intermediate cross origin clears that partitioned data] expected: @@ -48,5 +50,4 @@ [clearing cache doesn't affect subdomain] expected: - if (os == "android") and not debug and sessionHistoryInParent: [PASS, FAIL] - if (os == "android") and not debug and not sessionHistoryInParent: [PASS, FAIL] + if (os == "android") and not debug: [PASS, FAIL] diff --git a/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini b/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini index 052ffdcc378c6..e9f719e7aee98 100644 --- a/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini +++ b/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini @@ -43,6 +43,7 @@ [test paragraph text with line breaks: "\\r\\n"] expected: if (os == "android") and debug and sessionHistoryInParent: [PASS, FAIL] + if (os == "android") and not debug and sessionHistoryInParent: [PASS, FAIL] [test paragraph text with line breaks: "\\n\\r"] expected: @@ -52,6 +53,10 @@ expected: if not sessionHistoryInParent and not debug: [PASS, FAIL] + [test paragraph text with line breaks: "\\r"] + expected: + if (os == "android") and sessionHistoryInParent and not debug: [PASS, FAIL] + [clipboard-copy-selection-line-break.https.html?11-20] [test preformatted text with line breaks: "\\n\\n"] diff --git a/testing/web-platform/meta/compression/compression-bad-chunks.any.js.ini b/testing/web-platform/meta/compression/compression-bad-chunks.any.js.ini index 0cf1b9289b2fe..22face52b6cce 100644 --- a/testing/web-platform/meta/compression/compression-bad-chunks.any.js.ini +++ b/testing/web-platform/meta/compression/compression-bad-chunks.any.js.ini @@ -1,5 +1,6 @@ [compression-bad-chunks.any.sharedworker.html] expected: + if sessionHistoryInParent and (os == "android") and not debug: [OK, TIMEOUT] if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [compression-bad-chunks.any.serviceworker.html] @@ -20,7 +21,6 @@ [compression-bad-chunks.any.shadowrealm-in-window.html] expected: if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] - if (os == "android") and not sessionHistoryInParent and debug: TIMEOUT ERROR [compression-bad-chunks.any.shadowrealm-in-sharedworker.html] diff --git a/testing/web-platform/meta/compression/compression-including-empty-chunk.any.js.ini b/testing/web-platform/meta/compression/compression-including-empty-chunk.any.js.ini index d498474fff88d..b846fa250d7bc 100644 --- a/testing/web-platform/meta/compression/compression-including-empty-chunk.any.js.ini +++ b/testing/web-platform/meta/compression/compression-including-empty-chunk.any.js.ini @@ -23,7 +23,9 @@ expected: ERROR [compression-including-empty-chunk.any.shadowrealm-in-sharedworker.html] - expected: ERROR + expected: + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [compression-including-empty-chunk.https.any.shadowrealm-in-audioworklet.html] expected: ERROR diff --git a/testing/web-platform/meta/compression/decompression-correct-input.any.js.ini b/testing/web-platform/meta/compression/decompression-correct-input.any.js.ini index 7e76d2b850b67..727cabffc40f3 100644 --- a/testing/web-platform/meta/compression/decompression-correct-input.any.js.ini +++ b/testing/web-platform/meta/compression/decompression-correct-input.any.js.ini @@ -25,12 +25,13 @@ [decompression-correct-input.any.shadowrealm-in-shadowrealm.html] expected: - if sessionHistoryInParent and (os == "android") and not debug: [ERROR, TIMEOUT] - if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + if (os == "android") and not debug: [ERROR, TIMEOUT] ERROR [decompression-correct-input.any.shadowrealm-in-window.html] - expected: ERROR + expected: + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [decompression-correct-input.https.any.shadowrealm-in-audioworklet.html] expected: ERROR diff --git a/testing/web-platform/meta/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.ini b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.ini new file mode 100644 index 0000000000000..143f3cdb9275f --- /dev/null +++ b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.ini @@ -0,0 +1,18 @@ +[link_rel_modulepreload.sub.window.html] + [Modulepreload to http://www.web-platform.test:8000 fails.] + expected: FAIL + + [Modulepreload to http://www1.web-platform.test:8000 fails.] + expected: FAIL + + [Modulepreload to http://www2.web-platform.test:8000 fails.] + expected: FAIL + + [Modulepreload to http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000 fails.] + expected: FAIL + + [Modulepreload to http://xn--lve-6lad.web-platform.test:8000 fails.] + expected: FAIL + + [Modulepreload to http://not-web-platform.test:8000 fails.] + expected: FAIL diff --git a/testing/web-platform/meta/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.ini b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.ini new file mode 100644 index 0000000000000..6171dbbb2b35b --- /dev/null +++ b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.ini @@ -0,0 +1,18 @@ +[link_rel_prefetch.sub.window.html] + [Prefetch to http://www.web-platform.test:8000 fails.] + expected: FAIL + + [Prefetch to http://www1.web-platform.test:8000 fails.] + expected: FAIL + + [Prefetch to http://www2.web-platform.test:8000 fails.] + expected: FAIL + + [Prefetch to http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000 fails.] + expected: FAIL + + [Prefetch to http://xn--lve-6lad.web-platform.test:8000 fails.] + expected: FAIL + + [Prefetch to http://not-web-platform.test:8000 fails.] + expected: FAIL diff --git a/testing/web-platform/meta/connection-allowlist/tentative/link_rel_preload.sub.window.js.ini b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_preload.sub.window.js.ini new file mode 100644 index 0000000000000..77b3dd97fa186 --- /dev/null +++ b/testing/web-platform/meta/connection-allowlist/tentative/link_rel_preload.sub.window.js.ini @@ -0,0 +1,18 @@ +[link_rel_preload.sub.window.html] + [Preload to http://www.web-platform.test:8000 fails.] + expected: FAIL + + [Preload to http://www1.web-platform.test:8000 fails.] + expected: FAIL + + [Preload to http://www2.web-platform.test:8000 fails.] + expected: FAIL + + [Preload to http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000 fails.] + expected: FAIL + + [Preload to http://xn--lve-6lad.web-platform.test:8000 fails.] + expected: FAIL + + [Preload to http://not-web-platform.test:8000 fails.] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-animations/parsing/keyframes-name-invalid.html.ini b/testing/web-platform/meta/css/css-animations/parsing/keyframes-name-invalid.html.ini deleted file mode 100644 index 0d45c2949cd91..0000000000000 --- a/testing/web-platform/meta/css/css-animations/parsing/keyframes-name-invalid.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[keyframes-name-invalid.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html.ini new file mode 100644 index 0000000000000..e5ceea5010a07 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html.ini new file mode 100644 index 0000000000000..b0998ed693595 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html.ini new file mode 100644 index 0000000000000..9c9d5a3c4845e --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html.ini new file mode 100644 index 0000000000000..1c6689d2b9d33 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-004a.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html.ini new file mode 100644 index 0000000000000..71560f4417cc6 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-004b.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html.ini new file mode 100644 index 0000000000000..9e252fb8b6750 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-005.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html.ini new file mode 100644 index 0000000000000..d630b93662d65 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-cyclic-dependency-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html.ini new file mode 100644 index 0000000000000..166815f1e1744 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-cyclic-dependency-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html.ini new file mode 100644 index 0000000000000..120e29bf5ba59 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-cyclic-dependency-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html.ini new file mode 100644 index 0000000000000..558e5112d511d --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-synthesized-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html.ini new file mode 100644 index 0000000000000..0e85d2a4473ca --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html.ini @@ -0,0 +1,2 @@ +[column-grid-lanes-item-baseline-synthesized-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html.ini new file mode 100644 index 0000000000000..3ebff4e34f48d --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html.ini new file mode 100644 index 0000000000000..3f11fae5ce2f6 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html.ini new file mode 100644 index 0000000000000..c57ff693a903c --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html.ini new file mode 100644 index 0000000000000..9300ec632786a --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-004.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html.ini new file mode 100644 index 0000000000000..b7236af14b325 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-005.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html.ini new file mode 100644 index 0000000000000..7bbee0de69284 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-cyclic-dependency-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html.ini new file mode 100644 index 0000000000000..928a8b283bde7 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-cyclic-dependency-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html.ini b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html.ini new file mode 100644 index 0000000000000..808fbca868c10 --- /dev/null +++ b/testing/web-platform/meta/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html.ini @@ -0,0 +1,2 @@ +[row-grid-lanes-item-baseline-cyclic-dependency-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-view-transitions/new-content-is-inline.html.ini b/testing/web-platform/meta/css/css-view-transitions/new-content-is-inline.html.ini deleted file mode 100644 index f15f73d1dae2e..0000000000000 --- a/testing/web-platform/meta/css/css-view-transitions/new-content-is-inline.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[new-content-is-inline.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-view-transitions/old-content-is-inline.html.ini b/testing/web-platform/meta/css/css-view-transitions/old-content-is-inline.html.ini index 9a2a428031149..c323ebaa807e3 100644 --- a/testing/web-platform/meta/css/css-view-transitions/old-content-is-inline.html.ini +++ b/testing/web-platform/meta/css/css-view-transitions/old-content-is-inline.html.ini @@ -1,4 +1,2 @@ [old-content-is-inline.html] bug: Fuzz - expected: - if os == "win": FAIL diff --git a/testing/web-platform/meta/css/cssom-view/scroll-behavior-main-frame-window.html.ini b/testing/web-platform/meta/css/cssom-view/scroll-behavior-main-frame-window.html.ini index de51492f31479..4317d94749e69 100644 --- a/testing/web-platform/meta/css/cssom-view/scroll-behavior-main-frame-window.html.ini +++ b/testing/web-platform/meta/css/cssom-view/scroll-behavior-main-frame-window.html.ini @@ -1,16 +1,17 @@ [scroll-behavior-main-frame-window.html] [Main frame with smooth scroll-behavior ; scroll() with default behavior] expected: - if not fission and (os == "linux") and debug: [PASS, FAIL] + if (os == "linux") and debug and not fission: [PASS, FAIL] [Main frame with auto scroll-behavior ; scroll() with smooth behavior] expected: - if not fission and (os == "linux") and debug: [PASS, FAIL] + if (os == "linux") and debug and not fission: [PASS, FAIL] [Main frame with smooth scroll-behavior ; scrollTo() with auto behavior] expected: - if not fission and (os == "linux") and debug: [PASS, FAIL] + if (os == "win") and debug and (processor == "x86"): [PASS, FAIL] + if (os == "linux") and debug and not fission: [PASS, FAIL] [Main frame with smooth scroll-behavior ; scroll() with auto behavior] expected: - if not fission and (os == "linux") and debug: [PASS, FAIL] + if (os == "linux") and debug and not fission: [PASS, FAIL] diff --git a/testing/web-platform/meta/custom-elements/registries/element-mutation.html.ini b/testing/web-platform/meta/custom-elements/registries/element-mutation.html.ini index e41633e83635d..cdb7e62d462b8 100644 --- a/testing/web-platform/meta/custom-elements/registries/element-mutation.html.ini +++ b/testing/web-platform/meta/custom-elements/registries/element-mutation.html.ini @@ -1,12 +1,30 @@ [element-mutation.html] - [An element with global registry should not change its registry when moved into a shadow tree with scoped registry.] + [Declarative shadow DOM with shadowrootcustomelementregistry attribute without registry initialized should remain null registry after adoption.] expected: FAIL - [An element with scoped registry should not change its registry when moved out of the shadow tree.] + [An element with global registry should not change its registry when run append into a shadow tree with scoped registry.] expected: FAIL - [An element with scoped registry should not change its registry when moved into another shadow tree with different scoped registry.] + [An element with scoped registry should not change its registry when run append out of the shadow tree.] expected: FAIL - [Declarative shadow DOM with shadowrootcustomelementregistry attribute without registry initialized should remain null registry after adoption.] + [An element with scoped registry should not change its registry when run append into another shadow tree with different scoped registry.] + expected: FAIL + + [An element with global registry should not change its registry when run appendChild into a shadow tree with scoped registry.] + expected: FAIL + + [An element with scoped registry should not change its registry when run appendChild out of the shadow tree.] + expected: FAIL + + [An element with scoped registry should not change its registry when run appendChild into another shadow tree with different scoped registry.] + expected: FAIL + + [An element with global registry should not change its registry when run prepend into a shadow tree with scoped registry.] + expected: FAIL + + [An element with scoped registry should not change its registry when run prepend out of the shadow tree.] + expected: FAIL + + [An element with scoped registry should not change its registry when run prepend into another shadow tree with different scoped registry.] expected: FAIL diff --git a/testing/web-platform/meta/dom/abort/timeout-shadowrealm.any.js.ini b/testing/web-platform/meta/dom/abort/timeout-shadowrealm.any.js.ini index 8d43f2f37de0b..4c92a3ca5fbe3 100644 --- a/testing/web-platform/meta/dom/abort/timeout-shadowrealm.any.js.ini +++ b/testing/web-platform/meta/dom/abort/timeout-shadowrealm.any.js.ini @@ -13,8 +13,8 @@ [timeout-shadowrealm.any.shadowrealm-in-dedicatedworker.html] expected: - if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] - ERROR + if sessionHistoryInParent: ERROR + [ERROR, TIMEOUT] [timeout-shadowrealm.https.any.shadowrealm-in-audioworklet.html] expected: diff --git a/testing/web-platform/meta/dom/observable/tentative/crashtests/observable-gc.any.js.ini b/testing/web-platform/meta/dom/observable/tentative/crashtests/observable-gc.any.js.ini new file mode 100644 index 0000000000000..1111f1d3fc564 --- /dev/null +++ b/testing/web-platform/meta/dom/observable/tentative/crashtests/observable-gc.any.js.ini @@ -0,0 +1,50 @@ +[observable-gc.any.html] + [toArray(): does not crash on observable being GCed] + expected: FAIL + + [forEach(): does not crash on observable being GCed] + expected: FAIL + + [first(): does not crash on observable being GCed] + expected: FAIL + + [last(): does not crash on observable being GCed] + expected: FAIL + + [some(): does not crash on observable being GCed] + expected: FAIL + + [every(): does not crash on observable being GCed] + expected: FAIL + + [find(): does not crash on observable being GCed] + expected: FAIL + + [reduce(): does not crash on observable being GCed] + expected: FAIL + + +[observable-gc.any.worker.html] + [toArray(): does not crash on observable being GCed] + expected: FAIL + + [forEach(): does not crash on observable being GCed] + expected: FAIL + + [first(): does not crash on observable being GCed] + expected: FAIL + + [last(): does not crash on observable being GCed] + expected: FAIL + + [some(): does not crash on observable being GCed] + expected: FAIL + + [every(): does not crash on observable being GCed] + expected: FAIL + + [find(): does not crash on observable being GCed] + expected: FAIL + + [reduce(): does not crash on observable being GCed] + expected: FAIL diff --git a/testing/web-platform/meta/event-timing/gap-keydown-keyup.html.ini b/testing/web-platform/meta/event-timing/gap-keydown-keyup.html.ini index 44f3ace7ed20e..07d4593e79afe 100644 --- a/testing/web-platform/meta/event-timing/gap-keydown-keyup.html.ini +++ b/testing/web-platform/meta/event-timing/gap-keydown-keyup.html.ini @@ -1,6 +1,6 @@ [gap-keydown-keyup.html] [keydown duration shouldn't wait for keyup.] expected: - if (os == "win") and not debug and (processor == "x86_64"): [PASS, FAIL] - if (os == "mac") and not debug: [PASS, FAIL] - if (os == "android") and not debug: [PASS, FAIL] + if not debug and (os == "linux"): PASS + if debug: PASS + [PASS, FAIL] diff --git a/testing/web-platform/meta/fedcm/fedcm-login-status/cross-origin-status.https.html.ini b/testing/web-platform/meta/fedcm/fedcm-login-status/cross-origin-status.https.html.ini index 51e6250c0865a..7dc169ff2037e 100644 --- a/testing/web-platform/meta/fedcm/fedcm-login-status/cross-origin-status.https.html.ini +++ b/testing/web-platform/meta/fedcm/fedcm-login-status/cross-origin-status.https.html.ini @@ -1,6 +1,6 @@ [cross-origin-status.https.html] expected: - if (os == "win") and not debug and (processor == "x86_64"): [ERROR, TIMEOUT] + if (os == "win") and not debug and (processor == "x86_64"): [TIMEOUT, ERROR] if (os == "win") and not debug and (processor == "x86"): [ERROR, TIMEOUT] [Cross-origin same-site status header should work from fetch()] expected: [PASS, FAIL] @@ -19,10 +19,10 @@ [Status header should be ignored from a fenced frame, even if it is same-origin] expected: - if (os == "win") and not debug: [PASS, NOTRUN, TIMEOUT] if (os == "win") and ccov: [PASS, NOTRUN] + if (os == "win") and not debug: [NOTRUN, PASS, TIMEOUT] [Status header should be ignored from cross-site iframe] expected: - if (os == "win") and not debug and (processor == "x86_64"): [PASS, TIMEOUT] - if (os == "win") and not debug and (processor == "x86"): [PASS, TIMEOUT] + if (os == "win") and not debug and (processor == "x86_64"): [TIMEOUT, PASS] + if (os == "win") and not debug and (processor == "x86"): [PASS, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/meta/fedcm/fedcm-login-status/logged-out.https.html.ini b/testing/web-platform/meta/fedcm/fedcm-login-status/logged-out.https.html.ini index 0d9bcbd583709..26b712d2d22cf 100644 --- a/testing/web-platform/meta/fedcm/fedcm-login-status/logged-out.https.html.ini +++ b/testing/web-platform/meta/fedcm/fedcm-login-status/logged-out.https.html.ini @@ -1,3 +1,5 @@ [logged-out.https.html] + expected: + if (processor == "x86") and not debug: [OK, ERROR] [User info request should fail because we are marked as not logged in] expected: FAIL diff --git a/testing/web-platform/meta/html/browsers/the-window-object/window-open-noopener.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/window-open-noopener.html.ini index f8a0811c90256..e5c84fd494f74 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/window-open-noopener.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/window-open-noopener.html.ini @@ -1,12 +1,14 @@ [window-open-noopener.html?_self] expected: - if (os == "win") and (processor == "x86"): [OK, CRASH] + if (os == "win") and (processor == "x86") and debug: [OK, CRASH] + if (os == "win") and (processor == "x86") and not debug: [OK, CRASH] if (os == "android") and not debug: [OK, ERROR] [window-open-noopener.html?indexed] expected: if (processor == "x86_64") and (os == "linux") and debug and not fission: [OK, CRASH] - if processor == "x86": [OK, CRASH] + if (processor == "x86") and debug: [OK, CRASH] + if (processor == "x86") and not debug: [OK, CRASH] [noopener=1 means the same as noopener] expected: FAIL @@ -34,9 +36,9 @@ [window-open-noopener.html?_parent] expected: - if (os == "win") and (processor == "x86"): [CRASH, OK, ERROR] if (os == "android") and not debug: [OK, ERROR] [window-open-noopener.html?_top] expected: + if (os == "win") and debug and (processor == "x86"): [OK, CRASH] if (os == "android") and not debug: [OK, ERROR] diff --git a/testing/web-platform/meta/html/dom/partial-updates/tentative/stream-html-sanitizer.html.ini b/testing/web-platform/meta/html/dom/partial-updates/tentative/stream-html-sanitizer.html.ini new file mode 100644 index 0000000000000..d466585ce36b4 --- /dev/null +++ b/testing/web-platform/meta/html/dom/partial-updates/tentative/stream-html-sanitizer.html.ini @@ -0,0 +1,84 @@ +[stream-html-sanitizer.html] + [element.streamHTMLUnsafe with sanitizer {removeElements}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {elements}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {replaceWithChildrenElements}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {replaceWithChildrenElements} and text] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {attributes}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {removeAttributes}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {element: [{removeAttributes}\]}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer {element: [{attributes}\]}] + expected: FAIL + + [element.streamHTMLUnsafe with sanitizer should sanitize template contents] + expected: FAIL + + [element.streamHTMLUnsafe: sanitizer shouldn't allow escaping template] + expected: FAIL + + [element.streamHTMLUnsafe: sanitizer takes effect after foster parenting] + expected: FAIL + + [element.streamHTMLUnsafe: sanitizer with foster parenting:

] + expected: FAIL + + [element.streamHTMLUnsafe: sanitizer takes effect after adoption agency] + expected: FAIL + + [element.streamHTMLUnsafe: mutating the sanitizer while streaming does not effect the originally given sanitizer] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {removeElements}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {elements}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {replaceWithChildrenElements}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {replaceWithChildrenElements} and text] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {attributes}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {removeAttributes}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {element: [{removeAttributes}\]}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer {element: [{attributes}\]}] + expected: FAIL + + [element.streamAppendHTMLUnsafe with sanitizer should sanitize template contents] + expected: FAIL + + [element.streamAppendHTMLUnsafe: sanitizer shouldn't allow escaping template] + expected: FAIL + + [element.streamAppendHTMLUnsafe: sanitizer takes effect after foster parenting] + expected: FAIL + + [element.streamAppendHTMLUnsafe: sanitizer with foster parenting:

] + expected: FAIL + + [element.streamAppendHTMLUnsafe: sanitizer takes effect after adoption agency] + expected: FAIL + + [element.streamAppendHTMLUnsafe: mutating the sanitizer while streaming does not effect the originally given sanitizer] + expected: FAIL diff --git a/testing/web-platform/meta/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html.ini b/testing/web-platform/meta/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html.ini index bb4e40f7ee792..dd87930133a7c 100644 --- a/testing/web-platform/meta/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html.ini +++ b/testing/web-platform/meta/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html.ini @@ -3,4 +3,5 @@ if (os == "linux") and swgl and debug and fission: [PASS, FAIL] if (os == "win") and debug and not swgl: [PASS, FAIL] if (os == "linux") and swgl and not debug: [PASS, FAIL] + if (os == "android") and debug and not swgl: [PASS, FAIL] if (os == "linux") and not swgl: [PASS, FAIL] diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html.ini index f987935ec778e..e4338d353dee4 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html.ini @@ -1,13 +1,14 @@ implementation-status: backlog [autoplay-allowed-by-feature-policy-attribute.https.sub.html] expected: - if os_version == "14.70": [OK, TIMEOUT] - if os_version == "14": [OK, TIMEOUT] + if os == "mac": [OK, TIMEOUT] + if os == "android": [OK, TIMEOUT] [Feature policy "autoplay" can be enabled in cross-origin iframe using allow="autoplay" attribute] expected: - if os_version == "14.70": [PASS, TIMEOUT] - if os_version == "14": [PASS, TIMEOUT] + if os == "mac": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT] [Feature policy "autoplay" can be enabled in same-origin iframe using allow="autoplay" attribute] expected: if os == "mac": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT] diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.ini index 795a72d70a071..53142aecb9b97 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.ini @@ -1,21 +1,19 @@ implementation-status: backlog [autoplay-allowed-by-feature-policy.https.sub.html] expected: - if (os == "android") and (version == "7.0"): OK if os == "linux": OK [OK, TIMEOUT] [Feature-Policy header: autoplay * allows cross-origin iframes.] expected: - if (os == "android") and (version == "7.0"): PASS if os == "linux": PASS [PASS, TIMEOUT] [Feature-Policy header: autoplay * allows same-origin iframes.] expected: - if (os == "android") and (version == "7.0"): PASS if os == "linux": PASS [PASS, TIMEOUT] [Feature-Policy header: autoplay * allows the top-level document.] expected: if os == "mac": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT] diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html.ini index 196c271a1103c..56729fb1169db 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html.ini @@ -12,6 +12,7 @@ implementation-status: backlog [Default "autoplay" feature policy ["self"\] allows same-origin iframes.] expected: if os == "mac": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT] [Default "autoplay" feature policy ["self"\] allows the top-level document.] expected: diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.ini index 743df5759e2cc..7ecb93700e9c0 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.ini @@ -1,19 +1,21 @@ implementation-status: backlog [autoplay-disabled-by-feature-policy.https.sub.html] expected: - if (os == "android") and (version == "14"): [OK, TIMEOUT] if os == "mac": [OK, TIMEOUT] + if os == "android": [OK, TIMEOUT] [Feature-Policy header: autoplay "none" disallows same-origin iframes.] expected: if os == "mac": [FAIL, TIMEOUT] + if os == "android": [FAIL, TIMEOUT] FAIL [Feature-Policy header: autoplay "none" disallows cross-origin iframes.] expected: - if (os == "android") and (version == "14"): [FAIL, TIMEOUT] if os == "mac": [FAIL, TIMEOUT] + if os == "android": [FAIL, TIMEOUT] FAIL [Feature-Policy header: autoplay "none" has no effect on the top level document.] expected: if os == "mac": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT] diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html.ini index 5e78f1cb44c3c..b086a73315356 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html.ini @@ -1,2 +1,4 @@ [load-events-networkState.html] bug: 1709960 + expected: + if (os == "android") and debug: TIMEOUT diff --git a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html.ini index 02d2396f985e9..fb447ae9e6aff 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html.ini @@ -1,12 +1,13 @@ [resource-selection-candidate-insert-before.html] + expected: + if (os == "android") and debug and not sessionHistoryInParent: [TIMEOUT, OK] + if (os == "android") and not debug and sessionHistoryInParent: [TIMEOUT, OK] + if (os == "android") and not debug and not sessionHistoryInParent: [TIMEOUT, OK] [inserting another source before the candidate] expected: if (os == "win") and (os_version == "11.26100") and ccov: [PASS, FAIL] - if (os == "linux") and not debug and not asan and tsan: [PASS, FAIL] - if (os == "linux") and debug and not fission: [PASS, FAIL] - if (os == "linux") and not debug and asan: [PASS, FAIL] - if (os == "android") and debug and sessionHistoryInParent: [FAIL, PASS] + if (os == "android") and not debug and not sessionHistoryInParent: FAIL if (os == "win") and not debug: FAIL + if (os == "linux") and tsan: [PASS, FAIL] if os == "mac": FAIL - if (os == "android") and not sessionHistoryInParent and not debug: FAIL [FAIL, PASS] diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/modulepreload-referrerpolicy.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/modulepreload-referrerpolicy.html.ini index e97b2738f9166..985a8c6203638 100644 --- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/modulepreload-referrerpolicy.html.ini +++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/modulepreload-referrerpolicy.html.ini @@ -7,6 +7,7 @@ [Modulepreload with strict-origin policy should send origin-only referrer] expected: + if (os == "linux") and not asan and not debug and not tsan: [PASS, FAIL] if (os == "win") and debug and (processor == "x86_64"): [PASS, FAIL] if (os == "linux") and asan and fission: [PASS, FAIL] if (os == "android") and sessionHistoryInParent and debug: [PASS, FAIL] diff --git a/testing/web-platform/meta/mediacapture-record/MediaRecorder-canvas-media-source.https.html.ini b/testing/web-platform/meta/mediacapture-record/MediaRecorder-canvas-media-source.https.html.ini index 837ce8816c2cf..eeed3099f1a1f 100644 --- a/testing/web-platform/meta/mediacapture-record/MediaRecorder-canvas-media-source.https.html.ini +++ b/testing/web-platform/meta/mediacapture-record/MediaRecorder-canvas-media-source.https.html.ini @@ -58,5 +58,6 @@ [MediaRecorder-canvas-media-source.https.html?mimeType=video/mp4;codecs=av01,mp4a.40.2] expected: + if (os == "mac") and debug: [OK, TIMEOUT] if (os == "mac") and not debug: [OK, TIMEOUT] if (os == "linux") and not fission: [OK, CRASH] diff --git a/testing/web-platform/meta/mediacapture-record/MediaRecorder-creation.https.html.ini b/testing/web-platform/meta/mediacapture-record/MediaRecorder-creation.https.html.ini index ceef73fe4cc40..04d9875acf2a2 100644 --- a/testing/web-platform/meta/mediacapture-record/MediaRecorder-creation.https.html.ini +++ b/testing/web-platform/meta/mediacapture-record/MediaRecorder-creation.https.html.ini @@ -1,4 +1,4 @@ [MediaRecorder-creation.https.html] expected: if (os == "linux") and not debug and fission: [OK, CRASH] - if (os == "android") and fission: [OK, TIMEOUT] + if (os == "mac") and debug: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html.ini b/testing/web-platform/meta/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html.ini index 00d68e9d655dc..ba02046155829 100644 --- a/testing/web-platform/meta/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html.ini +++ b/testing/web-platform/meta/mediacapture-record/MediaRecorder-peerconnection-no-sink.https.html.ini @@ -1,11 +1,10 @@ [MediaRecorder-peerconnection-no-sink.https.html] expected: if (os == "android") and release_or_beta: OK - if (os_version == "24.04") and not debug and fission: [OK, CRASH] - if (os_version == "14.70") and not debug: [OK, TIMEOUT] - if os_version == "7.0": [OK, TIMEOUT] - if os_version == "18.04": [OK, CRASH] - if os_version == "14": [OK, TIMEOUT] + if (os == "linux") and not debug and fission: [OK, CRASH] + if (os == "mac") and debug: [OK, TIMEOUT] + if (os == "mac") and not debug: [OK, TIMEOUT] + if os == "android": [OK, TIMEOUT] [MediaRecorder records from PeerConnection without sinks, {"video":{}}] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1669551 expected: diff --git a/testing/web-platform/meta/mozilla-sync b/testing/web-platform/meta/mozilla-sync index 39259576aa7b3..984c0ede061a2 100644 --- a/testing/web-platform/meta/mozilla-sync +++ b/testing/web-platform/meta/mozilla-sync @@ -1 +1 @@ -upstream: 690f95ba24fdbe74cb0085793c448a4a989e5ed7 +upstream: 10bbfe6bcf96c8df39cd2d759fd33cbe05e99cbc diff --git a/testing/web-platform/meta/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html.ini b/testing/web-platform/meta/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html.ini index 2685882fb3e2a..131e008c4fccf 100644 --- a/testing/web-platform/meta/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html.ini +++ b/testing/web-platform/meta/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html.ini @@ -8,6 +8,7 @@ [pointerevent_after_target_removed_interleaved.tentative.html?touch] expected: + if (os == "win") and debug and (processor == "x86"): [OK, TIMEOUT] if (os == "linux") and debug: [OK, TIMEOUT] [mouse events from touch received before/after child removal at pointerdown] expected: @@ -16,6 +17,7 @@ [mouse events from touch received before/after child removal at pointerup] expected: + if (os == "win") and debug and (processor == "x86"): [FAIL, TIMEOUT] if (os == "linux") and debug: [FAIL, NOTRUN] FAIL diff --git a/testing/web-platform/meta/pointerevents/pointerevent_click_is_a_pointerevent.html.ini b/testing/web-platform/meta/pointerevents/pointerevent_click_is_a_pointerevent.html.ini index 6129c88fc9836..29d46a9660b83 100644 --- a/testing/web-platform/meta/pointerevents/pointerevent_click_is_a_pointerevent.html.ini +++ b/testing/web-platform/meta/pointerevents/pointerevent_click_is_a_pointerevent.html.ini @@ -13,16 +13,16 @@ [pointerevent_click_is_a_pointerevent.html?touch] expected: - if (os == "win") and debug and (processor == "x86"): [OK, TIMEOUT] - if (os == "linux") and debug and not fission: [OK, TIMEOUT] + if (processor == "x86_64") and (os == "linux") and debug and not fission: [OK, TIMEOUT] + if (processor == "x86") and debug: [OK, TIMEOUT] [click using touch is a PointerEvent with correct properties] expected: if (os == "linux") and debug and not fission: [PASS, TIMEOUT] [click using touch is a PointerEvent with correct properties in a subframe] expected: - if (os == "win") and debug and (processor == "x86"): [PASS, NOTRUN] - if (os == "linux") and debug and not fission: [PASS, NOTRUN] + if (processor == "x86_64") and (os == "linux") and debug and not fission: [PASS, NOTRUN] + if (processor == "x86") and debug: [PASS, TIMEOUT, NOTRUN] [click using touch is a PointerEvent with correct properties when no other PointerEvent listeners are present] expected: diff --git a/testing/web-platform/meta/streams/piping/flow-control.any.js.ini b/testing/web-platform/meta/streams/piping/flow-control.any.js.ini index bb382831b6546..ca59d97eb8c8f 100644 --- a/testing/web-platform/meta/streams/piping/flow-control.any.js.ini +++ b/testing/web-platform/meta/streams/piping/flow-control.any.js.ini @@ -11,6 +11,8 @@ if (os == "android") and debug and sessionHistoryInParent: [OK, TIMEOUT] [flow-control.any.html] + expected: + if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [flow-control.any.shadowrealm.html] expected: ERROR @@ -32,7 +34,7 @@ [flow-control.any.shadowrealm-in-dedicatedworker.html] expected: - if (os == "win") and not debug and (processor == "x86_64"): [ERROR, CRASH] + if (os == "win") and not debug: [ERROR, CRASH] ERROR [flow-control.any.shadowrealm-in-shadowrealm.html] diff --git a/testing/web-platform/meta/streams/piping/general-addition.any.js.ini b/testing/web-platform/meta/streams/piping/general-addition.any.js.ini index 02a9a40f6e403..e1e5ed398390e 100644 --- a/testing/web-platform/meta/streams/piping/general-addition.any.js.ini +++ b/testing/web-platform/meta/streams/piping/general-addition.any.js.ini @@ -18,7 +18,9 @@ expected: [ERROR, TIMEOUT] [general-addition.any.shadowrealm-in-sharedworker.html] - expected: ERROR + expected: + if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [general-addition.any.shadowrealm-in-shadowrealm.html] expected: diff --git a/testing/web-platform/meta/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js.ini b/testing/web-platform/meta/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js.ini index 255f525b2da74..feeeed9aa8189 100644 --- a/testing/web-platform/meta/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js.ini +++ b/testing/web-platform/meta/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js.ini @@ -1,4 +1,6 @@ [enqueue-with-detached-buffer.any.sharedworker.html] + expected: + if (os == "android") and sessionHistoryInParent and not debug: [OK, TIMEOUT] [enqueue-with-detached-buffer.any.worker.html] expected: @@ -32,7 +34,8 @@ [enqueue-with-detached-buffer.any.shadowrealm-in-shadowrealm.html] expected: - if not sessionHistoryInParent and debug: [ERROR, TIMEOUT] + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + if (os == "android") and not sessionHistoryInParent and debug: [ERROR, TIMEOUT] ERROR [enqueue-with-detached-buffer.any.shadowrealm-in-window.html] diff --git a/testing/web-platform/meta/streams/readable-byte-streams/general.any.js.ini b/testing/web-platform/meta/streams/readable-byte-streams/general.any.js.ini index fdd3a163091e0..cdda6edfeed0c 100644 --- a/testing/web-platform/meta/streams/readable-byte-streams/general.any.js.ini +++ b/testing/web-platform/meta/streams/readable-byte-streams/general.any.js.ini @@ -11,6 +11,7 @@ [general.any.serviceworker.html] expected: + if sessionHistoryInParent and (os == "android") and not debug: [OK, TIMEOUT] if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [general.any.sharedworker.html] @@ -40,8 +41,8 @@ [general.any.shadowrealm-in-window.html] expected: - if (os == "android") and debug and sessionHistoryInParent: [ERROR, TIMEOUT] - if (os == "android") and not debug and not sessionHistoryInParent: [ERROR, TIMEOUT] + if (os == "android") and sessionHistoryInParent and debug: [ERROR, TIMEOUT] + if (os == "android") and not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] ERROR [general.https.any.shadowrealm-in-audioworklet.html] diff --git a/testing/web-platform/meta/streams/readable-byte-streams/non-transferable-buffers.any.js.ini b/testing/web-platform/meta/streams/readable-byte-streams/non-transferable-buffers.any.js.ini index 5fb1bff658a07..d2b7c14b3a47d 100644 --- a/testing/web-platform/meta/streams/readable-byte-streams/non-transferable-buffers.any.js.ini +++ b/testing/web-platform/meta/streams/readable-byte-streams/non-transferable-buffers.any.js.ini @@ -31,9 +31,7 @@ expected: ERROR [non-transferable-buffers.https.any.shadowrealm-in-audioworklet.html] - expected: - if (os == "android") and debug and sessionHistoryInParent: TIMEOUT - ERROR + expected: ERROR [non-transferable-buffers.any.shadowrealm-in-shadowrealm.html] expected: ERROR @@ -44,4 +42,6 @@ ERROR [non-transferable-buffers.any.shadowrealm-in-sharedworker.html] - expected: ERROR + expected: + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR diff --git a/testing/web-platform/meta/streams/readable-byte-streams/patched-global.any.js.ini b/testing/web-platform/meta/streams/readable-byte-streams/patched-global.any.js.ini index d5ee54c2e7f60..6220a0db55798 100644 --- a/testing/web-platform/meta/streams/readable-byte-streams/patched-global.any.js.ini +++ b/testing/web-platform/meta/streams/readable-byte-streams/patched-global.any.js.ini @@ -8,8 +8,7 @@ [patched-global.any.shadowrealm-in-dedicatedworker.html] expected: - if (os == "android") and debug and sessionHistoryInParent: [ERROR, TIMEOUT] - if (os == "android") and debug and not sessionHistoryInParent: [ERROR, TIMEOUT] + if (os == "android") and debug: [ERROR, TIMEOUT] ERROR [patched-global.https.any.shadowrealm-in-audioworklet.html] @@ -24,7 +23,9 @@ expected: ERROR [patched-global.https.any.shadowrealm-in-serviceworker.html] - expected: ERROR + expected: + if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [patched-global.any.sharedworker.html] expected: diff --git a/testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.any.js.ini b/testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.any.js.ini index 43b642f89abba..5f6b5321ddd46 100644 --- a/testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.any.js.ini +++ b/testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.any.js.ini @@ -8,11 +8,10 @@ [floating-point-total-queue-size.any.sharedworker.html] expected: + if (os == "android") and sessionHistoryInParent and debug: TIMEOUT if (os == "android") and sessionHistoryInParent and not debug: [OK, TIMEOUT] [floating-point-total-queue-size.any.html] - expected: - if not sessionHistoryInParent and debug: TIMEOUT [floating-point-total-queue-size.any.shadowrealm.html] expected: diff --git a/testing/web-platform/meta/streams/readable-streams/global.html.ini b/testing/web-platform/meta/streams/readable-streams/global.html.ini index 7611922f98f9e..20496b3e12905 100644 --- a/testing/web-platform/meta/streams/readable-streams/global.html.ini +++ b/testing/web-platform/meta/streams/readable-streams/global.html.ini @@ -1,3 +1,4 @@ [global.html] expected: + if sessionHistoryInParent and (os == "android") and debug: [OK, TIMEOUT] if not sessionHistoryInParent and not debug: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/streams/transform-streams/errors.any.js.ini b/testing/web-platform/meta/streams/transform-streams/errors.any.js.ini index ad923d9de6abf..606cbb19a2bcb 100644 --- a/testing/web-platform/meta/streams/transform-streams/errors.any.js.ini +++ b/testing/web-platform/meta/streams/transform-streams/errors.any.js.ini @@ -36,6 +36,7 @@ [errors.any.shadowrealm-in-shadowrealm.html] expected: + if (os == "android") and debug and not sessionHistoryInParent: TIMEOUT if (os == "win") and not debug: [ERROR, CRASH] ERROR diff --git a/testing/web-platform/meta/streams/transform-streams/terminate.any.js.ini b/testing/web-platform/meta/streams/transform-streams/terminate.any.js.ini index 1fbe190445511..312bea4f59275 100644 --- a/testing/web-platform/meta/streams/transform-streams/terminate.any.js.ini +++ b/testing/web-platform/meta/streams/transform-streams/terminate.any.js.ini @@ -19,7 +19,9 @@ ERROR [terminate.any.shadowrealm-in-shadowrealm.html] - expected: ERROR + expected: + if not sessionHistoryInParent and debug: TIMEOUT + ERROR [terminate.https.any.shadowrealm-in-serviceworker.html] expected: [ERROR, TIMEOUT] diff --git a/testing/web-platform/meta/streams/writable-streams/error.any.js.ini b/testing/web-platform/meta/streams/writable-streams/error.any.js.ini index 89c1d65582de2..826b7604df55c 100644 --- a/testing/web-platform/meta/streams/writable-streams/error.any.js.ini +++ b/testing/web-platform/meta/streams/writable-streams/error.any.js.ini @@ -1,7 +1,7 @@ [error.any.serviceworker.html] expected: if (os == "android") and not debug and sessionHistoryInParent: [OK, ERROR, TIMEOUT] - if (os == "android") and not debug and not sessionHistoryInParent: [OK, ERROR] + if (os == "android") and not debug and not sessionHistoryInParent: [OK, TIMEOUT, ERROR] if (os == "mac") and not debug: [OK, ERROR] [error.any.worker.html] @@ -12,9 +12,9 @@ [error.any.sharedworker.html] expected: - if (os == "android") and debug and not sessionHistoryInParent: [OK, TIMEOUT] - if (os == "android") and not debug and sessionHistoryInParent: [OK, CRASH] - if (os == "android") and not debug and not sessionHistoryInParent: [OK, TIMEOUT, CRASH] + if sessionHistoryInParent and (os == "android") and not debug: [OK, CRASH] + if not sessionHistoryInParent and debug: [OK, TIMEOUT] + if not sessionHistoryInParent and not debug: [OK, TIMEOUT, CRASH] [error.any.shadowrealm.html] expected: diff --git a/testing/web-platform/meta/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js.ini b/testing/web-platform/meta/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js.ini index 08bc281459f46..382706c53769b 100644 --- a/testing/web-platform/meta/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js.ini +++ b/testing/web-platform/meta/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js.ini @@ -19,14 +19,16 @@ expected: ERROR [js-wasm-cycle.tentative.any.html] + expected: + if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [Check bindings in JavaScript and WebAssembly cycle (JS higher)] expected: FAIL + [js-wasm-cycle.tentative.any.js] [Check bindings in JavaScript and WebAssembly cycle (JS higher)] expected: FAIL + [js-wasm-cycle.tentative.https.any.shadowrealm-in-serviceworker.html] - expected: - if (os == "android") and debug and sessionHistoryInParent: TIMEOUT - ERROR + expected: ERROR diff --git a/testing/web-platform/meta/wasm/jsapi/instance/constructor.any.js.ini b/testing/web-platform/meta/wasm/jsapi/instance/constructor.any.js.ini index fb1eec3360d09..67e8a94574f6a 100644 --- a/testing/web-platform/meta/wasm/jsapi/instance/constructor.any.js.ini +++ b/testing/web-platform/meta/wasm/jsapi/instance/constructor.any.js.ini @@ -3,6 +3,8 @@ if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [constructor.any.worker.html] + expected: + if (os == "android") and sessionHistoryInParent and not debug: [OK, TIMEOUT] [constructor.any.shadowrealm.html] expected: ERROR diff --git a/testing/web-platform/meta/wasm/jsapi/instance/toString.any.js.ini b/testing/web-platform/meta/wasm/jsapi/instance/toString.any.js.ini index dfa1daa5c9905..63d89a5cc69d8 100644 --- a/testing/web-platform/meta/wasm/jsapi/instance/toString.any.js.ini +++ b/testing/web-platform/meta/wasm/jsapi/instance/toString.any.js.ini @@ -27,4 +27,6 @@ expected: ERROR [toString.any.shadowrealm-in-shadowrealm.html] - expected: ERROR + expected: + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR diff --git a/testing/web-platform/meta/wasm/jsapi/module/moduleSource.tentative.any.js.ini b/testing/web-platform/meta/wasm/jsapi/module/moduleSource.tentative.any.js.ini index 222976f206c9c..570e6b1160416 100644 --- a/testing/web-platform/meta/wasm/jsapi/module/moduleSource.tentative.any.js.ini +++ b/testing/web-platform/meta/wasm/jsapi/module/moduleSource.tentative.any.js.ini @@ -22,7 +22,7 @@ [moduleSource.tentative.any.worker.html] expected: - if (os == "win") and not debug and (processor == "x86_64"): [OK, CRASH] + if (os == "win") and not debug: [OK, CRASH] if (os == "android") and debug: [OK, TIMEOUT] [AbstractModuleSource intrinsic] expected: FAIL @@ -51,7 +51,9 @@ expected: [ERROR, TIMEOUT] [moduleSource.tentative.any.shadowrealm-in-window.html] - expected: ERROR + expected: + if (os == "android") and sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [moduleSource.tentative.https.any.shadowrealm-in-audioworklet.html] expected: diff --git a/testing/web-platform/meta/wasm/jsapi/tag/toString.tentative.any.js.ini b/testing/web-platform/meta/wasm/jsapi/tag/toString.tentative.any.js.ini index 92e5ff50d3a12..0929660648111 100644 --- a/testing/web-platform/meta/wasm/jsapi/tag/toString.tentative.any.js.ini +++ b/testing/web-platform/meta/wasm/jsapi/tag/toString.tentative.any.js.ini @@ -4,6 +4,7 @@ [toString.tentative.any.html] expected: + if (os == "android") and debug and sessionHistoryInParent: TIMEOUT if (os == "android") and not debug: [OK, TIMEOUT] [toString.tentative.any.shadowrealm.html] @@ -27,8 +28,7 @@ [toString.tentative.any.shadowrealm-in-window.html] expected: - if not sessionHistoryInParent and debug: TIMEOUT - if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + if not sessionHistoryInParent: [ERROR, TIMEOUT] ERROR [toString.tentative.any.shadowrealm-in-sharedworker.html] diff --git a/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-alternate.tentative.html.ini b/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-alternate.tentative.html.ini deleted file mode 100644 index 37c315d7333a4..0000000000000 --- a/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-alternate.tentative.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[event-trigger-alternate.tentative.html] - [Basic event-based animation-trigger with 'alternate' behavior] - expected: FAIL diff --git a/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-state.tentative.html.ini b/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-state.tentative.html.ini deleted file mode 100644 index 93cc7304bf182..0000000000000 --- a/testing/web-platform/meta/web-animations/animation-trigger/event-trigger-state.tentative.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[event-trigger-state.tentative.html] - [Basic event-based animation-trigger with 'state' behavior] - expected: FAIL diff --git a/testing/web-platform/meta/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-rendersizehint.html.ini b/testing/web-platform/meta/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-rendersizehint.html.ini index 6afb4793d0841..0b2a99172d171 100644 --- a/testing/web-platform/meta/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-rendersizehint.html.ini +++ b/testing/web-platform/meta/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-rendersizehint.html.ini @@ -1,4 +1,6 @@ [audiocontext-rendersizehint.html] + expected: + if not sessionHistoryInParent and not debug: [OK, TIMEOUT] [AudioContext with no renderSizeHint] expected: FAIL diff --git a/testing/web-platform/meta/webcodecs/video-encoder-h26x-annexb.https.any.js.ini b/testing/web-platform/meta/webcodecs/video-encoder-h26x-annexb.https.any.js.ini index 55599a37e6bad..34dba6e737a04 100644 --- a/testing/web-platform/meta/webcodecs/video-encoder-h26x-annexb.https.any.js.ini +++ b/testing/web-platform/meta/webcodecs/video-encoder-h26x-annexb.https.any.js.ini @@ -8,13 +8,15 @@ if (os == "android") or (version == "Ubuntu 18.04"): not implemented [Verify stream compliance h26x annexb] expected: - if os == "win": PRECONDITION_FAILED - if os == "linux": PRECONDITION_FAILED + if os == "mac": PASS + PRECONDITION_FAILED [video-encoder-h26x-annexb.https.any.html?h265_annexb_software] disabled: if (os == "android") or (version == "Ubuntu 18.04"): not implemented + expected: + if tsan: [OK, CRASH] [Verify stream compliance h26x annexb] expected: if os == "linux": PASS @@ -43,15 +45,11 @@ [video-encoder-h26x-annexb.https.any.worker.html?h264_annexb_software] [Verify stream compliance h26x annexb] expected: - if (os == "linux") and (version == "Ubuntu 18.04"): PRECONDITION_FAILED if os == "android": PRECONDITION_FAILED [video-encoder-h26x-annexb.https.any.worker.html?h264_annexb_hardware] [Verify stream compliance h26x annexb] expected: - if tsan: PRECONDITION_FAILED - if os == "linux": PRECONDITION_FAILED if os == "mac": PASS - if os == "win": PRECONDITION_FAILED PRECONDITION_FAILED diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browser/set_download_behavior/user_context.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browser/set_download_behavior/user_context.py.ini index 378bb35a165fa..d207f56e02ed2 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/browser/set_download_behavior/user_context.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/browser/set_download_behavior/user_context.py.ini @@ -1,5 +1,6 @@ [user_context.py] expected: + if (os == "linux") and (display == "wayland") and not debug: [OK, TIMEOUT] if (os == "mac") and not debug: [OK, TIMEOUT] [test_user_contexts[True-new\]] expected: FAIL diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/activate.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/activate.py.ini index 99e6e972575ff..4c209278bf41f 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/activate.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/activate.py.ini @@ -1,6 +1,7 @@ [activate.py] expected: - if not fission and not debug and sessionHistoryInParent and (os == "android"): [OK, TIMEOUT] + if (os == "android") and not debug and not fission and sessionHistoryInParent: [OK, TIMEOUT] + if (os == "linux") and (display == "wayland") and not debug: [OK, CRASH] [test_activate_window] disabled: if os == "android": Not supported diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/invalid.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/invalid.py.ini new file mode 100644 index 0000000000000..582d0600149a1 --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/activate/invalid.py.ini @@ -0,0 +1,3 @@ +[invalid.py] + expected: + if (display == "wayland") and not debug: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/capture_screenshot/origin.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/capture_screenshot/origin.py.ini new file mode 100644 index 0000000000000..fb68cc4d3b635 --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/capture_screenshot/origin.py.ini @@ -0,0 +1,3 @@ +[origin.py] + expected: + if (display == "wayland") and not debug: [OK, CRASH] diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/close/prompt_unload.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/close/prompt_unload.py.ini index 8a232c9d1bf78..e07e03951cba6 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/close/prompt_unload.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/close/prompt_unload.py.ini @@ -1,4 +1,6 @@ [prompt_unload.py] + expected: + if (display == "wayland") and not debug: [OK, CRASH] [test_prompt_unload_triggering_dialog[window\]] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1879324 expected: diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/context_created/original_opener.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/context_created/original_opener.py.ini index 0b4c853c7b34e..ca13f1326ca9d 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/context_created/original_opener.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/context_created/original_opener.py.ini @@ -1,5 +1,6 @@ [original_opener.py] expected: - if (os == "linux") and (display == "x11") and tsan: [OK, TIMEOUT] - if (os == "linux") and (display == "wayland") and not debug: [OK, TIMEOUT] + if (os == "linux") and not tsan and (display == "wayland") and debug: [OK, CRASH] + if (os == "linux") and not tsan and (display == "wayland") and not debug: [OK, TIMEOUT] if (os == "mac") and not debug: [OK, TIMEOUT] + if (os == "linux") and tsan: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/webdriver/tests/bidi/network/continue_response/invalid.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/network/continue_response/invalid.py.ini index 3c7f171dc404d..280eb57bcba64 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/network/continue_response/invalid.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/network/continue_response/invalid.py.ini @@ -570,3 +570,7 @@ [test_params_status_code_invalid_value[4.3\]] expected: if tsan: [PASS, ERROR] + + [test_params_cookies_invalid_type[False\]] + expected: + if (os == "win") and debug: [PASS, FAIL] diff --git a/testing/web-platform/meta/webdriver/tests/bidi/network/response_started/response_started.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/network/response_started/response_started.py.ini new file mode 100644 index 0000000000000..c30980465f528 --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/bidi/network/response_started/response_started.py.ini @@ -0,0 +1,3 @@ +[response_started.py] + expected: + if (display == "wayland") and debug: [OK, CRASH] diff --git a/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js.ini b/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js.ini index 695c4f989e49f..b6fa7f491f091 100644 --- a/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js.ini +++ b/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js.ini @@ -1,7 +1,6 @@ [DOMException-constructor-and-prototype.any.worker.html] expected: - if sessionHistoryInParent: OK - [OK, TIMEOUT] + if not sessionHistoryInParent: [OK, TIMEOUT] [DOMException-constructor-and-prototype.any.html] @@ -32,5 +31,6 @@ [DOMException-constructor-and-prototype.any.shadowrealm-in-dedicatedworker.html] expected: + if sessionHistoryInParent and (os == "android") and not debug: [ERROR, TIMEOUT] if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] ERROR diff --git a/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js.ini b/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js.ini index 2889505065bb0..96db610bb5de7 100644 --- a/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js.ini +++ b/testing/web-platform/meta/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js.ini @@ -19,7 +19,9 @@ expected: ERROR [DOMException-constructor-behavior.any.shadowrealm-in-shadowrealm.html] - expected: ERROR + expected: + if not sessionHistoryInParent and not debug: [ERROR, TIMEOUT] + ERROR [DOMException-constructor-behavior.https.any.shadowrealm-in-serviceworker.html] expected: [ERROR, TIMEOUT] diff --git a/testing/web-platform/meta/webmessaging/postMessage_MessagePorts_xsite.sub.window.js.ini b/testing/web-platform/meta/webmessaging/postMessage_MessagePorts_xsite.sub.window.js.ini index 695a16764e602..af889e6d055bb 100644 --- a/testing/web-platform/meta/webmessaging/postMessage_MessagePorts_xsite.sub.window.js.ini +++ b/testing/web-platform/meta/webmessaging/postMessage_MessagePorts_xsite.sub.window.js.ini @@ -1,3 +1,3 @@ [postMessage_MessagePorts_xsite.sub.window.html] expected: - if (os == "android") and fission: [OK, TIMEOUT] + if (os == "android") and sessionHistoryInParent and not debug: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html.ini b/testing/web-platform/meta/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html.ini index 2517a8ff50b0e..d795799484680 100644 --- a/testing/web-platform/meta/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html.ini +++ b/testing/web-platform/meta/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html.ini @@ -1,4 +1,5 @@ [RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html] expected: + if (os == "android") and debug and sessionHistoryInParent: [OK, TIMEOUT] if (os == "android") and debug and not sessionHistoryInParent: [OK, TIMEOUT] if (os == "android") and not debug: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/webrtc/protocol/rtp-demuxing.html.ini b/testing/web-platform/meta/webrtc/protocol/rtp-demuxing.html.ini index d72b7048ddd09..316a97579513a 100644 --- a/testing/web-platform/meta/webrtc/protocol/rtp-demuxing.html.ini +++ b/testing/web-platform/meta/webrtc/protocol/rtp-demuxing.html.ini @@ -5,10 +5,9 @@ [Can demux two video tracks with different payload types on a bundled connection] expected: if (os == "win") and debug and (processor == "x86_64"): [PASS, FAIL] - if (os == "mac") and debug: [PASS, FAIL] - if (os == "mac") and not debug: [PASS, FAIL] if (os == "linux") and debug: [PASS, TIMEOUT] if (os == "linux") and not debug: [PASS, TIMEOUT] + if os == "mac": [PASS, FAIL] [Can demux two video tracks with the same payload type on an unbundled connection] expected: diff --git a/testing/web-platform/meta/webrtc/simulcast/h264.https.html.ini b/testing/web-platform/meta/webrtc/simulcast/h264.https.html.ini index 5fa165ad7ceb3..021ca0594dc83 100644 --- a/testing/web-platform/meta/webrtc/simulcast/h264.https.html.ini +++ b/testing/web-platform/meta/webrtc/simulcast/h264.https.html.ini @@ -5,6 +5,5 @@ [H264 simulcast setup with two streams] expected: if (os == "win") and debug and (processor == "x86_64"): [PASS, FAIL] - if (os == "mac") and debug: [PASS, FAIL] - if (os == "mac") and not debug: [PASS, FAIL] if (os == "linux") and not debug: [PASS, TIMEOUT] + if os == "mac": [PASS, FAIL] diff --git a/testing/web-platform/mozilla/meta/mathml/fonts/math-font-001.html.ini b/testing/web-platform/mozilla/meta/mathml/fonts/math-font-001.html.ini index 92d55e3a39daa..c8d9ec5e98f63 100644 --- a/testing/web-platform/mozilla/meta/mathml/fonts/math-font-001.html.ini +++ b/testing/web-platform/mozilla/meta/mathml/fonts/math-font-001.html.ini @@ -1,2 +1,3 @@ [math-font-001.html] - prefs: [font.name-list.serif.x-math: Consolas\, Menlo\, Droid Sans Mono\, Liberation Mono\, monospace] + prefs: [mathml.font_family_math.enabled: true, + font.name-list.serif.x-math: Consolas\, Menlo\, Droid Sans Mono\, Liberation Mono\, monospace] diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js new file mode 100644 index 0000000000000..ffd9287ab7e6b --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js @@ -0,0 +1,62 @@ +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=resources/utils.js +// META: timeout=long + +const port = get_host_info().HTTP_PORT_ELIDED; +const SUCCESS = true; +const FAILURE = false; + +function modulepreload_test(origin, expectation) { + promise_test( + async t => { + const key = token(); + const value = 'hello'; + const params = new URLSearchParams(); + params.set('key', key); + params.set('value', value); + + const link = document.createElement('link'); + link.rel = 'modulepreload'; + link.href = `${origin}${STORE_URL}?${params.toString()}`; + document.head.appendChild(link); + t.add_cleanup(() => link.remove()); + + if (expectation === SUCCESS) { + const result = await nextValueFromServer(key); + assert_equals(result, value); + } else { + const result = await Promise.race([ + new Promise(r => t.step_timeout(r, 1000)), nextValueFromServer(key) + ]); + assert_true(typeof result === 'undefined'); + } + }, + `Modulepreload to ${origin} ${ + expectation === SUCCESS ? 'succeeds' : 'fails'}.`); +} + +const test_cases = [ + // Page loads originate from `http://hosts[][]`; modulepreloading this origin + // succeeds, while subdomains fail. + {origin: 'http://{{hosts[][]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[][www]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www1]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www2]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][天気の良い日]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][élève]}}' + port, expectation: FAILURE}, + + // The pattern we've specified in the header ("*://*.hosts[alt]:*/") will + // match any subdomain of `hosts[alt]` (though not, as it turns out, + // `hosts[alt]` itself. + {origin: 'http://{{hosts[alt][]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[alt][www]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www1]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www2]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][天気の良い日]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][élève]}}' + port, expectation: SUCCESS}, +]; + +for (let i = 0; i < test_cases.length; i++) { + modulepreload_test(test_cases[i].origin, test_cases[i].expectation); +} diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.sub.headers b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.sub.headers new file mode 100644 index 0000000000000..05a5826807caa --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_modulepreload.sub.window.js.sub.headers @@ -0,0 +1 @@ +Connection-Allowlist: (response-origin "*://:subdomain.{{hosts[alt][]}}:*" "*://*:*/*\\?*&ignore-allowlist=true*") \ No newline at end of file diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js new file mode 100644 index 0000000000000..6bd4ad5a79fc6 --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js @@ -0,0 +1,62 @@ +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=resources/utils.js +// META: timeout=long + +const port = get_host_info().HTTP_PORT_ELIDED; +const SUCCESS = true; +const FAILURE = false; + +function prefetch_test(origin, expectation) { + promise_test( + async t => { + const key = token(); + const value = 'hello'; + const params = new URLSearchParams(); + params.set('key', key); + params.set('value', value); + + const link = document.createElement('link'); + link.rel = 'prefetch'; + link.href = `${origin}${STORE_URL}?${params.toString()}`; + document.head.appendChild(link); + t.add_cleanup(() => link.remove()); + + if (expectation === SUCCESS) { + const result = await nextValueFromServer(key); + assert_equals(result, value); + } else { + const result = await Promise.race([ + new Promise(r => t.step_timeout(r, 1000)), nextValueFromServer(key) + ]); + assert_true(typeof result === 'undefined'); + } + }, + `Prefetch to ${origin} ${ + expectation === SUCCESS ? 'succeeds' : 'fails'}.`); +} + +const test_cases = [ + // Page loads originate from `http://hosts[][]`; prefetching this origin + // succeeds, while subdomains fail. + {origin: 'http://{{hosts[][]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[][www]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www1]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www2]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][天気の良い日]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][élève]}}' + port, expectation: FAILURE}, + + // The pattern we've specified in the header ("*://*.hosts[alt]:*/") will + // match any subdomain of `hosts[alt]` (though not, as it turns out, + // `hosts[alt]` itself. + {origin: 'http://{{hosts[alt][]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[alt][www]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www1]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www2]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][天気の良い日]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][élève]}}' + port, expectation: SUCCESS}, +]; + +for (let i = 0; i < test_cases.length; i++) { + prefetch_test(test_cases[i].origin, test_cases[i].expectation); +} diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.sub.headers b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.sub.headers new file mode 100644 index 0000000000000..05a5826807caa --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_prefetch.sub.window.js.sub.headers @@ -0,0 +1 @@ +Connection-Allowlist: (response-origin "*://:subdomain.{{hosts[alt][]}}:*" "*://*:*/*\\?*&ignore-allowlist=true*") \ No newline at end of file diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js new file mode 100644 index 0000000000000..a526284c4b965 --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js @@ -0,0 +1,63 @@ +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=resources/utils.js +// META: timeout=long + +const port = get_host_info().HTTP_PORT_ELIDED; +const SUCCESS = true; +const FAILURE = false; + +function preload_test(origin, expectation) { + promise_test( + async t => { + const key = token(); + const value = 'hello'; + const params = new URLSearchParams(); + params.set('key', key); + params.set('value', value); + + const link = document.createElement('link'); + link.rel = 'preload'; + link.href = `${origin}${STORE_URL}?${params.toString()}`; + link.as = 'script' + document.head.appendChild(link); + t.add_cleanup(() => link.remove()); + + if (expectation === SUCCESS) { + const result = await nextValueFromServer(key); + assert_equals(result, value); + } else { + const result = await Promise.race([ + new Promise(r => t.step_timeout(r, 1000)), nextValueFromServer(key) + ]); + assert_true(typeof result === 'undefined'); + } + }, + `Preload to ${origin} ${ + expectation === SUCCESS ? 'succeeds' : 'fails'}.`); +} + +const test_cases = [ + // Page loads originate from `http://hosts[][]`; preloading this origin + // succeeds, while subdomains fail. + {origin: 'http://{{hosts[][]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[][www]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www1]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][www2]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][天気の良い日]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[][élève]}}' + port, expectation: FAILURE}, + + // The pattern we've specified in the header ("*://*.hosts[alt]:*/") will + // match any subdomain of `hosts[alt]` (though not, as it turns out, + // `hosts[alt]` itself. + {origin: 'http://{{hosts[alt][]}}' + port, expectation: FAILURE}, + {origin: 'http://{{hosts[alt][www]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www1]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][www2]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][天気の良い日]}}' + port, expectation: SUCCESS}, + {origin: 'http://{{hosts[alt][élève]}}' + port, expectation: SUCCESS}, +]; + +for (let i = 0; i < test_cases.length; i++) { + preload_test(test_cases[i].origin, test_cases[i].expectation); +} diff --git a/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js.sub.headers b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js.sub.headers new file mode 100644 index 0000000000000..05a5826807caa --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/link_rel_preload.sub.window.js.sub.headers @@ -0,0 +1 @@ +Connection-Allowlist: (response-origin "*://:subdomain.{{hosts[alt][]}}:*" "*://*:*/*\\?*&ignore-allowlist=true*") \ No newline at end of file diff --git a/testing/web-platform/tests/connection-allowlist/tentative/resources/key-value-store.py b/testing/web-platform/tests/connection-allowlist/tentative/resources/key-value-store.py new file mode 100644 index 0000000000000..9946855547db0 --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/resources/key-value-store.py @@ -0,0 +1,31 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Key-Value store server. + +The request takes "key=" and "value=" URL parameters. The key should be a UUID +generated by token(). + +- When only the "key=" is specified, serves a 200 response whose body contains + the stored value specified by the key. If the stored value doesn't exist, + serves a 200 response with an empty body. +- When both the "key=" and "value=" are specified, stores the pair and serves + a 200 response without body. +""" + + +def main(request, response): + key = request.GET.get(b"key") + value = request.GET.get(b"value", None) + + # Store the value. + if value: + request.server.stash.put(key, value) + return (200, [], b"") + + # Get the value. + data = request.server.stash.take(key) + if not data: + return (200, [], b"") + return (200, [], data) \ No newline at end of file diff --git a/testing/web-platform/tests/connection-allowlist/tentative/resources/utils.js b/testing/web-platform/tests/connection-allowlist/tentative/resources/utils.js new file mode 100644 index 0000000000000..7d7f02f007df3 --- /dev/null +++ b/testing/web-platform/tests/connection-allowlist/tentative/resources/utils.js @@ -0,0 +1,38 @@ +const STORE_URL = + '/connection-allowlist/tentative/resources/key-value-store.py'; +// URL with this query is exempted from connection allowlist. +const IGNORE_ALLOWLIST = 'ignore-allowlist=true'; + +// Reads the value specified by `key` from the key-value store on the server. +async function readValueFromServer(key) { + const serverUrl = `${STORE_URL}?${IGNORE_ALLOWLIST}&key=${key}`; + const response = await fetch(serverUrl); + if (!response.ok) + throw new Error('An error happened in the server'); + const value = await response.text(); + + // The value is not stored in the server. + if (value === '') + return {status: false}; + + return {status: true, value: value}; +} + +// Convenience wrapper around the above getter that will wait until a value is +// available on the server. +async function nextValueFromServer(key) { + // Resolve the key if it is a Promise. + key = await key; + + while (true) { + // Fetches the test result from the server. + const {status, value} = await readValueFromServer(key); + if (!status) { + // The test result has not been stored yet. Retry after a while. + await new Promise(resolve => step_timeout(resolve, 100)); + continue; + } + + return value; + } +} diff --git a/testing/web-platform/tests/content-security-policy/generic/only-valid-whitespaces-are-allowed.html b/testing/web-platform/tests/content-security-policy/generic/only-valid-whitespaces-are-allowed.html index 9b3636c9feb87..217023a91e450 100644 --- a/testing/web-platform/tests/content-security-policy/generic/only-valid-whitespaces-are-allowed.html +++ b/testing/web-platform/tests/content-security-policy/generic/only-valid-whitespaces-are-allowed.html @@ -31,6 +31,11 @@ // https://github.com/webcompat/web-bugs/issues/18902 has more details about why this particularly relevant { "csp": "img-src\u00A0'none';", "expected": true, "name": "U+00A0 NBSP should not be parsed between directive name and value" }, { "csp": "img-src http://example.com\u00A0http://example2.com;", "expected": true, "name": "U+00A0 NBSP should not be parsed inside directive value" }, + + // Ensure vertical tab (U+000B) is not considered a valid whitespace + // ASCII whitespace does not include vertical tab per https://infra.spec.whatwg.org/#ascii-whitespace + { "csp": "img-src\u000B'none';", "expected": true, "name": "U+000B VT should not be parsed between directive name and value" }, + { "csp": "img-src http://example.com\u000Bhttp://example2.com;", "expected": true, "name": "U+000B VT should not be parsed inside directive value" }, ]; tests.forEach(test => { diff --git a/testing/web-platform/tests/css/CSS2/colors/WEB_FEATURES.yml b/testing/web-platform/tests/css/CSS2/colors/WEB_FEATURES.yml index 6bfa95a6c58b7..707091b7b1161 100644 --- a/testing/web-platform/tests/css/CSS2/colors/WEB_FEATURES.yml +++ b/testing/web-platform/tests/css/CSS2/colors/WEB_FEATURES.yml @@ -2,3 +2,7 @@ features: - name: background-position files: - table-backgrounds-bs-row-002.xht +- name: color + files: + - "*" + - "!table-backgrounds-bs-row-002.xht" diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases-ref.html b/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases-ref.html new file mode 100644 index 0000000000000..85337ad1f7697 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases-ref.html @@ -0,0 +1,24 @@ + + + +

The following should have a green background:

+ +
+
+
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases.html b/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases.html new file mode 100644 index 0000000000000..f8fecfccca2b1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-name-edge-cases.html @@ -0,0 +1,75 @@ + + + +Setting 'animation-duration' to an infinite value should not hang + + + + + + + + +

The following should have a green background:

+ +
+
+
+
+
+
+
+ + +
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html index f14dff3e2c0db..010d70874e1cb 100644 --- a/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html +++ b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html @@ -41,6 +41,8 @@ test_keyframes_name_invalid('revert-layer, four'); // TODO: https://bugs.chromium.org/p/chromium/issues/detail?id=1342609 // test_keyframes_name_invalid('--foo'); + + test_keyframes_name_invalid('""'); - \ No newline at end of file + diff --git a/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml index 1b2ab1b44c6d1..1a7a8ae995e6e 100644 --- a/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml +++ b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml @@ -63,3 +63,9 @@ features: - name: named-color files: - named-001.html +- name: color + files: + - color-001.html + - color-002.html + - t31-color-text-a.xht + - t423-transparent-* diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001-ref.html new file mode 100644 index 0000000000000..2af60ca144a8d --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001-ref.html @@ -0,0 +1,110 @@ + + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html new file mode 100644 index 0000000000000..c26bbe863d4f1 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-001.html @@ -0,0 +1,122 @@ + + + + + CSS Grid Lanes Test: justify-items:baseline/last baseline + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002-ref.html new file mode 100644 index 0000000000000..9ea55e536ff76 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002-ref.html @@ -0,0 +1,111 @@ + + + + + + + +
+
baseline
+
baseline
+
center
+
last baseline
+
last baseline
+
start
+
baseline
+
baseline
+
end
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html new file mode 100644 index 0000000000000..9ec7b6f0e70f8 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-002.html @@ -0,0 +1,105 @@ + + + + + CSS Grid Lanes Test: justify-self baseline override + + + + + + +
+
baseline
+
baseline
+
center
+
last baseline
+
last baseline
+
start
+
baseline
+
baseline
+
end
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003-ref.html new file mode 100644 index 0000000000000..3f601ce931399 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003-ref.html @@ -0,0 +1,143 @@ + + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html new file mode 100644 index 0000000000000..1bf79e65ec421 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-003.html @@ -0,0 +1,139 @@ + + + + + CSS Grid Lanes Test: baseline alignment with different margins + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a-ref.html new file mode 100644 index 0000000000000..bc2d91281fc91 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a-ref.html @@ -0,0 +1,134 @@ + + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html new file mode 100644 index 0000000000000..4baaa83dfdb17 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004a.html @@ -0,0 +1,136 @@ + + + + + CSS Grid Lanes Test: baseline alignment with multi-track spanning items and dense packing + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b-ref.html new file mode 100644 index 0000000000000..09d7d2c4ffc56 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b-ref.html @@ -0,0 +1,133 @@ + + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html new file mode 100644 index 0000000000000..22a7279c2e2a9 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-004b.html @@ -0,0 +1,136 @@ + + + + + CSS Grid Lanes Test: baseline alignment with multi-track spanning items and dense packing + + + + + + +
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+
+
AAA
+
BBB
+
CCC
+
DDD
+
EEE
+
FFF
+
GGG
+
HHH
+
III
+
JJJ
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005-ref.html new file mode 100644 index 0000000000000..63bcc704f3e55 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005-ref.html @@ -0,0 +1,134 @@ + + + + + + + +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html new file mode 100644 index 0000000000000..1311894e29615 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-005.html @@ -0,0 +1,143 @@ + + + + + CSS Grid Lanes Test: baseline alignment with different writing modes + + + + + + +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001-ref.html new file mode 100644 index 0000000000000..f7f968527dbd8 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001-ref.html @@ -0,0 +1,82 @@ + + + + + + + + +
+
min-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
+
+
+
+
+
max-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html new file mode 100644 index 0000000000000..23eadc45ba550 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-001.html @@ -0,0 +1,74 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
min-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
+
+
+
+
+
max-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002-ref.html new file mode 100644 index 0000000000000..a455f6b30f11f --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002-ref.html @@ -0,0 +1,73 @@ + + + + + + + + +
+
flexible-sized columns - items with relative width
+
+
ÉÉ
+
ÉÉ
+
+
+
+
+
flex max-function columns - items with relative width
+
+
+
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html new file mode 100644 index 0000000000000..587e23c15e79d --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-002.html @@ -0,0 +1,75 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
flexible-sized columns - items with relative width
+
+
ÉÉ
+
ÉÉ
+
+
+
+
+
flex max-function columns - items with relative width
+
+
+
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003-ref.html new file mode 100644 index 0000000000000..188441a96f14a --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003-ref.html @@ -0,0 +1,76 @@ + + + + + + + + +
+
auto-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
fit-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html new file mode 100644 index 0000000000000..b65c3e8a94777 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-cyclic-dependency-003.html @@ -0,0 +1,75 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
auto-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
fit-content-sized columns - items with relative width
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001-ref.html new file mode 100644 index 0000000000000..51b8a2ab7c609 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001-ref.html @@ -0,0 +1,69 @@ + + + + + + + + +
Horizontal grid-lanes and verticalRL items do not share a baseline context
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ +
Horizontal grid-lanes and verticalLR item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html new file mode 100644 index 0000000000000..648c46f5a4d89 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-001.html @@ -0,0 +1,74 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment with synthesized baselines for orthogonal items + + + + + + + + +
Horizontal grid-lanes and verticalRL items do not share a baseline context
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ +
Horizontal grid-lanes and verticalLR item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002-ref.html new file mode 100644 index 0000000000000..f72e0e7afac4c --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002-ref.html @@ -0,0 +1,68 @@ + + + + + + + + +
VerticalLR grid-lanes and Horizontal item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ +
VerticalRL grid-lanes and Horizontal item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html new file mode 100644 index 0000000000000..4985216959cb8 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/column-grid-lanes-item-baseline-synthesized-002.html @@ -0,0 +1,73 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment with synthesized baselines for orthogonal items + + + + + + + + +
VerticalLR grid-lanes and Horizontal item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ +
VerticalRL grid-lanes and Horizontal item
+
+
ÉÉ É ÉÉÉ É ÉÉ É
+
É É ÉÉ
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001-ref.html new file mode 100644 index 0000000000000..d14af9b85d423 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001-ref.html @@ -0,0 +1,77 @@ + + + + + + + +
+
1
+
2
+
3
+
4
line 2
+
5
+
6
+
+
+
1
+
2
+
3
+
4
line 2
+
5
+
6
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html new file mode 100644 index 0000000000000..e4aa2aeb4abf9 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-001.html @@ -0,0 +1,93 @@ + + + + + CSS Grid Lanes Test: align-items:baseline/last baseline + + + + + + +
+
1
+
2
+
3
+
4
line 2
+
5
+
6
+
+
+
1
+
2
+
3
+
4
line 2
+
5
+
6
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002-ref.html new file mode 100644 index 0000000000000..2b6fa69779472 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002-ref.html @@ -0,0 +1,108 @@ + + + + + + + +
+
baseline
+
baseline
+
center
+
baseline
+
last
baseline
+
last
baseline
+
start
+
baseline
+
baseline
+
end
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html new file mode 100644 index 0000000000000..1c7c40ac4f1b8 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-002.html @@ -0,0 +1,119 @@ + + + + + CSS Grid Lanes Test: align-self baseline override + + + + + + +
+
baseline
+
baseline
+
center
+
baseline
+
last
baseline
+
last
baseline
+
start
+
baseline
+
baseline
+
end
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003-ref.html new file mode 100644 index 0000000000000..2203969b2ba3a --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003-ref.html @@ -0,0 +1,128 @@ + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html new file mode 100644 index 0000000000000..a0d7e187bbccc --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-003.html @@ -0,0 +1,139 @@ + + + + + CSS Grid Lanes Test: baseline alignment with different margins + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004-ref.html new file mode 100644 index 0000000000000..9bbb8046bac54 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004-ref.html @@ -0,0 +1,132 @@ + + + + + + + +
+
1
+
2
+
3
line 2
+
4
+
5
+
6
+
7
line 2
+
8
+
9
+
10
+
+
+
1
+
2
+
3
line 2
+
4
+
5
+
6
+
7
line 2
+
8
+
9
+
10
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html new file mode 100644 index 0000000000000..506d7572cda60 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-004.html @@ -0,0 +1,136 @@ + + + + + CSS Grid Lanes Test: baseline alignment with multi-track spanning items and dense packing + + + + + + +
+
1
+
2
+
3
line 2
+
4
+
5
+
6
+
7
line 2
+
8
+
9
+
10
+
+
+
1
+
2
+
3
line 2
+
4
+
5
+
6
+
7
line 2
+
8
+
9
+
10
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005-ref.html new file mode 100644 index 0000000000000..06f8a4df3ddee --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005-ref.html @@ -0,0 +1,133 @@ + + + + + + + +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html new file mode 100644 index 0000000000000..c3b3e5d932a12 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-005.html @@ -0,0 +1,144 @@ + + + + + CSS Grid Lanes Test: baseline alignment with different writing modes + + + + + + +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ +
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+
AB
CD
+
EF
GH
+
IJ
+
KL
+
MN
+
OP
+
QR
ST
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001-ref.html new file mode 100644 index 0000000000000..aa012417d7823 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001-ref.html @@ -0,0 +1,63 @@ + + + + + + + + +
+
min-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ +
+
max-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html new file mode 100644 index 0000000000000..43f4360a393b6 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-001.html @@ -0,0 +1,66 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
min-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
max-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002-ref.html new file mode 100644 index 0000000000000..a6910670494b3 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002-ref.html @@ -0,0 +1,63 @@ + + + + + + + + +
+
flexible-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
flex max-function rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html new file mode 100644 index 0000000000000..66cedbf563053 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-002.html @@ -0,0 +1,66 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
flexible-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
flex max-function rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003-ref.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003-ref.html new file mode 100644 index 0000000000000..2b8531c8481d9 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003-ref.html @@ -0,0 +1,61 @@ + + + + + + + + +
+
auto-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
fit-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html new file mode 100644 index 0000000000000..577a6ec07dafd --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-lanes/tentative/baseline/row-grid-lanes-item-baseline-cyclic-dependency-003.html @@ -0,0 +1,65 @@ + + + + + CSS Grid Lanes Test: Self-Baseline alignment and sizing cyclic dependency + + + + + + + +
+
auto-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+
+
fit-content-sized rows - items with relative height
+
+
ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É
+
ÉÉÉÉ É ÉÉ
+
+
+
+ + diff --git a/testing/web-platform/tests/css/css-multicol/crashtests/shrink-column-height-block-with-margin-in-inline.html b/testing/web-platform/tests/css/css-multicol/crashtests/shrink-column-height-block-with-margin-in-inline.html new file mode 100644 index 0000000000000..2789a8adfdb5d --- /dev/null +++ b/testing/web-platform/tests/css/css-multicol/crashtests/shrink-column-height-block-with-margin-in-inline.html @@ -0,0 +1,14 @@ + +Shrink multicol height so that a child block (inside an inline) with a top margin gets pushed to the next column and gets its margin truncated. + + +
+ + +
x
+
+
+ diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-is-inline-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-is-inline-ref.html index 10098bec8efc7..d8821ea58ee1a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-is-inline-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-is-inline-ref.html @@ -2,7 +2,7 @@ View transitions: New content is an inline element (ref) - +
- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL

START STATE

- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL

END STATE

- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL
- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL

START STATE

- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL

END STATE

- FILLER FILLER - INLINE INLINE INLINE INLINE + FILL FILL + INL INL
+

+ diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/WEB_FEATURES.yml b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/WEB_FEATURES.yml index 906718b80359f..98683158c596c 100644 --- a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/WEB_FEATURES.yml +++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/WEB_FEATURES.yml @@ -3,3 +3,8 @@ features: files: - document-base-url.html - multiple-base.sub.html +- name: iframe-srcdoc + files: + - document-base-url-about-srcdoc.https.window.js + - document-base-url-changes-about-srcdoc.https.window.js + - nontraditional-about-srcdoc.html diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/WEB_FEATURES.yml b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/WEB_FEATURES.yml index 470b91ca71102..7cae7ee081d34 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/WEB_FEATURES.yml +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/WEB_FEATURES.yml @@ -9,3 +9,22 @@ features: - name: hashchange files: - srcdoc_change_hash.html +- name: iframe-srcdoc + files: + - srcdoc* + - "!srcdoc_change_hash.html" +- name: iframe-sandbox + files: + - iframe_sandbox_* + - sandbox-* + - sandbox_* +- name: iframe + files: + - "*" + - "!iframe-loading-lazy*" + - "!iframe-loading-lazy-base-url*" + - "!iframe-with-base.html" + - "!srcdoc*" + - "!iframe_sandbox_*" + - "!sandbox-*" + - "!sandbox_*" diff --git a/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-details-crash.html b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-details-crash.html new file mode 100644 index 0000000000000..8e5bb0463678f --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-details-crash.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-meter-crash.html b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-meter-crash.html new file mode 100644 index 0000000000000..4b3af0cf9d2c4 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-meter-crash.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-progress-crash.html b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-progress-crash.html new file mode 100644 index 0000000000000..a376b5046a226 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-progress-crash.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-textarea-crash.html b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-textarea-crash.html new file mode 100644 index 0000000000000..ed02f1530ea14 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/slot-dir-attach-child-textarea-crash.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-invalid-path-fallback.svg b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-invalid-path-fallback.svg new file mode 100644 index 0000000000000..957692547ef22 --- /dev/null +++ b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-invalid-path-fallback.svg @@ -0,0 +1,17 @@ + + Invalid 'path' attribute should fallback to href + + + + + + + + + + + + Text along a quadratic curve + + + diff --git a/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-precedence-over-href.svg b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-precedence-over-href.svg new file mode 100644 index 0000000000000..c34e7d48dd428 --- /dev/null +++ b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-precedence-over-href.svg @@ -0,0 +1,16 @@ + + Checking `path` precedence over `href` on textPath element + + + + + + + + + + + Text along a quadratic curve + + + diff --git a/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-ref.svg b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-ref.svg new file mode 100644 index 0000000000000..51a780cbd1d02 --- /dev/null +++ b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr-ref.svg @@ -0,0 +1,10 @@ + + + + + + + Text along a quadratic curve + + + diff --git a/testing/web-platform/tests/svg/text/reftests/textpath-path-attr.svg b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr.svg new file mode 100644 index 0000000000000..eea6a7160f934 --- /dev/null +++ b/testing/web-platform/tests/svg/text/reftests/textpath-path-attr.svg @@ -0,0 +1,13 @@ + + Supported 'path' on textPath element + + + + + + + + Text along a quadratic curve + + + diff --git a/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-alternate.tentative.html b/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-alternate.tentative.html deleted file mode 100644 index de0f3fd0bc901..0000000000000 --- a/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-alternate.tentative.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - -
Click me!
- -
Watch me!
- - - diff --git a/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-state.tentative.html b/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-state.tentative.html deleted file mode 100644 index 4ea44408c2980..0000000000000 --- a/testing/web-platform/tests/web-animations/animation-trigger/event-trigger-state.tentative.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - -
Click me!
- -
Watch me!
- - - diff --git a/testing/web-platform/tests/x-frame-options/get-decode-split.html b/testing/web-platform/tests/x-frame-options/get-decode-split.html index 9f5101d610eb2..237df46d49ad3 100644 --- a/testing/web-platform/tests/x-frame-options/get-decode-split.html +++ b/testing/web-platform/tests/x-frame-options/get-decode-split.html @@ -20,4 +20,16 @@ sameOriginAllowed: false, crossOriginAllowed: false }); + +xfo_simple_tests({ + headerValue: `\x0BDENY`, + sameOriginAllowed: true, + crossOriginAllowed: true +}); + +xfo_simple_tests({ + headerValue: `\x0CDENY`, + sameOriginAllowed: true, + crossOriginAllowed: true +}); diff --git a/testing/web-platform/tests/xhr/WEB_FEATURES.yml b/testing/web-platform/tests/xhr/WEB_FEATURES.yml index eb73be520a0f9..8333253b6ac50 100644 --- a/testing/web-platform/tests/xhr/WEB_FEATURES.yml +++ b/testing/web-platform/tests/xhr/WEB_FEATURES.yml @@ -25,3 +25,6 @@ features: - name: http2 files: - status.h2.window.js +- name: iframe-sandbox + files: + - access-control-sandboxed-iframe-* diff --git a/third_party/jpeg-xl/.bazelignore b/third_party/jpeg-xl/.bazelignore deleted file mode 100644 index 912eacc1b5274..0000000000000 --- a/third_party/jpeg-xl/.bazelignore +++ /dev/null @@ -1 +0,0 @@ -third_party diff --git a/third_party/jpeg-xl/.clang-format b/third_party/jpeg-xl/.clang-format deleted file mode 100644 index a61b61c569724..0000000000000 --- a/third_party/jpeg-xl/.clang-format +++ /dev/null @@ -1,4 +0,0 @@ -BasedOnStyle: Google -IncludeCategories: - - Regex: '^ -# instead . -# - modernize-return-braced-init-list: this often doesn't improve readability. -# - modernize-use-auto: is too aggressive towards using auto. -# - modernize-use-default-member-init: with a mix of constructors and default -# member initialization this can be confusing if enforced. -# - modernize-use-trailing-return-type: does not improve readability when used -# systematically. -# - modernize-use-using: typedefs are ok. -# -# - readability-else-after-return: It doesn't always improve readability. -# - readability-static-accessed-through-instance -# It is often more useful and readable to access a constant of a passed -# variable (like d.N) instead of using the type of the variable that could be -# long and complex. -# - readability-uppercase-literal-suffix: we write 1.0f, not 1.0F. - -Checks: >- - bugprone-*, - clang-*, - -clang-diagnostic-unused-command-line-argument, - google-*, - modernize-*, - performance-*, - readability-*, - -bugprone-branch-clone, - -bugprone-easily-swappable-parameters, - -bugprone-implicit-widening-of-multiplication-result, - -bugprone-infinite-loop, - -bugprone-narrowing-conversions, - -bugprone-unused-local-non-trivial-variable, - -modernize-avoid-c-arrays, - -modernize-concat-nested-namespaces, - -modernize-deprecated-headers, - -modernize-return-braced-init-list, - -modernize-type-traits, - -modernize-use-auto, - -modernize-use-default-member-init, - -modernize-use-nodiscard, - -modernize-use-trailing-return-type, - -modernize-use-using, - -performance-enum-size, - -readability-avoid-nested-conditional-operator, - -readability-else-after-return, - -readability-function-cognitive-complexity, - -readability-identifier-length, - -readability-magic-numbers, - -readability-redundant-access-specifiers, - -readability-simplify-boolean-expr, - -readability-static-accessed-through-instance, - -readability-suspicious-call-argument, - -readability-uppercase-literal-suffix, - -readability-use-anyofallof, - - -WarningsAsErrors: >- - bugprone-argument-comment, - bugprone-macro-parentheses, - bugprone-suspicious-string-compare, - bugprone-use-after-move, - clang-*, - clang-analyzer-*, - -clang-diagnostic-unused-command-line-argument, - google-build-using-namespace, - google-explicit-constructor, - google-readability-braces-around-statements, - google-readability-namespace-comments, - modernize-use-override, - readability-inconsistent-declaration-parameter-name - -# We are only interested in the headers from this projects, excluding -# third_party/ and build/. -HeaderFilterRegex: '^.*/(lib|tools)/.*\.h$' - -CheckOptions: - - key: readability-braces-around-statements.ShortStatementLines - value: '2' - - key: google-readability-braces-around-statements.ShortStatementLines - value: '2' - - key: readability-implicit-bool-conversion.AllowPointerConditions - value: '1' - - key: readability-implicit-bool-conversion.AllowIntegerConditions - value: '1' - - key: bugprone-signed-char-misuse.CharTypdefsToIgnore - value: 'int8_t' diff --git a/third_party/jpeg-xl/.pre-commit-config.yaml b/third_party/jpeg-xl/.pre-commit-config.yaml deleted file mode 100644 index 96b5d7fb5254c..0000000000000 --- a/third_party/jpeg-xl/.pre-commit-config.yaml +++ /dev/null @@ -1,30 +0,0 @@ -repos: -- repo: https://github.com/gherynos/pre-commit-java - rev: v0.2.4 - hooks: - - id: Checkstyle -- repo: https://github.com/gitleaks/gitleaks - rev: v8.16.3 - hooks: - - id: gitleaks -- repo: https://github.com/jumanjihouse/pre-commit-hooks - rev: 3.0.0 - hooks: - - id: shellcheck -- repo: https://github.com/pocc/pre-commit-hooks - rev: v1.3.5 - hooks: - - id: cpplint -- repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.38.0 - hooks: - - id: eslint -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/pylint-dev/pylint - rev: v2.17.2 - hooks: - - id: pylint diff --git a/third_party/jpeg-xl/.readthedocs.yaml b/third_party/jpeg-xl/.readthedocs.yaml deleted file mode 100644 index ee25fed4816ff..0000000000000 --- a/third_party/jpeg-xl/.readthedocs.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. -# -# readthedocs.io configuration file. See: -# https://docs.readthedocs.io/en/stable/config-file/v2.html - -version: 2 - -sphinx: - configuration: doc/sphinx/conf.py - -build: - os: ubuntu-22.04 - tools: - python: "3" - apt_packages: - - doxygen - - graphviz - -python: - install: - - requirements: doc/sphinx/requirements.txt diff --git a/third_party/jpeg-xl/AUTHORS b/third_party/jpeg-xl/AUTHORS deleted file mode 100644 index afd884bc79df5..0000000000000 --- a/third_party/jpeg-xl/AUTHORS +++ /dev/null @@ -1,103 +0,0 @@ -# List of the project authors. -# When contributing you can add your name to this list. -# For a complete list of contributions made after the move -# from gitlab to github, see -# https://github.com/libjxl/libjxl/graphs/contributors. -# See CONTRIBUTING.md for details. -# -# -# Please keep each list sorted. If you wish to change your email address please -# send a pull request. - -# Organizations: - -# - Cloudinary Ltd.: -Jon Sneyers - -# - Google: -Evgenii Kliuchnikov -Iulia Comșa -Jan Wassenberg -Jyrki Alakuijala -Lode Vandevenne -Luca Versari -Marcin Kowalczyk -Martin Bruse -Moritz Firsching -Sami Boukortt -Sebastian Gomez -Thomas Fischbacher -Zoltan Szabadka - -# Individuals: -a-shvedov -Aditya Patadia -Ahmad Amsyar Asyadiq Bin Syaiful Bahri <27284123+Ahmad-Amsyar@users.noreply.github.com> -Alex Xu (Hello71) -Alexander Sago -Alifian Caesar Khalid -Alistair Barrow -Andrius Lukas Narbutas -Aous Naman -Artem Selishchev -Aryan Pingle -Biswapriyo Nath -CanadianBaconBoi -Damiano Albani -Damon Townsend -Daniel Novomeský -David Burnett -dependabot[bot] -Diego Pino -Dirk Lemstra -Dmitry Baryshev -Don Olmstead -Dong Xu -estrogently <41487185+estrogently@users.noreply.github.com> -Even Rouault -Fred Brennan -Gerhard Huber -gi-man -Gilles Devillers (GilDev) -Heiko Becker -Ivan Kokorev -Jim Robinson -John Platts -Jonathan Brown (Jonnyawsom3) -Joshua Root -Kai Hollberg -Kerry Su -Kleis Auke Wolthuizen -L. E. Segovia -ledoge -Leo Izen -Lovell Fuller -Maarten DB -Marcin Konicki -Martin Strunz -Mathieu Malaterre -Mikk Leini -Misaki Kasumi -Moonchild Straver -Nicholas Hayes <0xC0000054@users.noreply.github.com> -Nigel Tao -oupson -Petr Diblík -Pieter Wuille -roland-rollo -Samuel Leong -Sandro -sandstrom -Sergey Fedorov -Stephan T. Lavavej -StepSecurity Bot -Sylvestre Ledru -Thomas Bonfort -Timo Rothenpieler -tmkk -Vincent Torri -Wonwoo Choi -xiota -Yonatan Nebenzhal -Ziemowit Zabawa -源文雨 <41315874+fumiama@users.noreply.github.com> diff --git a/third_party/jpeg-xl/BUILD.bazel b/third_party/jpeg-xl/BUILD.bazel deleted file mode 100644 index 45c5261a2ea47..0000000000000 --- a/third_party/jpeg-xl/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -package(default_visibility = ["//:__subpackages__"]) - -filegroup( - name = "testdata", - srcs = glob([ - "testdata/**/*.icc", - "testdata/**/*.pam", - "testdata/**/*.pfm", - "testdata/**/*.pgm", - "testdata/**/*.pnm", - "testdata/**/*.ppm", - "testdata/**/*.png", - "testdata/**/*.jpg", - "testdata/**/*.jxl", - "testdata/**/*.gif", - "testdata/**/*.y4m", - "testdata/**/*.jxl", - "testdata/**/*.png", - "testdata/**/*.jpg", - "testdata/position_encoding/*.txt", - ]), -) - diff --git a/third_party/jpeg-xl/BUILDING.md b/third_party/jpeg-xl/BUILDING.md deleted file mode 100644 index 7e9bc2aad3a40..0000000000000 --- a/third_party/jpeg-xl/BUILDING.md +++ /dev/null @@ -1,85 +0,0 @@ -# Compilation - -For more details and other workflows see the "Advanced guide" below. - -## Checking out the code - -```bash -git clone https://github.com/libjxl/libjxl.git --recursive --shallow-submodules -``` - -This repository uses git submodules to handle some third party dependencies -under `third_party`, that's why it is important to pass `--recursive`. If you -didn't check out with `--recursive`, or any submodule has changed, run: - -```bash -git submodule update --init --recursive --depth 1 --recommend-shallow -``` - -The `--shallow-submodules` and `--depth 1 --recommend-shallow` options create -shallow clones which only downloads the commits requested, and is all that is -needed to build `libjxl`. Should full clones be necessary, you could always run: - -```bash -git submodule foreach git fetch --unshallow -git submodule update --init --recursive -``` - -which pulls the rest of the commits in the submodules. - -Important: If you downloaded a zip file or tarball from the web interface you -won't get the needed submodules and the code will not compile. You can download -these external dependencies from source running `./deps.sh`. The git workflow -described above is recommended instead. - -## Installing dependencies - -Required dependencies for compiling the code, in a Debian/Ubuntu based -distribution run: - -```bash -sudo apt install cmake pkg-config libbrotli-dev -``` - -Optional dependencies for supporting other formats in the `cjxl`/`djxl` tools, -in a Debian/Ubuntu based distribution run: - -```bash -sudo apt install libgif-dev libjpeg-dev libopenexr-dev libpng-dev libwebp-dev -``` - -We recommend using a recent Clang compiler (version 7 or newer), for that -install clang and set `CC` and `CXX` variables. - -```bash -sudo apt install clang -export CC=clang CXX=clang++ -``` - -## Building - -```bash -cd libjxl -mkdir build -cd build -cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF .. -cmake --build . -- -j$(nproc) -``` - -The encoder/decoder tools will be available in the `build/tools` directory. - -## Installing - -```bash -sudo cmake --install . -``` - - -## Building JPEG XL for developers - -For experienced developers, we provide build instructions for several other environments: - -* [Building on Debian](doc/developing_in_debian.md) -* Building on Windows with [vcpkg](doc/developing_in_windows_vcpkg.md) (Visual Studio 2019) -* Building on Windows with [MSYS2](doc/developing_in_windows_msys.md) -* [Cross Compiling for Windows with Crossroad](doc/developing_with_crossroad.md) diff --git a/third_party/jpeg-xl/BUILDING_Haiku.md b/third_party/jpeg-xl/BUILDING_Haiku.md deleted file mode 100644 index 1ffca1453c77b..0000000000000 --- a/third_party/jpeg-xl/BUILDING_Haiku.md +++ /dev/null @@ -1,20 +0,0 @@ -## Disclaimer - -Haiku builds are not officially supported, i.e. the build might not work at all, -some tests may fail and some sub-projects are excluded from build. - -This manual outlines Haiku-specific setup. For general building and testing -instructions see "[BUILDING](BUILDING.md)" and -"[Building and Testing changes](doc/building_and_testing.md)". - -## Dependencies - -```shell -pkgman install llvm9_clang ninja cmake doxygen libjpeg_turbo_devel giflib_devel -``` - -## Building - -```shell -TEST_STACK_LIMIT=none CMAKE_FLAGS="-I/boot/system/develop/tools/lib/gcc/x86_64-unknown-haiku/8.3.0/include/c++ -I/boot/system/develop/tools/lib/gcc/x86_64-unknown-haiku/8.3.0/include/c++/x86_64-unknown-haiku" CMAKE_SHARED_LINKER_FLAGS="-shared -Xlinker -soname=libjpegxl.so -lpthread" ./ci.sh opt -``` diff --git a/third_party/jpeg-xl/BUILDING_OSX.md b/third_party/jpeg-xl/BUILDING_OSX.md deleted file mode 100644 index b5f5e34db711e..0000000000000 --- a/third_party/jpeg-xl/BUILDING_OSX.md +++ /dev/null @@ -1,41 +0,0 @@ -## Disclaimer - -OSX builds have "best effort" support, i.e. build might not work at all, some -tests may fail and some sub-projects are excluded from build. - -This manual outlines OSX specific setup. For general building and testing -instructions see "[BUILDING](BUILDING.md)" and -"[Building and Testing changes](doc/building_and_testing.md)". - -[Homebrew](https://brew.sh/) is a popular package manager. JPEG XL library and -binaries could be installed using it: - -```bash -brew install jpeg-xl -``` - -## Dependencies - -Make sure that `brew doctor` does not report serious problems and up-to-date -version of XCode is installed. - -Installing (actually, building) `clang` might take a couple hours. - -```bash -brew install llvm -``` - -```bash -brew install coreutils cmake giflib jpeg-turbo libpng ninja zlib -``` - -Before building the project check that `which clang` is -`/usr/local/opt/llvm/bin/clang`, not the one provided by XCode. If not, update -`PATH` environment variable. - -Also, setting `CMAKE_PREFIX_PATH` might be necessary for correct include paths -resolving, e.g.: - -```bash -export CMAKE_PREFIX_PATH=`brew --prefix giflib`:`brew --prefix jpeg-turbo`:`brew --prefix libpng`:`brew --prefix zlib` -``` \ No newline at end of file diff --git a/third_party/jpeg-xl/CHANGELOG.md b/third_party/jpeg-xl/CHANGELOG.md deleted file mode 100644 index 66bec271979d0..0000000000000 --- a/third_party/jpeg-xl/CHANGELOG.md +++ /dev/null @@ -1,403 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.11.1] - 2024-26-11 - -### Fixed - - Huffman lookup table size fix (#3871 - - [CVE-2024-11403](https://www.cve.org/cverecord?id=CVE-2024-11403)) - - Check height limit in modular trees. (#3943 - - [CVE-2024-11498](https://www.cve.org/cverecord?id=CVE-2024-11498)) - -## [0.11.0] - 2024-09-13 - -### Added - - Gain Map API (#3552 and #3628): `JxlGainMapBundle` struct and API functions - to read and write gain map bundles`JxlGainMapWriteBundle` and - `JxlGainMapReadBundle` as well as handling compressed ICC profiles: - `JxlICCProfileEncode` and `JxlICCProfileDecode`. - - decoder API: added `JXL_DEC_BOX_COMPLETE` event to signal that the output - buffer for the current box has received all contents. Previously, this was - to be determined from the fact that the decoder had moved on either to - `JXL_DEC_SUCCESS` or to another subsequent `JXL_DEC_BOX`. This change is - made backward-compatible by the fact that the new event must be explicitly - subscribed to, and that `JXL_DEC_SUCCESS` / `JXL_DEC_BOX` still occur - afterwards and still imply that the previous box must be complete. - -### Changed / clarified - - avoiding abort in release build (#3631 and #3639) - -## [0.10.2] - 2024-03-08 - -### Fixed - - bugs in (lossless) encoding (#3367, #3359 and #3386) - - re-enable installation of MIME file (#3375) - - bugs in streaming mode (#3379 and #3380) - -## [0.10.1] - 2024-02-28 - -### Fixed - - reduce allocations (#3336 and #3339), - fixing a significant speed regression present since 0.9.0 - - bug in streaming encoding (#3331) - -## [0.10.0] - 2024-02-21 - -### Added - - decoder API: added `JxlDecoderGetBoxSizeContents` for getting the size of the - content of a box without the headers. - - encoder API: implemented new api functions for streaming encoding. - -### Changed / clarified - - decoder/encoder API: return failure when surface allocation fail - - encoder API / cjxl: updated modular effort levels to faster settings; the - effort range is now 1-10, with 11 available in advanced mode. - -## [0.9.2] - 2024-02-07 - -### Fixed - - bugs in the gdk-pixbuf plugin - - some build issues - -## [0.9.1] - 2024-01-08 - -### Fixed - - multiple build issues - -## [0.9.0] - 2023-12-22 - -### Added - - encoder API: add `JxlEncoderSetExtraChannelDistance` to adjust the quality - of extra channels (like alpha) separately. - - encoder API: new api functions for streaming encoding: - - `JxlEncoderSetOutputProcessor` - - `JxlEncoderFlushInput` - - `JxlEncoderOutputProcessor` struct - - `JxlEncoderSetOutputCallback` - - `JxlChunkedFrameInputSource` struct - - `JxlEncoderAddChunkedFrame` - - encoder API: new options for more fine-grained control over metadata - preservation when using `JxlEncoderAddJPEGFrame`: - - `JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF` - - `JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP` - - `JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF` - - encoder API: new function `JxlEncoderSetUpsamplingMode` to change the upsampling - method, e.g. to use nearest-neighbor upsampling for pixel art - - decoder API: implemented `JxlDecoderSetOutputColorProfile` and - `JxlDecoderSetCms` to enable decoding to desired colorspace. - - cjxl can now be used to explicitly add/update/strip Exif/XMP/JUMBF metadata using - the decoder-hints syntax, e.g. `cjxl input.ppm -x exif=input.exif output.jxl` - - djxl can now be used to extract Exif/XMP/JUMBF metadata - - encoder API: new function `JxlEncoderDistanceFromQuality` for convenience to - calculate a `distance` given a `quality` - -### Removed - - API: the Butteraugli API (`jxl/butteraugli.h`) was removed. - - encoder and decoder API: all deprecated functions were removed: - `JxlDecoderDefaultPixelFormat`, `JxlEncoderOptionsSetLossless`, - `JxlEncoderOptionsSetEffort`, `JxlEncoderOptionsSetDecodingSpeed`, - `JxlEncoderOptionsSetDistance`, `JxlEncoderOptionsCreate`, as well as - the deprecated enumerator values `JXL_DEC_EXTENSIONS`, `JXL_ENC_NOT_SUPPORTED`, - `JXL_TYPE_BOOLEAN`, `JXL_TYPE_UINT32`, and deprecated type `JxlEncoderOptions`. - - decoder API: the signature of `JxlDecoderGetColorAsEncodedProfile`, - `JxlDecoderGetICCProfileSize`, and `JxlDecoderGetColorAsICCProfile` - changed: a deprecated unused argument was removed. - -### Changed / clarified - - changed the name of the cjxl flag `photon_noise` to `photon_noise_iso` - - fixed how large boxes are decoded (#2958) - - fixed encoding files with unreadable patches (#3042, #3046) - -## [0.8.2] - 2023-06-14 - -### Changed - - Security: Fix an integer underflow bug in patch decoding (#2551- CVE-2023-35790). - -## [0.8.1] - 2023-02-03 - -### Changed - - Allow fast-lossless for 16-bit float input (#2093) - - Fix bug in palette (#2120) - - Security: Fix OOB read in exif.h (#2101 - [CVE-2023-0645](https://www.cve.org/cverecord?id=CVE-2023-0645)) - -## [0.8.0] - 2023-01-18 - -### Added - - decoder API: new function `JxlDecoderSetImageBitDepth` to set the bit depth - of the output buffer. - - decoder API proposal: add `JxlDecoderSetOutputColorProfile` and - `JxlDecoderSetCms` to enable decoding to desired colorspace; NB: not - implemented yet. - - encoder API: new function `JxlEncoderSetFrameBitDepth` to set the bit depth - of the input buffer. - - encoder API: add an effort 10 option for lossless compression; using this - setting requires calling `JxlEncoderAllowExpertOptions`. - - encoder API: new `JXL_ENC_FRAME_SETTING_JPEG_COMPRESS_BOXES` enum value to - allow explicit control of metadata compression - -### Removed - - common API: removed `JxlIntrinsicSizeHeader` - - decoder API: removed deprecated `JXL_DEC_NEED_DC_OUT_BUFFER` and - `JXL_DEC_DC_IMAGE` events, `JxlDecoderDCOutBufferSize` and - `JxlDecoderSetDCOutBuffer` functions - -### Changed / clarified - - encoder API: `JxlEncoderProcessOutput` requires at least 32 bytes of output - space to proceed and guarantees that at least one byte will be written - -## [0.7] - 2022-07-21 - -### Added - - Export version information in headers. - - decoder API: Ability to decode the content of metadata boxes: - `JXL_DEC_BOX`, `JXL_DEC_BOX_NEED_MORE_OUTPUT`, `JxlDecoderSetBoxBuffer`, - `JxlDecoderGetBoxType`, `JxlDecoderGetBoxSizeRaw` and - `JxlDecoderSetDecompressBoxes`. - - decoder API: ability to mark the input is finished: `JxlDecoderCloseInput`. - - decoder API: ability to request updates on different progressive events using - `JxlDecoderSetProgressiveDetail`; currently supported events are - `kDC`, `kLastPasses` and `kPasses`. - - decoder API: ability to specify desired intensity target using - `JxlDecoderSetDesiredIntensityTarget` - - decoder API: new function `JxlDecoderSetCoalesced` to allow decoding - non-coalesced (unblended) frames, e.g. layers of a composite still image - or the cropped frames of a recompressed GIF/APNG. - - decoder API: new function `JxlDecoderSetUnpremultiplyAlpha` to set - preference for getting an associated alpha channel with premultiplied or - unpremultiplied colors. - - decoder API: field added to `JxlFrameHeader`: a `JxlLayerInfo` struct - that contains crop dimensions and offsets and blending information for - the non-coalesced case. - - decoder API: new function `JxlDecoderGetExtraChannelBlendInfo` to get - the blending information for extra channels in the non-coalesced case. - - decoder API: new function `JxlDecoderSetMultithreadedImageOutCallback`, - allowing output callbacks to receive more information about the number of - threads on which they are running. - - decoder API: new function `JxlDecoderSkipCurrentFrame` to skip processing - the current frame after a progressive detail is reached. - - decoder API: new function `JxlDecoderGetIntendedDownsamplingRatio` to get - the intended downsampling ratio of progressive steps, based on the - information in the frame header. - - decoder API: new function `JxlDecoderSetRenderSpotcolors` to allow disabling - rendering of spot colors. - - decoder/encoder API: add two fields to `JXLBasicInfo`: `intrinsic_xsize` - and `intrinsic_ysize` to signal the intrinsic size. - - encoder API: ability to add metadata boxes, added new functions - `JxlEncoderAddBox`, `JxlEncoderUseBoxes`, `JxlEncoderCloseBoxes` and - `JxlEncoderCloseFrames`. - - encoder API: added ability to set several encoder options / extra fields to - frames using `JxlEncoderSetFrameName`, `JxlEncoderFrameSettingsSetOption`, - `JxlEncoderFrameSettingsSetFloatOption`. - - encoder API: added ability to check required codestream compatibility level - and force specified using `JxlEncoderGetRequiredCodestreamLevel` and - `JxlEncoderSetCodestreamLevel`. - - encoder API: added ability to force emitting box-based container format - using `JxlEncoderUseContainer`. - - encoder API: added ability to store JPEG metadata for lossless reconstruction - using `JxlEncoderStoreJPEGMetadata` - - encoder API: new functions `JxlEncoderSetFrameHeader` and - `JxlEncoderSetExtraChannelBlendInfo` to set animation - and blending parameters of the frame, and `JxlEncoderInitFrameHeader` and - `JxlEncoderInitBlendInfo` to initialize the structs to set. - - encoder API: ability to encode arbitrary extra channels: - `JxlEncoderInitExtraChannelInfo`, `JxlEncoderSetExtraChannelInfo`, - `JxlEncoderSetExtraChannelName` and `JxlEncoderSetExtraChannelBuffer`. - - encoder API: ability to plug custom CMS implementation using - `JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms)` - - encoder API: added `JxlEncoderGetError` to retrieve last encoder error. - -### Changed -- decoder API: using `JxlDecoderCloseInput` at the end of all input is required - when using JXL_DEC_BOX, and is now also encouraged in other cases, but not - required in those other cases for backwards compatibility. -- encoder API: `JxlEncoderCloseInput` now closes both frames and boxes input. -- CLI: `cjxl` and `djxl` have been reimplemented on the base of public decoder - and encoder API; dropped dependency on `gflags` for argument parsing. - -### Deprecated -- decoder API: `JXL_DEC_EXTENSIONS` event: use `JXL_DEC_BASIC_INFO` -- decoder / encoder API: pixel types `JXL_TYPE_BOOLEAN` and `JXL_TYPE_UINT32`: - consider using `JXL_TYPE_UINT8` and `JXL_TYPE_FLOAT` correspondingly. -- decoder API: pixel format parameter for `JxlDecoderGetColorAsEncodedProfile` - and `JxlDecoderGetICCProfileSize`: pass `NULL`. -- decoder API: `JxlDecoderDefaultPixelFormat` -- encoder API: `JxlEncoderOptions`: use `JxlEncoderFrameSettings` instead. -- encoder API: `JxlEncoderOptionsCreate`: use `JxlEncoderFrameSettingsCreate` - instead. -- encoder API: `JxlEncoderOptionsSetDistance`: use `JxlEncoderSetFrameDistance` - instead. -- encoder API: `JxlEncoderOptionsSetLossless`: use `JxlEncoderSetFrameLossless` - instead. -- encoder API: `JxlEncoderOptionsSetEffort`: use - `JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, effort)` - instead. -- encoder API: `JxlEncoderOptionsSetDecodingSpeed`: use - `JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier)` - instead. -- encoder API: deprecated `JXL_ENC_NOT_SUPPORTED`, the encoder returns - `JXL_ENC_ERROR` instead and there is no need to handle - `JXL_ENC_NOT_SUPPORTED`. - -## [0.6.1] - 2021-10-29 -### Changed - - Security: Fix OOB read in splines rendering (#735 - - [CVE-2021-22563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22563)) - - Security: Fix OOB copy (read/write) in out-of-order/multi-threaded decoding - (#708 - [CVE-2021-22564](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22564)) - - Fix segfault in `djxl` tool with `--allow_partial_files` flag (#781). - - Fix border in extra channels when using upsampling (#796) - -## [0.6] - 2021-10-04 -### Added - - API: New functions to decode extra channels: - `JxlDecoderExtraChannelBufferSize` and `JxlDecoderSetExtraChannelBuffer`. - - API: New function `JxlEncoderInitBasicInfo` to initialize `JxlBasicInfo` - (only needed when encoding). NOTE: it is now required to call this function - when using the encoder. Padding was added to the struct for forward - compatibility. - - API: Support for encoding oriented images. - - API: FLOAT16 support in the encoder API. - - Rewrite of the GDK pixbuf loader plugin. Added proper color management and - animation support. - - Rewrite of GIMP plugin. Added compression parameters dialog and switched to - using the public C API. - - Debian packages for GDK pixbuf loader (`libjxl-gdk-pixbuf`) and GIMP - (`libjxl-gimp-plugin`) plugins. - - `cjxl`/`djxl` support for `stdin` and `stdout`. - -### Changed - - API: Renamed the field `alpha_associated` in `JxlExtraChannelInfo` to - `alpha_premultiplied`, to match the corresponding name in `JxlBasicInfo`. - - Improved the 2x2 downscaling method in the encoder for the optional color - channel resampling for low bit rates. - - Fixed: the combination of floating point original data, XYB color encoding, - and Modular mode was broken (in both encoder and decoder). It now works. - NOTE: this can cause the current encoder to write jxl bitstreams that do - not decode with the old decoder. In particular this will happen when using - cjxl with PFM, EXR, or floating point PSD input, and a combination of XYB - and modular mode is used (which caused an encoder error before), e.g. - using options like `-m -q 80` (lossy modular), `-d 4.5` or `--progressive_dc=1` - (modular DC frame), or default lossy encoding on an image where patches - end up being used. There is no problem when using cjxl with PNG, JPEG, GIF, - APNG, PPM, PGM, PGX, or integer (8-bit or 16-bit) PSD input. - - `libjxl` static library now bundles skcms, fixing static linking in - downstream projects when skcms is used. - - Spline rendering performance improvements. - - Butteraugli changes for less visual masking. - -## [0.5] - 2021-08-02 -### Added - - API: New function to decode the image using a callback outputting a part of a - row per call. - - API: 16-bit float output support. - - API: `JxlDecoderRewind` and `JxlDecoderSkipFrames` functions to skip more - efficiently to earlier animation frames. - - API: `JxlDecoderSetPreferredColorProfile` function to choose color profile in - certain circumstances. - - encoder: Adding `center_x` and `center_y` flags for more control of the tile - order. - - New encoder speeds `lightning` (1) and `thunder` (2). - -### Changed - - Re-licensed the project under a BSD 3-Clause license. See the - [LICENSE](LICENSE) and [PATENTS](PATENTS) files for details. - - Full JPEG XL part 1 specification support: Implemented all the spec required - to decode files to pixels, including cases that are not used by the encoder - yet. Part 2 of the spec (container format) is final but not fully implemented - here. - - Butteraugli metric improvements. Exact numbers are different from previous - versions. - - Memory reductions during decoding. - - Reduce the size of the jxl_dec library by removing dependencies. - - A few encoding speedups. - - Clarify the security policy. - - Significant encoding improvements (~5 %) and less ringing. - - Butteraugli metric to have some less masking. - - `cjxl` flag `--speed` is deprecated and replaced by the `--effort` synonym. - -### Removed -- API for returning a downsampled DC was deprecated - (`JxlDecoderDCOutBufferSize` and `JxlDecoderSetDCOutBuffer`) and will be - removed in the next release. - -## [0.3.7] - 2021-03-29 -### Changed - - Fix a rounding issue in 8-bit decoding. - -## [0.3.6] - 2021-03-25 -### Changed - - Fix a bug that could result in the generation of invalid codestreams as - well as failure to decode valid streams. - -## [0.3.5] - 2021-03-23 -### Added - - New encode-time options for faster decoding at the cost of quality. - - Man pages for cjxl and djxl. - -### Changed - - Memory usage improvements. - - Faster decoding to 8-bit output with the C API. - - GIMP plugin: avoid the sRGB conversion dialog for sRGB images, do not show - a console window on Windows. - - Various bug fixes. - -## [0.3.4] - 2021-03-16 -### Changed - - Improved box parsing. - - Improved metadata handling. - - Performance and memory usage improvements. - -## [0.3.3] - 2021-03-05 -### Changed - - Performance improvements for small images. - - Add a (flag-protected) non-high-precision mode with better speed. - - Significantly speed up the PQ EOTF. - - Allow optional HDR tone mapping in djxl (--tone_map, --display_nits). - - Change the behavior of djxl -j to make it consistent with cjxl (#153). - - Improve image quality. - - Improve EXIF handling. - -## [0.3.2] - 2021-02-12 -### Changed - - Fix embedded ICC encoding regression - [#149](https://gitlab.com/wg1/jpeg-xl/-/issues/149). - -## [0.3.1] - 2021-02-10 -### Changed - - New experimental Butteraugli API (`jxl/butteraugli.h`). - - Encoder improvements to low quality settings. - - Bug fixes, including fuzzer-found potential security bug fixes. - - Fixed `-q 100` and `-d 0` not triggering lossless modes. - -## [0.3] - 2021-01-29 -### Changed - - Minor change to the Decoder C API to accommodate future work for other ways - to provide input. - - Future decoder C API changes will be backwards compatible. - - Lots of bug fixes since the previous version. - -## [0.2] - 2020-12-24 -### Added - - JPEG XL bitstream format is frozen. Files encoded with 0.2 will be supported - by future versions. - -### Changed - - Files encoded with previous versions are not supported. - -## [0.1.1] - 2020-12-01 - -## [0.1] - 2020-11-14 -### Added - - Initial release of an encoder (`cjxl`) and decoder (`djxl`) that work - together as well as a benchmark tool for comparison with other codecs - (`benchmark_xl`). - - Note: JPEG XL format is in the final stages of standardization, minor changes - to the codestream format are still possible but we are not expecting any - changes beyond what is required by bug fixing. - - API: new decoder API in C, check the `examples/` directory for its example - usage. The C API is a work in progress and likely to change both in API and - ABI in future releases. diff --git a/third_party/jpeg-xl/CMakeLists.txt b/third_party/jpeg-xl/CMakeLists.txt deleted file mode 100644 index 2e5a1650e9242..0000000000000 --- a/third_party/jpeg-xl/CMakeLists.txt +++ /dev/null @@ -1,547 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# Ubuntu focal ships with cmake 3.16. -cmake_minimum_required(VERSION 3.16...3.27) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -project(LIBJXL LANGUAGES C CXX) - -# TODO(sboukortt): remove once oss-fuzz passes -DBUILD_SHARED_LIBS=OFF -if(JPEGXL_ENABLE_FUZZERS) - message(STATUS "Fuzzer build detected, building static libs") - set(BUILD_SHARED_LIBS OFF) -endif() - -message(STATUS "CMAKE_SYSTEM_PROCESSOR is ${CMAKE_SYSTEM_PROCESSOR}") -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag("-fsanitize=fuzzer-no-link" CXX_FUZZERS_SUPPORTED) -check_cxx_compiler_flag("-fmacro-prefix-map=OLD=NEW" CXX_MACRO_PREFIX_MAP) -check_cxx_compiler_flag("-fno-rtti" CXX_NO_RTTI_SUPPORTED) - -# Add "DebugOpt" CMake build type. Unlike builtin DEBUG it is optimized. -string(REGEX REPLACE "-DNDEBUG " "" CMAKE_CXX_FLAGS_DEBUGOPT "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDEBUG" ) -string(REGEX REPLACE "-DNDEBUG " "" CMAKE_C_FLAGS_DEBUGOPT "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDEBUG" ) - -# Enabled PIE binaries by default if supported. -include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED) -if(CHECK_PIE_SUPPORTED) - check_pie_supported(LANGUAGES CXX) - if(CMAKE_CXX_LINK_PIE_SUPPORTED) - set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) - endif() -endif() - -if(PROVISION_DEPENDENCIES) - # Run script to provision dependencies. - find_program (BASH_PROGRAM bash) - if(BASH_PROGRAM) - execute_process( - COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/deps.sh - RESULT_VARIABLE PROVISION_DEPENDENCIES_RESULT) - endif() - if(NOT PROVISION_DEPENDENCIES_RESULT EQUAL "0") - message(FATAL_ERROR "${CMAKE_CURRENT_SOURCE_DIR}/deps.sh failed with ${PROVISION_DEPENDENCIES_RESULT}") - endif() -endif() - -### Project build options: -if(CXX_FUZZERS_SUPPORTED) - # Enabled by default except on arm64, Windows and Apple builds. - set(ENABLE_FUZZERS_DEFAULT true) -endif() -find_package(PkgConfig) -if(NOT APPLE AND NOT WIN32 AND NOT HAIKU AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") - pkg_check_modules(TCMallocMinimalVersionCheck QUIET IMPORTED_TARGET - libtcmalloc_minimal) - if(TCMallocMinimalVersionCheck_FOUND AND - NOT TCMallocMinimalVersionCheck_VERSION VERSION_EQUAL 2.8.0) - # Enabled by default except on Windows and Apple builds for - # tcmalloc != 2.8.0. tcmalloc 2.8.1 already has a fix for this issue. - set(ENABLE_TCMALLOC_DEFAULT true) - else() - message(STATUS - "tcmalloc version ${TCMallocMinimalVersionCheck_VERSION} -- " - "tcmalloc 2.8.0 disabled due to " - "https://github.com/gperftools/gperftools/issues/1204") - endif() -endif() - -check_cxx_source_compiles( - "int main() { - #if !defined(HWY_DISABLED_TARGETS) - static_assert(false, \"HWY_DISABLED_TARGETS is not defined\"); - #endif - return 0; - }" - JXL_HWY_DISABLED_TARGETS_FORCED -) - -if((SANITIZER STREQUAL "msan") OR EMSCRIPTEN) - set(BUNDLE_LIBPNG_DEFAULT YES) -else() - set(BUNDLE_LIBPNG_DEFAULT NO) -endif() - - -if(EXISTS "${PROJECT_SOURCE_DIR}/third_party/libjpeg-turbo/jconfig.h.in") - set(ENABLE_JPEGLI_DEFAULT YES) -else() - set(ENABLE_JPEGLI_DEFAULT NO) - message(STATUS "libjpeg-turbo submodule is absent; not enabling jpegli") -endif() - -include(TestBigEndian) -test_big_endian(ARCH_IS_BIG_ENDIAN) -if(ARCH_IS_BIG_ENDIAN) - set(ENABLE_SKCMS_DEFAULT NO) - message(STATUS "Big-endian architecture detected; defaulting to lcms2 instead of skcms") -else() - set(ENABLE_SKCMS_DEFAULT YES) -endif() - -# Standard cmake naming for building shared libraries. -get_property(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) -option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ${SHARED_LIBS_SUPPORTED}) - -set(JPEGXL_ENABLE_FUZZERS ${ENABLE_FUZZERS_DEFAULT} CACHE BOOL - "Build JPEGXL fuzzer targets.") -set(JPEGXL_ENABLE_DEVTOOLS false CACHE BOOL - "Build JPEGXL developer tools.") -set(JPEGXL_ENABLE_TOOLS true CACHE BOOL - "Build JPEGXL user tools: cjxl and djxl.") -set(JPEGXL_ENABLE_JPEGLI ${ENABLE_JPEGLI_DEFAULT} CACHE BOOL - "Build jpegli library.") -set(JPEGXL_ENABLE_JPEGLI_LIBJPEG true CACHE BOOL - "Build libjpeg.so shared library based on jpegli.") -set(JPEGXL_INSTALL_JPEGLI_LIBJPEG false CACHE BOOL - "Install jpegli version of libjpeg.so system-wide.") -set(JPEGLI_LIBJPEG_LIBRARY_VERSION "62.3.0" CACHE STRING - "Library version of the libjpeg.so shared library that we build.") -set(JPEGLI_LIBJPEG_LIBRARY_SOVERSION "62" CACHE STRING - "Library so-version of the libjpeg.so shared library that we build.") -set(JPEGXL_ENABLE_DOXYGEN true CACHE BOOL - "Generate C API documentation using Doxygen.") -set(JPEGXL_ENABLE_MANPAGES true CACHE BOOL - "Build and install man pages for the command-line tools.") -set(JPEGXL_ENABLE_BENCHMARK true CACHE BOOL - "Build JPEGXL benchmark tools.") -set(JPEGXL_ENABLE_EXAMPLES true CACHE BOOL - "Build JPEGXL library usage examples.") -set(JPEGXL_BUNDLE_LIBPNG ${BUNDLE_LIBPNG_DEFAULT} CACHE BOOL - "Build libpng from source and link it statically.") -set(JPEGXL_ENABLE_JNI true CACHE BOOL - "Build JPEGXL JNI Java wrapper, if Java dependencies are installed.") -set(JPEGXL_ENABLE_SJPEG true CACHE BOOL - "Build JPEGXL with support for encoding with sjpeg.") -set(JPEGXL_ENABLE_OPENEXR true CACHE BOOL - "Build JPEGXL with support for OpenEXR if available.") -set(JPEGXL_ENABLE_SKCMS ${ENABLE_SKCMS_DEFAULT} CACHE BOOL - "Build with skcms instead of lcms2.") -set(JPEGXL_ENABLE_VIEWERS false CACHE BOOL - "Build JPEGXL viewer tools for evaluation.") -set(JPEGXL_ENABLE_TCMALLOC ${ENABLE_TCMALLOC_DEFAULT} CACHE BOOL - "Build JPEGXL using gperftools (tcmalloc) allocator.") -set(JPEGXL_ENABLE_PLUGINS false CACHE BOOL - "Build third-party plugins to support JPEG XL in other applications.") -set(JPEGXL_ENABLE_COVERAGE false CACHE BOOL - "Enable code coverage tracking for libjxl. This also enables debug and disables optimizations.") -set(JPEGXL_ENABLE_SIZELESS_VECTORS false CACHE BOOL - "Builds in support for SVE/RVV vectorization") -set(JPEGXL_ENABLE_TRANSCODE_JPEG true CACHE BOOL - "Builds in support for decoding transcoded JXL files back to JPEG,\ - disabling it makes the decoder reject JXL_DEC_JPEG_RECONSTRUCTION events,\ - (default enabled)") -set(JPEGXL_ENABLE_BOXES true CACHE BOOL - "Builds in support for decoding boxes in JXL files,\ - disabling it makes the decoder reject JXL_DEC_BOX events,\ - (default enabled)") -set(JPEGXL_STATIC false CACHE BOOL - "Build tools as static binaries.") -set(JPEGXL_WARNINGS_AS_ERRORS false CACHE BOOL - "Treat warnings as errors during compilation.") -set(JPEGXL_DEP_LICENSE_DIR "" CACHE STRING - "Directory where to search for system dependencies \"copyright\" files.") -set(JPEGXL_FORCE_NEON false CACHE BOOL - "Set flags to enable NEON in arm if not enabled by your toolchain.") -set(JPEGXL_TEST_TOOLS false CACHE BOOL - "Run scripts that test the encoding / decoding tools.") -set(JPEGXL_ENABLE_AVX512 false CACHE BOOL - "Build with AVX512 support (faster on CPUs that support it, but larger binary size).") -set(JPEGXL_ENABLE_AVX512_SPR false CACHE BOOL - "Build with AVX-512FP16 support (faster on CPUs that support it, but larger binary size).") -set(JPEGXL_ENABLE_AVX512_ZEN4 false CACHE BOOL -"Build with Zen4-optimized AVX512 support (faster on CPUs that support it, but larger binary size).") -set(JPEGXL_ENABLE_WASM_THREADS true CACHE BOOL - "Builds WASM modules with threads support") - -# Force system dependencies. -set(JPEGXL_FORCE_SYSTEM_BROTLI false CACHE BOOL - "Force using system installed brotli instead of third_party/brotli source.") -set(JPEGXL_FORCE_SYSTEM_GTEST false CACHE BOOL - "Force using system installed googletest (gtest) instead of third_party/googletest source.") -set(JPEGXL_FORCE_SYSTEM_LCMS2 false CACHE BOOL - "Force using system installed lcms2 instead of third_party/lcms source.") -set(JPEGXL_FORCE_SYSTEM_HWY false CACHE BOOL - "Force using system installed highway (libhwy-dev) instead of third_party/highway source.") - -# Check minimum compiler versions. Older compilers are not supported and fail -# with hard to understand errors. -if (NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID) - message(FATAL_ERROR "Different C/C++ compilers set: " - "${CMAKE_C_COMPILER_ID} vs ${CMAKE_CXX_COMPILER_ID}") -endif() - -message(STATUS - "Compiled IDs C:${CMAKE_C_COMPILER_ID}, C++:${CMAKE_CXX_COMPILER_ID}") - -set(JXL_HWY_INCLUDE_DIRS "$,hwy::hwy,hwy>,INTERFACE_INCLUDE_DIRECTORIES>>") -# Always disable SSSE3 since it is rare to have SSSE3 but not SSE4 -set(HWY_DISABLED_TARGETS "HWY_SSSE3") -if (NOT JPEGXL_ENABLE_AVX512) - message(STATUS "Disabled AVX512 (set JPEGXL_ENABLE_AVX512 to enable it)") - set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_AVX3") - add_definitions(-DFJXL_ENABLE_AVX512=0) -endif() -if (NOT JPEGXL_ENABLE_AVX512_SPR) - message(STATUS "Disabled AVX512_SPR (set JPEGXL_ENABLE_AVX512_SPR to enable it)") - set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_AVX3_SPR") -endif() -if (NOT JPEGXL_ENABLE_AVX512_ZEN4) - message(STATUS "Disabled AVX512_ZEN4 (set JPEGXL_ENABLE_AVX512_ZEN4 to enable it)") - set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_AVX3_ZEN4") -endif() - - - -# CMAKE_EXPORT_COMPILE_COMMANDS is used to generate the compilation database -# used by clang-tidy. -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -if(JPEGXL_STATIC) - set(BUILD_SHARED_LIBS 0) - - # https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170 - # https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html - set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$>:MultiThreaded>" CACHE STRING "") - - # Clang developers say that in case to use "static" we have to build stdlib - # ourselves; for real use case we don't care about stdlib, as it is "granted", - # so just linking all other libraries is fine. - if (NOT MSVC) - string(APPEND CMAKE_EXE_LINKER_FLAGS " -static") - endif() - if ((NOT WIN32 AND NOT APPLE) OR CYGWIN OR MINGW) - set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - string(APPEND CMAKE_EXE_LINKER_FLAGS " -static-libgcc -static-libstdc++") - endif() -endif() # JPEGXL_STATIC - -# Threads -set(THREADS_PREFER_PTHREAD_FLAG YES) -find_package(Threads REQUIRED) - -# These settings are important to drive check_cxx_source_compiles -# See CMP0067 (min cmake version is 3.10 anyway) - -if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES) - set(CMAKE_CXX_STANDARD 17) -else() - if ("cxx_std_14" IN_LIST CMAKE_CXX_COMPILE_FEATURES) - set(CMAKE_CXX_STANDARD 14) - else() - set(CMAKE_CXX_STANDARD 11) - endif() -endif() -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_STANDARD_REQUIRED YES) - -# Atomics -find_package(Atomics REQUIRED) - -if(JPEGXL_STATIC) - if (MINGW) - # In MINGW libstdc++ uses pthreads directly. When building statically a - # program (regardless of whether the source code uses pthread or not) the - # toolchain will add stdc++ and pthread to the linking step but stdc++ will - # be linked statically while pthread will be linked dynamically. - # To avoid this and have pthread statically linked with need to pass it in - # the command line with "-Wl,-Bstatic -lpthread -Wl,-Bdynamic" but the - # linker will discard it if not used by anything else up to that point in - # the linker command line. If the program or any dependency don't use - # pthread directly -lpthread is discarded and libstdc++ (added by the - # toolchain later) will then use the dynamic version. For this we also need - # to pass -lstdc++ explicitly before -lpthread. For pure C programs -lstdc++ - # will be discarded anyway. - # This adds these flags as dependencies for *all* targets. Adding this to - # CMAKE_EXE_LINKER_FLAGS instead would cause them to be included before any - # object files and therefore discarded. This should be set in the - # INTERFACE_LINK_LIBRARIES of Threads::Threads but some third_part targets - # don't depend on it. - link_libraries(-Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic) - elseif(CMAKE_USE_PTHREADS_INIT) - # "whole-archive" is not supported on OSX. - if (NOT APPLE) - # Set pthreads as a whole-archive, otherwise weak symbols in the static - # libraries will discard pthreads symbols leading to segmentation fault at - # runtime. - message(STATUS "Using -lpthread as --whole-archive") - set_target_properties(Threads::Threads PROPERTIES - INTERFACE_LINK_LIBRARIES - "-Wl,--whole-archive;-lpthread;-Wl,--no-whole-archive") - endif() - endif() -endif() # JPEGXL_STATIC - -if (EMSCRIPTEN AND JPEGXL_ENABLE_WASM_THREADS) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") -endif() - -if (CXX_MACRO_PREFIX_MAP) - add_compile_options(-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.) -endif() - -if (CXX_NO_RTTI_SUPPORTED) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -endif() - -# Internal flags for coverage builds: -set(JPEGXL_COVERAGE_FLAGS) -set(JPEGXL_COVERAGE_LINK_FLAGS) - -if (MSVC) - # TODO(janwas): add flags - add_definitions(-D_CRT_SECURE_NO_WARNINGS) -else () - # Global compiler flags for all targets here and in subdirectories. - add_definitions( - # Avoid changing the binary based on the current time and date. - -D__DATE__="redacted" - -D__TIMESTAMP__="redacted" - -D__TIME__="redacted" - ) - - # TODO(eustas): JXL currently compiles, but does not pass tests... - if (NOT JXL_HWY_DISABLED_TARGETS_FORCED) - if (NOT JPEGXL_ENABLE_SIZELESS_VECTORS) - set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_SVE|HWY_SVE2|HWY_SVE_256|HWY_SVE2_128|HWY_RVV") - endif() - add_compile_options($<$>:-DHWY_DISABLED_TARGETS=\(${HWY_DISABLED_TARGETS}\)>) - endif() - - # Machine flags. - add_compile_options(-funwind-tables) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options("SHELL:-Xclang -mrelax-all") - endif() - if (CXX_CONSTRUCTOR_ALIASES_SUPPORTED) - add_compile_options("SHELL:-Xclang -mconstructor-aliases") - endif() - - if(WIN32) - # Not supported by clang-cl, but frame pointers are default on Windows - else() - add_compile_options(-fno-omit-frame-pointer) - endif() - - # CPU flags - remove once we have NEON dynamic dispatch - - # TODO(janwas): this also matches M1, but only ARMv7 is intended/needed. - if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") - if(JPEGXL_FORCE_NEON) - # GCC requires these flags, otherwise __ARM_NEON is undefined. - add_compile_options(-mfpu=neon-vfpv4 -mfloat-abi=hard) - endif() - endif() - - add_compile_options( - # Ignore this to allow redefining __DATE__ and others. - -Wno-builtin-macro-redefined - - # Global warning settings. - -Wall - ) - - if (JPEGXL_WARNINGS_AS_ERRORS) - add_compile_options(-Werror) - endif () - - if(JPEGXL_ENABLE_COVERAGE) - set(JPEGXL_COVERAGE_FLAGS - -g -O0 -fprofile-arcs -ftest-coverage - ) - set(JPEGXL_COVERAGE_LINK_FLAGS - --coverage - ) - endif() # JPEGXL_ENABLE_COVERAGE -endif () # !MSVC - -include(GNUInstallDirs) - -# Separately build/configure testing frameworks and other third_party libraries -# to allow disabling tests in those libraries. -include(third_party/testing.cmake) -add_subdirectory(third_party) -# Copy the JXL license file to the output build directory. -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" - ${PROJECT_BINARY_DIR}/LICENSE.jpeg-xl COPYONLY) - -# Enable tests regardless of where they are defined. -enable_testing() -include(CTest) -# Specify default location of `testdata`: -if(NOT DEFINED JPEGXL_TEST_DATA_PATH) - set(JPEGXL_TEST_DATA_PATH "${PROJECT_SOURCE_DIR}/testdata") -endif() - -# Libraries. -add_subdirectory(lib) - -if(BUILD_TESTING) - # Script to run tests over the source code in bash. - find_program (BASH_PROGRAM bash) - if(BASH_PROGRAM) - add_test( - NAME bash_test - COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/bash_test.sh) - endif() -endif() # BUILD_TESTING - -# Documentation generated by Doxygen -if(JPEGXL_ENABLE_DOXYGEN) - find_package(Doxygen) - if(DOXYGEN_FOUND) - set(DOXYGEN_GENERATE_HTML "YES") - set(DOXYGEN_GENERATE_XML "YES") - set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include") - if(JPEGXL_WARNINGS_AS_ERRORS) - set(DOXYGEN_WARN_AS_ERROR "YES") - endif() - set(DOXYGEN_QUIET "YES") - doxygen_add_docs(doc - "${CMAKE_CURRENT_SOURCE_DIR}/lib/include" - "${CMAKE_CURRENT_SOURCE_DIR}/doc/api.txt" - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - COMMENT "Generating C API documentation") - - # Add sphinx doc build step for readthedocs.io (requires doxygen too). - find_program(SPHINX_BUILD_PROGRAM sphinx-build) - if(SPHINX_BUILD_PROGRAM) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent" - COMMENT "Generating readthedocs.io output on ${CMAKE_CURRENT_BINARY_DIR}/rtd" - COMMAND ${SPHINX_BUILD_PROGRAM} -q -W -b html -j auto - ${CMAKE_SOURCE_DIR}/doc/sphinx - ${CMAKE_CURRENT_BINARY_DIR}/rtd - DEPENDS doc - ) - # This command runs the documentation generation every time since the output - # target file doesn't exist. - add_custom_target(rtd-html - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent - ) - else() # SPHINX_BUILD_PROGRAM\ - message(WARNING "sphinx-build not found, skipping rtd documentation") - endif() # SPHINX_BUILD_PROGRAM - - else() - # Create a "doc" target for compatibility since "doc" is not otherwise added to - # the build when doxygen is not installed. - add_custom_target(doc false - COMMENT "Error: Can't generate doc since Doxygen not installed.") - endif() # DOXYGEN_FOUND -endif() # JPEGXL_ENABLE_DOXYGEN - -if(JPEGXL_ENABLE_MANPAGES) - find_program(ASCIIDOC a2x) - if(ASCIIDOC) - file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1) - if(ASCIIDOC_SHEBANG MATCHES "sh$" OR ASCIIDOC_SHEBANG MATCHES "libexec/bin/python$" OR MINGW) - set(ASCIIDOC_PY_FOUND ON) - # Run the program directly and set ASCIIDOC as empty. - set(ASCIIDOC_PY "${ASCIIDOC}") - set(ASCIIDOC "") - elseif(ASCIIDOC_SHEBANG MATCHES "python2") - find_package(Python2 COMPONENTS Interpreter) - set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}") - set(ASCIIDOC_PY Python2::Interpreter) - elseif(ASCIIDOC_SHEBANG MATCHES "python3") - find_package(Python3 COMPONENTS Interpreter) - set(ASCIIDOC_PY_FOUND "${Python3_Interpreter_FOUND}") - set(ASCIIDOC_PY Python3::Interpreter) - else() - find_package(Python COMPONENTS Interpreter QUIET) - if(NOT Python_Interpreter_FOUND) - find_program(ASCIIDOC_PY python) - if(ASCIIDOC_PY) - set(ASCIIDOC_PY_FOUND ON) - endif() - else() - set(ASCIIDOC_PY_FOUND "${Python_Interpreter_FOUND}") - set(ASCIIDOC_PY Python::Interpreter) - endif() - endif() - - if (ASCIIDOC_PY_FOUND) - set(MANPAGE_FILES "") - set(MANPAGES "") - foreach(PAGE IN ITEMS cjxl djxl) - # Invoking the Python interpreter ourselves instead of running the a2x binary - # directly is necessary on MSYS2, otherwise it is run through cmd.exe which - # does not recognize it. - add_custom_command( - OUTPUT "${PAGE}.1" - COMMAND "${ASCIIDOC_PY}" - ARGS ${ASCIIDOC} - --format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt" - MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt") - list(APPEND MANPAGE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PAGE}.1") - list(APPEND MANPAGES "${PAGE}.1") - endforeach() - add_custom_target(manpages ALL DEPENDS ${MANPAGES}) - install(FILES ${MANPAGE_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) - endif() # ASCIIDOC_PY_FOUND - else() - message(WARNING "asciidoc was not found, the man pages will not be installed.") - endif() # ASCIIDOC -endif() # JPEGXL_ENABLE_MANPAGES - -# Example usage code. -if (JPEGXL_ENABLE_EXAMPLES) - include(examples/examples.cmake) -endif () - -# Plugins for third-party software -if (JPEGXL_ENABLE_PLUGINS) - add_subdirectory(plugins) -endif () - -# Binary tools -add_subdirectory(tools) - - -macro(list_test_targets out dir) - get_property(dir_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS) - foreach(target ${dir_targets}) - if (target MATCHES ".*_test") - list(APPEND ${out} ${target}) - endif() - endforeach() - get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) - foreach(subdir ${subdirectories}) - list_test_targets(${out} ${subdir}) - endforeach() -endmacro() - -set(all_tests_list) -list_test_targets(all_tests_list ${CMAKE_CURRENT_SOURCE_DIR}) - -if(all_tests_list) - add_custom_target(all_tests) - add_dependencies(all_tests ${all_tests_list}) -endif() diff --git a/third_party/jpeg-xl/CODE_OF_CONDUCT.md b/third_party/jpeg-xl/CODE_OF_CONDUCT.md deleted file mode 100644 index b2d81a32142de..0000000000000 --- a/third_party/jpeg-xl/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,93 +0,0 @@ -# Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of -experience, education, socio-economic status, nationality, personal appearance, -race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, or to ban temporarily or permanently any -contributor for other behaviors that they deem inappropriate, threatening, -offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -This Code of Conduct also applies outside the project spaces when the Project -Steward has a reasonable belief that an individual's behavior may have a -negative impact on the project or its community. - -## Conflict Resolution - -We do not believe that all conflict is bad; healthy debate and disagreement -often yield positive results. However, it is never okay to be disrespectful or -to engage in behavior that violates the project’s code of conduct. - -If you see someone violating the code of conduct, you are encouraged to address -the behavior directly with those involved. Many issues can be resolved quickly -and easily, and this gives people more control over the outcome of their -dispute. If you are unable to resolve the matter for any reason, or if the -behavior is threatening or harassing, report it. We are dedicated to providing -an environment where participants feel welcome and safe. - -Reports should be directed to Jyrki Alakuijala , the -Project Steward(s) for JPEG XL. It is the Project Steward’s duty to -receive and address reported violations of the code of conduct. They will then -work with a committee consisting of representatives from the Open Source -Programs Office and the Google Open Source Strategy team. If for any reason you -are uncomfortable reaching out to the Project Steward, please email -opensource@google.com. - -We will investigate every complaint, but you may not receive a direct response. -We will use our discretion in determining when and how to follow up on reported -incidents, which may range from not taking action to permanent expulsion from -the project and project-sponsored spaces. We will notify the accused of the -report and provide them an opportunity to discuss it before any action is taken. -The identity of the reporter will be omitted from the details of the report -supplied to the accused. In potentially harmful situations, such as ongoing -harassment or threats to anyone's safety, we may take action without notice. - -## Attribution - -This Code of Conduct is adapted from the Contributor Covenant, version 1.4, -available at -https://www.contributor-covenant.org/version/1/4/code-of-conduct.html diff --git a/third_party/jpeg-xl/CONTRIBUTING.md b/third_party/jpeg-xl/CONTRIBUTING.md deleted file mode 100644 index 1a255ba40b33b..0000000000000 --- a/third_party/jpeg-xl/CONTRIBUTING.md +++ /dev/null @@ -1,133 +0,0 @@ -# Contributing to libjxl - -## Contributing with bug reports - -For security-related issues please see [SECURITY.md](SECURITY.md). - -We welcome suggestions, feature requests and bug reports. Before opening a new -issue please take a look if there is already an existing one in the following -link: - - * https://github.com/libjxl/libjxl/issues - -## Contributing with patches and Pull Requests - -We'd love to accept your contributions to the JPEG XL Project. Please read -through this section before sending a Pull Request. - -### Contributor License Agreements - -Our project is open source under the terms outlined in the [LICENSE](LICENSE) -and [PATENTS](PATENTS) files. Before we can accept your contributions, even for -small changes, there are just a few small guidelines you need to follow: - -Please fill out either the individual or corporate Contributor License Agreement -(CLA) with Google. JPEG XL Project is an an effort by multiple individuals and -companies, including the initial contributors Cloudinary and Google, but Google -is the legal entity in charge of receiving these CLA and relicensing this -software: - - * If you are an individual writing original source code and you're sure you - own the intellectual property, then you'll need to sign an [individual - CLA](https://code.google.com/legal/individual-cla-v1.0.html). - - * If you work for a company that wants to allow you to contribute your work, - then you'll need to sign a [corporate - CLA](https://code.google.com/legal/corporate-cla-v1.0.html). - -Follow either of the two links above to access the appropriate CLA and -instructions for how to sign and return it. Once we receive it, we'll be able -to accept your pull requests. - -***NOTE***: Only original source code from you and other people that have signed -the CLA can be accepted into the main repository. - -### License - -Contributions are licensed under the project's [LICENSE](LICENSE). Each new -file must include the following header when possible, with comment style adapted -to the language as needed: - -``` -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -``` - -### Code Reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -### Contribution philosophy - - * Prefer small changes, even if they don't implement a complete feature. Small - changes are easier to review and can be submitted faster. Think about what's - the smallest unit you can send that makes sense to review and submit in - isolation. For example, new modules that are not yet used by the tools but - have their own unittests are ok. If you have unrelated changes that - you discovered while working on something else, please send them in a - different Pull Request. If your are refactoring code and changing - functionality try to send the refactor first without any change in - functionality. Reviewers may ask you to split a Pull Request and it is - easier to create a smaller change from the beginning. - - * Describe your commits. Add a meaningful description to your commit message, - explain what you are changing if it is not trivially obvious, but more - importantly explain *why* you are making those changes. For example "Fix - build" is not a good commit message, describe what build and if it makes sense - why is this fixing it or why was it failing without this. It is very likely - that people far in the future without any context you have right now will be - looking at your commit trying to figure out why was the change introduced. If - related to an issue in this or another repository include a link to it. - - * Code Style: We follow the [Google C++ Coding - Style](https://google.github.io/styleguide/cppguide.html). A - [clang-format](https://clang.llvm.org/docs/ClangFormat.html) configuration - file is available to automatically format your code, you can invoke it with - the `./ci.sh lint` helper tool. - - * Testing: Test your change and explain in the commit message *how* your - commit was tested. For example adding unittests or in some cases just testing - with the existing ones is enough. In any case, mention what testing was - performed so reviewers can evaluate whether that's enough testing. In many - cases, testing that the Continuous Integration workflow passes is enough. - - * Make one commit per Pull Request / review, unless there's a good reason not - to. If you have multiple changes send multiple Pull Requests and each one can - have its own review. - - * When addressing comments from reviewers prefer to squash or fixup your - edits and force-push your commit. When merging changes into the repository we - don't want to include the history of code review back and forth changes or - typos. Reviewers can click on the "force-pushed" automatic comment on a Pull - Request to see the changes between versions. We use "Rebase and merge" policy - to keep a linear git history which is easier to reason about. - - * Your change must pass the build and test workflows. There's a `ci.sh` script - to help building and testing these configurations. See [building and - testing](doc/building_and_testing.md) for more details. - -### Contributing checklist. - - * Sign the CLA (only needed once per user, see above). - - * AUTHORS: You can add your name to the [AUTHORS](AUTHORS) file. - - * Style guide. Check `./ci.sh lint`. - - * Meaningful commit description: What and *why*, links to issues, testing - procedure. - - * Squashed multiple edits into a single commit. - - * Upload your changes to your fork and [create a Pull - Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). - -# Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google.com/conduct/). diff --git a/third_party/jpeg-xl/CONTRIBUTORS b/third_party/jpeg-xl/CONTRIBUTORS deleted file mode 100644 index 848096f9219b7..0000000000000 --- a/third_party/jpeg-xl/CONTRIBUTORS +++ /dev/null @@ -1,23 +0,0 @@ -# This files lists individuals who made significant contributions to the JPEG XL -# code base, such as design, adding features, performing experiments, ... -# Small changes such as a small bugfix or fixing spelling errors are not -# included. If you'd like to be included in this file thanks to a significant -# contribution, feel free to send a pull request changing this file. -Alex Deymo -Alexander Rhatushnyak -Evgenii Kliuchnikov -Iulia-Maria Comșa -Jan Wassenberg -Jon Sneyers -Jyrki Alakuijala -Krzysztof Potempa -Lode Vandevenne -Luca Versari -Martin Bruse -Moritz Firsching -Renata Khasanova -Robert Obryk -Sami Boukortt -Sebastian Gomez-Gonzalez -Thomas Fischbacher -Zoltan Szabadka diff --git a/third_party/jpeg-xl/LICENSE b/third_party/jpeg-xl/LICENSE deleted file mode 100644 index c66034b105f23..0000000000000 --- a/third_party/jpeg-xl/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) the JPEG XL Project Authors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/jpeg-xl/MODULE.bazel b/third_party/jpeg-xl/MODULE.bazel deleted file mode 100644 index cc2397538dfbf..0000000000000 --- a/third_party/jpeg-xl/MODULE.bazel +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "giflib", version = "5.2.1") -bazel_dep(name = "googletest", version = "1.14.0") -bazel_dep(name = "libjpeg_turbo", version = "2.1.91") -bazel_dep(name = "libpng", version = "1.6.40") -bazel_dep(name = "libwebp", version = "1.3.2") -bazel_dep(name = "openexr", version = "3.2.1") diff --git a/third_party/jpeg-xl/MODULE.bazel.lock b/third_party/jpeg-xl/MODULE.bazel.lock deleted file mode 100644 index 7ea00d853cbed..0000000000000 --- a/third_party/jpeg-xl/MODULE.bazel.lock +++ /dev/null @@ -1,132 +0,0 @@ -{ - "lockFileVersion": 11, - "registryFileHashes": { - "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", - "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", - "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/source.json": "06cc0842d241da0c5edc755edb3c7d0d008d304330e57ecf2d6449fb0b633a82", - "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", - "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015", - "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", - "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", - "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", - "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", - "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", - "https://bcr.bazel.build/modules/giflib/5.2.1/MODULE.bazel": "810dbc4275425c89ffe648dd78c537fe2eb1d2a9704d10e950b295263af03366", - "https://bcr.bazel.build/modules/giflib/5.2.1/source.json": "94215af981976c329eaec0083727b155ea89607e61debea50ed508e7963ef9a6", - "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", - "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", - "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5", - "https://bcr.bazel.build/modules/imath/3.1.9/MODULE.bazel": "26fe47ee8137a4c605667fb0d26a5c12b8fb2e758824a376789b287b2f9d424d", - "https://bcr.bazel.build/modules/imath/3.1.9/source.json": "22b7d9e617d4d26626f5ac8fba3cd2bd7a87f7501c99fa847f8d9e2980416e8f", - "https://bcr.bazel.build/modules/libdeflate/1.19/MODULE.bazel": "b7396a2edfd5ce6669509fbdd10db5e8731d60954063699c546c3126c8156824", - "https://bcr.bazel.build/modules/libdeflate/1.19/source.json": "d4604a526efba9b5347309de49673bbe152da465f7c80c7f7ffe6800d8b504d1", - "https://bcr.bazel.build/modules/libjpeg_turbo/2.1.91/MODULE.bazel": "bcc23b7c4866af2d7777ee49db435603ca1e35b90ea0689f8051900fa8c73c6b", - "https://bcr.bazel.build/modules/libjpeg_turbo/2.1.91/source.json": "42ea85708058e2408f229075e1cbeaad13fa2719918ff9c505be5e22b57ef17b", - "https://bcr.bazel.build/modules/libpng/1.6.40/MODULE.bazel": "cc1952a9b5efd4df3dfdb9f9ba2b1c8d88b4fd9b0e474185cb81d90a31c7c453", - "https://bcr.bazel.build/modules/libpng/1.6.40/source.json": "2fe294bf161c2d3f1e04e7cecb6eb2e6c0c198698b23cabc1c4e6ff77d82a86a", - "https://bcr.bazel.build/modules/libwebp/1.3.2/MODULE.bazel": "c60edf34a913daebac9bd2cbe17b84048e4a7a5d3571f70be93c1b1227a69659", - "https://bcr.bazel.build/modules/libwebp/1.3.2/source.json": "e7b8d3047ad9758fda22fcf46bd8b57414b0eb5e7903f4ce888683d778633cf7", - "https://bcr.bazel.build/modules/openexr/3.2.1/MODULE.bazel": "5665fa95490825760943601d618e2d70eb45378ea3f2961c5ec18f23ae8a2106", - "https://bcr.bazel.build/modules/openexr/3.2.1/source.json": "afc17dda6614ff723cc1def634fa4f33534d3d29514b089fa4aa5eb47ba1c65b", - "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", - "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", - "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", - "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", - "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", - "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6", - "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", - "https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b", - "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", - "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", - "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", - "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", - "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", - "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", - "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/7.6.1/source.json": "8f3f3076554e1558e8e468b2232991c510ecbcbed9e6f8c06ac31c93bcf38362", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35", - "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", - "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", - "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c", - "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", - "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", - "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", - "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", - "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", - "https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459", - "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", - "https://bcr.bazel.build/modules/zlib/1.3/MODULE.bazel": "6a9c02f19a24dcedb05572b2381446e27c272cd383aed11d41d99da9e3167a72", - "https://bcr.bazel.build/modules/zlib/1.3/source.json": "b6b43d0737af846022636e6e255fd4a96fee0d34f08f3830e6e0bac51465c37c" - }, - "selectedYankedVersions": {}, - "moduleExtensions": { - "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { - "general": { - "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=", - "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "local_config_apple_cc": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf", - "attributes": {} - }, - "local_config_apple_cc_toolchains": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf_toolchains", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "apple_support~", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, - "@@platforms//host:extension.bzl%host_platform": { - "general": { - "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", - "usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "host_platform": { - "bzlFile": "@@platforms//host:extension.bzl", - "ruleClassName": "host_platform_repo", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [] - } - } - } -} diff --git a/third_party/jpeg-xl/PATENTS b/third_party/jpeg-xl/PATENTS deleted file mode 100644 index c95b8f4105f21..0000000000000 --- a/third_party/jpeg-xl/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the JPEG XL project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of JPEG XL, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of JPEG XL. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of JPEG XL or any code incorporated within this -implementation of JPEG XL constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of JPEG XL -shall terminate as of the date such litigation is filed. diff --git a/third_party/jpeg-xl/README.md b/third_party/jpeg-xl/README.md deleted file mode 100644 index 877eabac8f94d..0000000000000 --- a/third_party/jpeg-xl/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# JPEG XL reference implementation - -[![Build/Test](https://github.com/libjxl/libjxl/actions/workflows/build_test.yml/badge.svg)]( -https://github.com/libjxl/libjxl/actions/workflows/build_test.yml) -[![Build/Test Cross](https://github.com/libjxl/libjxl/actions/workflows/build_test_cross.yml/badge.svg)]( -https://github.com/libjxl/libjxl/actions/workflows/build_test_cross.yml) -[![Conformance](https://github.com/libjxl/libjxl/actions/workflows/conformance.yml/badge.svg)]( -https://github.com/libjxl/libjxl/actions/workflows/conformance.yml) -[![CIFuzz](https://github.com/libjxl/libjxl/actions/workflows/fuzz.yml/badge.svg)]( -https://github.com/libjxl/libjxl/actions/workflows/fuzz.yml) -[![Releases](https://github.com/libjxl/libjxl/actions/workflows/release.yaml/badge.svg)]( -https://github.com/libjxl/libjxl/actions/workflows/release.yaml) -[![Doc](https://readthedocs.org/projects/libjxl/badge/?version=latest)]( -https://libjxl.readthedocs.io/en/latest/?badge=latest) -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7845/badge)]( -https://www.bestpractices.dev/projects/7845) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/libjxl/libjxl/badge)]( -https://securityscorecards.dev/viewer/?uri=github.com/libjxl/libjxl) -[![codecov](https://codecov.io/gh/libjxl/libjxl/branch/main/graph/badge.svg)]( -https://codecov.io/gh/libjxl/libjxl) - -JXL logo - -This repository contains a reference implementation of JPEG XL (encoder and -decoder), called `libjxl`. This software library is -[used by many applications that support JPEG XL](doc/software_support.md). - -JPEG XL was standardized in 2022 as [ISO/IEC 18181](https://jpeg.org/jpegxl/workplan.html). -The [core codestream](doc/format_overview.md#codestream-features) is specified in 18181-1, -the [file format](doc/format_overview.md#file-format-features) in 18181-2. -[Decoder conformance](https://github.com/libjxl/conformance) is defined in 18181-3, -and 18181-4 is the [reference software](https://github.com/libjxl/libjxl). - -The library API, command line options, and tools in this repository are subject -to change, however files encoded with `cjxl` conform to the JPEG XL specification -and can be decoded with current and future `djxl` decoders or the `libjxl` decoding library. - -## Installation - -In most Linux distributions, installing `libjxl` is just a matter of using the package management system. -For example in Debian-based distributions: `apt install libjxl-tools` will install `cjxl` and `djxl` -and other tools like `benchmark_xl` are available in the package `libjxl-devtools`. -On MacOS, you can use [Homebrew](https://brew.sh/): `brew install jpeg-xl`. - -[![libjxl packaging status](https://repology.org/badge/vertical-allrepos/libjxl.svg?exclude_unsupported=1&columns=3&exclude_sources=modules,site&header=libjxl%20packaging%20status)](https://repology.org/project/libjxl/versions) - -From the [releases page](https://github.com/libjxl/libjxl/releases/) the following can be downloaded: - - Windows binaries - - Debian and Ubuntu .deb packages - -Of course you can also [build libjxl from sources](BUILDING.md). - - -## Usage - -To encode a source image to JPEG XL with default settings: - -```bash -cjxl input.png output.jxl -``` - -The desired visual fidelity can be selected using the `--distance` parameter -(in units of just-noticeable difference, where 0 is lossless and the most useful lossy range is 0.5 .. 3.0), -or using `--quality` (on a scale from 0 to 100, roughly matching libjpeg). -The [encode effort](doc/encode_effort.md) can be selected using the `--effort` parameter. - -For more settings run `cjxl --help` or for a full list of options -run `cjxl -v -v --help`. - -To decode a JPEG XL file run: - -```bash -djxl input.jxl output.png -``` - -When possible, `cjxl`/`djxl` are able to read/write the following image formats: -OpenEXR (`.exr`), GIF (`.gif`), JPEG (`.jpg`/`.jpeg`), NetPBM (`.pam`/`.pgm`/`.ppm`), -Portable FloatMap (`.pfm`), PGX Test Format (`.pgx`), Portable Network Graphics (`.png`), -Animated PNG (`.png`/`.apng`), and JPEG XL itself (`.jxl`). - -Specifically for JPEG files, the default `cjxl` behavior is to apply lossless -recompression and the default `djxl` behavior is to reconstruct the original -JPEG file (when the extension of the output file is `.jpg`). - -### Benchmarking - -For speed benchmarks on single images in single or multi-threaded decoding -`djxl` can print decoding speed information. See `djxl --help` for details -on the decoding options and note that the output image is optional for -benchmarking purposes. - -For more comprehensive benchmarking options, see the -[benchmarking guide](doc/benchmarking.md). - -### Library API - -Besides the `libjxl` library [API documentation](https://libjxl.readthedocs.io/en/latest/), -there are [example applications](examples/) and [plugins](plugins/) that can be used as a reference or -starting point for developers who wish to integrate `libjxl` in their project. - - -## License - -This software is available under a 3-clause BSD license which can be found in -the [LICENSE](LICENSE) file, with an "Additional IP Rights Grant" as outlined in -the [PATENTS](PATENTS) file. - -Please note that the PATENTS file only mentions Google since Google is the legal -entity receiving the Contributor License Agreements (CLA) from all contributors -to the JPEG XL Project, including the initial main contributors to the JPEG XL -format: Cloudinary and Google. - -## Additional documentation - -### Codec description - -* [JPEG XL Format Overview](doc/format_overview.md) -* [Introductory paper](https://www.spiedigitallibrary.org/proceedings/Download?fullDOI=10.1117%2F12.2529237) (open-access) -* [XL Overview](doc/xl_overview.md) - a brief introduction to the source code modules -* [JPEG XL white paper](https://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf) -* [JPEG XL official website](https://jpeg.org/jpegxl) -* [JPEG XL community website](https://jpegxl.info) - -### Development process - -* [More information on testing/build options](doc/building_and_testing.md) -* [Git guide for JPEG XL](doc/developing_in_github.md) - for developers -* [Fuzzing](doc/fuzzing.md) - for developers -* [Building Web Assembly artifacts](doc/building_wasm.md) -* [Test coverage on Codecov.io](https://app.codecov.io/gh/libjxl/libjxl) - for - developers -* [libjxl documentation on readthedocs.io](https://libjxl.readthedocs.io/) -* The development of jpegli, the improved JPEG encoder and decoder, will continue at https://github.com/google/jpegli - -### Contact - -If you encounter a bug or other issue with the software, please open an Issue here. - -There is a [subreddit about JPEG XL](https://www.reddit.com/r/jpegxl/), and -informal chatting with developers and early adopters of `libjxl` can be done on the -[JPEG XL Discord server](https://discord.gg/DqkQgDRTFu). diff --git a/third_party/jpeg-xl/SECURITY.md b/third_party/jpeg-xl/SECURITY.md deleted file mode 100644 index d03012a63ad94..0000000000000 --- a/third_party/jpeg-xl/SECURITY.md +++ /dev/null @@ -1,73 +0,0 @@ -# Security and Vulnerability Policy for libjxl - -## TL;DR: - -CPE prefix: `cpe:2.3:a:libjxl_project:libjxl` - -To report a security issue, please email libjxl-security@google.com. - -Include in your email a description of the issue, the steps you took to create -the issue, affected versions, and if known, mitigations for the issue. Our -vulnerability management team will acknowledge receiving your email within 3 -working days. - -This project follows a 90 day disclosure timeline. - -For all other bugs, where there are no security implications about disclosing -the unpatched bug, open a [new issue](https://github.com/libjxl/libjxl/issues) -checking first for existing similar issues. If in doubt about the security -impact of a bug you discovered, email first. - -## Policy overview - -libjxl's Security Policy is based on the [Google Open Source program -guidelines](https://github.com/google/oss-vulnerability-guide) for coordinated -vulnerability disclosure. - -Early versions of `libjxl` had a different security policy that didn't provide -security and vulnerability disclosure support. Versions up to and including -0.3.7 are not covered and won't receive any security advisory. - -Only released versions, starting from version 0.5, are covered by this policy. -Development branches, arbitrary commits from `main` branch or even releases with -backported features externally patched on top are not covered. Only those -versions with a release tag in `libjxl`'s repository are covered, starting from -version 0.5. - -## What's a "Security bug" - -A security bug is a bug that can potentially be exploited to let an attacker -gain unauthorized access or privileges such as disclosing information or -arbitrary code execution. Not all fuzzer-found bugs and not all assert() -failures are considered security bugs in libjxl. For a detailed explanation and -examples see our [Security Vulnerabilities Playbook](doc/vuln_playbook.md). - -## What to expect - -To report a security issue, please email libjxl-security@google.com with all the -details about the bug you encountered. - - * Include a description of the issue, steps to reproduce, etc. Compiler - versions, flags, exact version used and even CPU are often relevant given our - usage of SIMD and run-time dispatch of SIMD instructions. - - * A member of our security team will reply to you within 3 business days. Note - that business days are different in different countries. - - * We will evaluate the issue and we may require more input from your side to - reproduce it. - - * If the issue fits in the description of a security bug, we will issue a - CVE, publish a fix and make a new minor or patch release with it. There is - a maximum of 90 day disclosure timeline, we ask you to not publish the - details before the 90 day deadline or the release date (whichever comes - first). - - * In the case that we publish a CVE we will credit the external researcher who - reported the issue. When reporting security issues please let us know if you - need to include specific information while doing so, like for example a - company affiliation. - -Our security team follows the [Security Vulnerabilities -Playbook](doc/vuln_playbook.md). For more details about the process and policies -please take a look at it. diff --git a/third_party/jpeg-xl/WORKSPACE b/third_party/jpeg-xl/WORKSPACE deleted file mode 100644 index d8cd9a7495762..0000000000000 --- a/third_party/jpeg-xl/WORKSPACE +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -workspace(name = "libjxl") - -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -local_repository( - name = "highway", - path = "third_party/highway", -) - -local_repository( - name = "brotli", - path = "third_party/brotli", -) - -new_local_repository( - name = "skcms", - build_file_content = """ -cc_library( - name = "skcms", - srcs = [ - "skcms.cc", - "src/skcms_internals.h", - "src/skcms_Transform.h", - "src/Transform_inl.h", - ], - hdrs = ["skcms.h"], - visibility = ["//visibility:public"], -) - """, - path = "third_party/skcms", -) diff --git a/third_party/jpeg-xl/bash_test.sh b/third_party/jpeg-xl/bash_test.sh deleted file mode 100755 index 9472c85970bf6..0000000000000 --- a/third_party/jpeg-xl/bash_test.sh +++ /dev/null @@ -1,312 +0,0 @@ -#!/bin/bash -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# Tests implemented in bash. These typically will run checks about the source -# code rather than the compiled one. - -SELF=$(realpath "$0") -MYDIR=$(dirname "${SELF}") - -set -u - -test_includes() { - local ret=0 - local f - for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do - if [ ! -e "$f" ]; then - continue - fi - # Check that the full paths to the public headers are not used, since users - # of the library will include the library as: #include "jxl/foobar.h". - if grep -i -H -n -E '#include\s*[<"]lib/include/jxl' "$f" >&2; then - echo "Don't add \"include/\" to the include path of public headers." >&2 - ret=1 - fi - - if [[ "${f#third_party/}" == "$f" ]]; then - # $f is not in third_party/ - - # Check that local files don't use the full path to third_party/ - # directory since the installed versions will not have that path. - # Add an exception for third_party/dirent.h. - if grep -v -F 'third_party/dirent.h' "$f" | \ - grep -i -H -n -E '#include\s*[<"]third_party/' >&2 && - [[ $ret -eq 0 ]]; then - cat >&2 <&2 - ret=1 - fi - done - return ${ret} -} - -test_copyright() { - local ret=0 - local f - for f in $( - git ls-files | grep -E \ - '(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui|\.yml)$'); do - if [ ! -e "$f" ]; then - continue - fi - if [[ "${f#third_party/}" == "$f" ]]; then - # $f is not in third_party/ - if ! head -n 10 "$f" | - grep -F 'Copyright (c) the JPEG XL Project Authors.' >/dev/null ; then - echo "$f: Missing Copyright blob near the top of the file." >&2 - ret=1 - fi - if ! head -n 10 "$f" | - grep -F 'Use of this source code is governed by a BSD-style' \ - >/dev/null ; then - echo "$f: Missing License blob near the top of the file." >&2 - ret=1 - fi - fi - done - return ${ret} -} - -# Check that we don't use "%zu" or "%zd" in format string for size_t. -test_printf_size_t() { - local ret=0 - if grep -n -E '%[0-9]*z[udx]' \ - $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then - echo "Don't use '%zu' or '%zd' in a format string, instead use " \ - "'%\" PRIuS \"' or '%\" PRIdS \"'." >&2 - ret=1 - fi - - if grep -n -E '[^_]gtest\.h' \ - $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then - echo "Don't include gtest directly, instead include 'testing.h'. " >&2 - ret=1 - fi - - local f - for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' | - cut -f 1 -d : | uniq); do - if [ ! -e "$f" ]; then - continue - fi - if ! grep -F printf_macros.h "$f" >/dev/null; then - echo "$f: Add lib/jxl/base/printf_macros.h for PRI.S, or use other " \ - "types for code outside lib/jxl library." >&2 - ret=1 - fi - done - - for f in $(git ls-files | grep -E "\.h$" | grep -v -E '(printf_macros\.h|testing\.h)' | - xargs grep -n 'PRI[udx]S'); do - # Having PRIuS / PRIdS in a header file means that printf_macros.h may - # be included before a system header, in particular before gtest headers. - # those may re-define PRIuS unconditionally causing a compile error. - echo "$f: Don't use PRI.S in header files. Sorry." - ret=1 - done - - return ${ret} -} - -# Check that "dec_" code doesn't depend on "enc_" headers. -test_dec_enc_deps() { - local ret=0 - local f - for f in $(git ls-files | grep -E '/dec_'); do - if [ ! -e "$f" ]; then - continue - fi - if [[ "${f#third_party/}" == "$f" ]]; then - # $f is not in third_party/ - if grep -n -H -E "#include.*/enc_" "$f" >&2; then - echo "$f: Don't include \"enc_*\" files from \"dec_*\" files." >&2 - ret=1 - fi - fi - done - return ${ret} -} - -# Check for git merge conflict markers. -test_merge_conflict() { - local ret=0 - TEXT_FILES='(\.cc|\.cpp|\.h|\.sh|\.m|\.py|\.md|\.txt|\.cmake)$' - for f in $(git ls-files | grep -E "${TEXT_FILES}"); do - if [ ! -e "$f" ]; then - continue - fi - if grep -E '^<<<<<<< ' "$f"; then - echo "$f: Found git merge conflict marker. Please resolve." >&2 - ret=1 - fi - done - return ${ret} -} - -# Check that the library and the package have the same version. This prevents -# accidentally having them out of sync. -get_version() { - local varname=$1 - local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1) - [[ -n "${line}" ]] - line="${line#set(${varname} }" - line="${line%)}" - echo "${line}" -} - -test_version() { - local major=$(get_version JPEGXL_MAJOR_VERSION) - local minor=$(get_version JPEGXL_MINOR_VERSION) - local patch=$(get_version JPEGXL_PATCH_VERSION) - # Check that the version is not empty - if [[ -z "${major}${minor}${patch}" ]]; then - echo "Couldn't parse version from CMakeLists.txt" >&2 - return 1 - fi - local pkg_version=$(head -n 1 debian/changelog) - # Get only the part between the first "jpeg-xl (" and the following ")". - pkg_version="${pkg_version#jpeg-xl (}" - pkg_version="${pkg_version%%)*}" - if [[ -z "${pkg_version}" ]]; then - echo "Couldn't parse version from debian package" >&2 - return 1 - fi - - local lib_version="${major}.${minor}.${patch}" - lib_version="${lib_version%.0}" - if [[ "${pkg_version}" != "${lib_version}"* ]]; then - echo "Debian package version (${pkg_version}) doesn't match library" \ - "version (${lib_version})." >&2 - return 1 - fi - return 0 -} - -# Check that the SHA versions in deps.sh matches the git submodules. -test_deps_version() { - while IFS= read -r line; do - if [[ "${line:0:10}" != "[submodule" ]]; then - continue - fi - line="${line#[submodule \"}" - line="${line%\"]}" - local varname=$(tr '[:lower:]' '[:upper:]' <<< "${line}") - varname="${varname/\//_}" - if ! grep -F "${varname}=" deps.sh >/dev/null; then - # Ignoring submodule not in deps.sh - continue - fi - local deps_sha=$(grep -F "${varname}=" deps.sh | cut -f 2 -d '"') - [[ -n "${deps_sha}" ]] - local git_sha=$(git ls-tree -r HEAD "${line}" | cut -f 1 | cut -f 3 -d ' ') - if [[ "${deps_sha}" != "${git_sha}" ]]; then - cat >&2 </dev/null; then - cat >&2 <&2; then - echo "Don't use \"%n\"." >&2 - ret=1 - fi - done - return ${ret} -} - -main() { - local ret=0 - cd "${MYDIR}" - - if ! git rev-parse >/dev/null 2>/dev/null; then - echo "Not a git checkout, skipping bash_test" - return 0 - fi - - IFS=$'\n' - for f in $(declare -F); do - local test_name=$(echo "$f" | cut -f 3 -d ' ') - # Runs all the local bash functions that start with "test_". - if [[ "${test_name}" == test_* ]]; then - echo "Test ${test_name}: Start" - if ${test_name}; then - echo "Test ${test_name}: PASS" - else - echo "Test ${test_name}: FAIL" - ret=1 - fi - fi - done - return ${ret} -} - -main "$@" diff --git a/third_party/jpeg-xl/ci.sh b/third_party/jpeg-xl/ci.sh deleted file mode 100755 index a2ad829cb7371..0000000000000 --- a/third_party/jpeg-xl/ci.sh +++ /dev/null @@ -1,1555 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# Continuous integration helper module. This module is meant to be called from -# workflows during the continuous integration build, as well as from the -# command line for developers. - -set -eu - -OS=`uname -s` - -SELF=$(realpath "$0") -MYDIR=$(dirname "${SELF}") - -### Environment parameters: -TEST_STACK_LIMIT="${TEST_STACK_LIMIT:-256}" -BENCHMARK_NUM_THREADS="${BENCHMARK_NUM_THREADS:-0}" -BUILD_CONFIG=${BUILD_CONFIG:-} -CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-RelWithDebInfo} -CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH:-} -CMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER:-} -CMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER:-} -CMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM:-} -SKIP_BUILD="${SKIP_BUILD:-0}" -SKIP_TEST="${SKIP_TEST:-0}" -FASTER_MSAN_BUILD="${FASTER_MSAN_BUILD:-0}" -TARGETS="${TARGETS:-all doc}" -TEST_SELECTOR="${TEST_SELECTOR:-}" -BUILD_TARGET="${BUILD_TARGET:-}" -ENABLE_WASM_SIMD="${ENABLE_WASM_SIMD:-0}" -if [[ -n "${BUILD_TARGET}" ]]; then - BUILD_DIR="${BUILD_DIR:-${MYDIR}/build-${BUILD_TARGET%%-*}}" -else - BUILD_DIR="${BUILD_DIR:-${MYDIR}/build}" -fi -# Whether we should post a message in the MR when the build fails. -POST_MESSAGE_ON_ERROR="${POST_MESSAGE_ON_ERROR:-1}" -# By default, do a lightweight debian HWY package build. -HWY_PKG_OPTIONS="${HWY_PKG_OPTIONS:---set-envvar=HWY_EXTRA_CONFIG=-DBUILD_TESTING=OFF -DHWY_ENABLE_EXAMPLES=OFF -DHWY_ENABLE_CONTRIB=OFF}" - -# Set default compilers to clang if not already set -export CC=${CC:-clang} -export CXX=${CXX:-clang++} - -# Time limit for the "fuzz" command in seconds (0 means no limit). -FUZZER_MAX_TIME="${FUZZER_MAX_TIME:-0}" - -SANITIZER="none" - - -if [[ "${BUILD_TARGET%%-*}" == "x86_64" || - "${BUILD_TARGET%%-*}" == "i686" ]]; then - # Default to building all targets, even if compiler baseline is SSE4 - HWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS:-HWY_EMU128} -else - HWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS:-} -fi - -# Convenience flag to pass both CMAKE_C_FLAGS and CMAKE_CXX_FLAGS -CMAKE_FLAGS=${CMAKE_FLAGS:-} -CMAKE_C_FLAGS="${CMAKE_C_FLAGS:-} ${CMAKE_FLAGS}" -CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS:-} ${CMAKE_FLAGS}" - -CMAKE_CROSSCOMPILING_EMULATOR=${CMAKE_CROSSCOMPILING_EMULATOR:-} -CMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS:-} -CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH:-} -CMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS:-} -CMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS:-} -CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE:-} - -if [[ "${ENABLE_WASM_SIMD}" -ne "0" ]]; then - CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -msimd128" - CMAKE_C_FLAGS="${CMAKE_C_FLAGS} -msimd128" - CMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS} -msimd128" -fi - -if [[ "${ENABLE_WASM_SIMD}" -eq "2" ]]; then - CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DHWY_WANT_WASM2" - CMAKE_C_FLAGS="${CMAKE_C_FLAGS} -DHWY_WANT_WASM2" -fi - -if [[ -z "${BUILD_CONFIG}" ]]; then - TOOLS_DIR="${BUILD_DIR}/tools" -else - TOOLS_DIR="${BUILD_DIR}/tools/${BUILD_CONFIG}" -fi - -if [[ ! -z "${HWY_BASELINE_TARGETS}" ]]; then - CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DHWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS}" -fi - -# Version inferred from the CI variables. -CI_COMMIT_SHA=${GITHUB_SHA:-} -JPEGXL_VERSION=${JPEGXL_VERSION:-} - -# Benchmark parameters -STORE_IMAGES=${STORE_IMAGES:-1} -BENCHMARK_CORPORA="${MYDIR}/third_party/corpora" - -# Local flags passed to sanitizers. -UBSAN_FLAGS=( - -fsanitize=alignment - -fsanitize=bool - -fsanitize=bounds - -fsanitize=builtin - -fsanitize=enum - -fsanitize=float-cast-overflow - -fsanitize=float-divide-by-zero - -fsanitize=integer-divide-by-zero - -fsanitize=null - -fsanitize=object-size - -fsanitize=pointer-overflow - -fsanitize=return - -fsanitize=returns-nonnull-attribute - -fsanitize=shift-base - -fsanitize=shift-exponent - -fsanitize=unreachable - -fsanitize=vla-bound - - -fno-sanitize-recover=undefined - # Brunsli uses unaligned accesses to uint32_t, so alignment is just a warning. - -fsanitize-recover=alignment -) -# -fsanitize=function doesn't work on aarch64 and arm. -if [[ "${BUILD_TARGET%%-*}" != "aarch64" && - "${BUILD_TARGET%%-*}" != "arm" ]]; then - UBSAN_FLAGS+=( - -fsanitize=function - ) -fi -if [[ "${BUILD_TARGET%%-*}" != "arm" ]]; then - UBSAN_FLAGS+=( - -fsanitize=signed-integer-overflow - ) -fi - -CLANG_TIDY_BIN_CANDIDATES=( - clang-tidy - clang-tidy-6.0 - clang-tidy-7 - clang-tidy-8 - clang-tidy-9 - clang-tidy-10 - clang-tidy-11 - clang-tidy-12 - clang-tidy-13 - clang-tidy-14 - clang-tidy-15 - clang-tidy-16 - clang-tidy-17 - clang-tidy-18 -) - -CLANG_TIDY_BIN=${CLANG_TIDY_BIN:-$(which ${CLANG_TIDY_BIN_CANDIDATES[@]} 2>/dev/null | tail -n 1)} -# Default to "cat" if "colordiff" is not installed or if stdout is not a tty. -if [[ -t 1 ]]; then - COLORDIFF_BIN=$(which colordiff cat 2>/dev/null | head -n 1) -else - COLORDIFF_BIN="cat" -fi -FIND_BIN=$(which gfind find 2>/dev/null | head -n 1) -# "false" will disable wine64 when not installed. This won't allow -# cross-compiling. -WINE_BIN=$(which wine64 false 2>/dev/null | head -n 1) - -CLANG_VERSION="${CLANG_VERSION:-}" -# Detect the clang version suffix and store it in CLANG_VERSION. For example, -# "6.0" for clang 6 or "7" for clang 7. -detect_clang_version() { - if [[ -n "${CLANG_VERSION}" ]]; then - return 0 - fi - local clang_version=$("${CC:-clang}" --version | head -n1) - clang_version=${clang_version#"Debian "} - clang_version=${clang_version#"Ubuntu "} - local llvm_tag - case "${clang_version}" in - "clang version 6."*) - CLANG_VERSION="6.0" - ;; - "clang version "*) - # Any other clang version uses just the major version number. - local suffix="${clang_version#clang version }" - CLANG_VERSION="${suffix%%.*}" - ;; - "emcc"*) - # We can't use asan or msan in the emcc case. - ;; - *) - echo "Unknown clang version: ${clang_version}" >&2 - return 1 - esac -} - -# Temporary files cleanup hooks. -CLEANUP_FILES=() -cleanup() { - if [[ ${#CLEANUP_FILES[@]} -ne 0 ]]; then - rm -fr "${CLEANUP_FILES[@]}" - fi -} - -# Executed on exit. -on_exit() { - local retcode="$1" - # Always cleanup the CLEANUP_FILES. - cleanup -} - -trap 'retcode=$?; { set +x; } 2>/dev/null; on_exit ${retcode}' INT TERM EXIT - - -# These variables are populated when calling merge_request_commits(). - -# The current hash at the top of the current branch or merge request branch (if -# running from a merge request pipeline). -MR_HEAD_SHA="" -# The common ancestor between the current commit and the tracked branch, such -# as main. This includes a list -MR_ANCESTOR_SHA="" - -# Populate MR_HEAD_SHA and MR_ANCESTOR_SHA. -merge_request_commits() { - { set +x; } 2>/dev/null - # GITHUB_SHA is the current reference being build in GitHub Actions. - if [[ -n "${GITHUB_SHA:-}" ]]; then - # GitHub normally does a checkout of a merge commit on a shallow repository - # by default. We want to get a bit more of the history to be able to diff - # changes on the Pull Request if needed. This fetches 10 more commits which - # should be enough given that PR normally should have 1 commit. - git -C "${MYDIR}" fetch -q origin "${GITHUB_SHA}" --depth 10 - if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then - MR_HEAD_SHA="$(git rev-parse "FETCH_HEAD^2" 2>/dev/null || - echo "${GITHUB_SHA}")" - else - MR_HEAD_SHA="${GITHUB_SHA}" - fi - else - MR_HEAD_SHA=$(git -C "${MYDIR}" rev-parse -q "HEAD") - fi - - if [[ -n "${GITHUB_BASE_REF:-}" ]]; then - # Pull request workflow in GitHub Actions. GitHub checkout action uses - # "origin" as the remote for the git checkout. - git -C "${MYDIR}" fetch -q origin "${GITHUB_BASE_REF}" - MR_ANCESTOR_SHA=$(git -C "${MYDIR}" rev-parse -q FETCH_HEAD) - else - # We are in a local branch, not a pull request workflow. - MR_ANCESTOR_SHA=$(git -C "${MYDIR}" rev-parse -q HEAD@{upstream} || true) - fi - - if [[ -z "${MR_ANCESTOR_SHA}" ]]; then - echo "Warning, not tracking any branch, using the last commit in HEAD.">&2 - # This prints the return value with just HEAD. - MR_ANCESTOR_SHA=$(git -C "${MYDIR}" rev-parse -q "${MR_HEAD_SHA}^") - else - # GitHub runs the pipeline on a merge commit, no need to look for the common - # ancestor in that case. - if [[ -z "${GITHUB_BASE_REF:-}" ]]; then - MR_ANCESTOR_SHA=$(git -C "${MYDIR}" merge-base \ - "${MR_ANCESTOR_SHA}" "${MR_HEAD_SHA}") - fi - fi - set -x -} - - -# Set up and export the environment variables needed by the child processes. -export_env() { - if [[ "${BUILD_TARGET}" == *mingw32 ]]; then - # Wine needs to know the paths to the mingw dlls. These should be - # separated by ';'. - WINEPATH=$("${CC:-clang}" -print-search-dirs --target="${BUILD_TARGET}" \ - | grep -F 'libraries: =' | cut -f 2- -d '=' | tr ':' ';') - # We also need our own libraries in the wine path. - local real_build_dir=$(realpath "${BUILD_DIR}") - # Some library .dll dependencies are installed in /bin: - export WINEPATH="${WINEPATH};${real_build_dir};${real_build_dir}/third_party/brotli;/usr/${BUILD_TARGET}/bin" - - local prefix="${BUILD_DIR}/wineprefix" - mkdir -p "${prefix}" - export WINEPREFIX=$(realpath "${prefix}") - fi - # Sanitizers need these variables to print and properly format the stack - # traces: - LLVM_SYMBOLIZER=$("${CC:-clang}" -print-prog-name=llvm-symbolizer || true) - if [[ -n "${LLVM_SYMBOLIZER}" ]]; then - export ASAN_SYMBOLIZER_PATH="${LLVM_SYMBOLIZER}" - export MSAN_SYMBOLIZER_PATH="${LLVM_SYMBOLIZER}" - export UBSAN_SYMBOLIZER_PATH="${LLVM_SYMBOLIZER}" - fi -} - -cmake_configure() { - export_env - - if [[ "${STACK_SIZE:-0}" == 1 ]]; then - # Dump the stack size of each function in the .stack_sizes section for - # analysis. - CMAKE_C_FLAGS+=" -fstack-size-section" - CMAKE_CXX_FLAGS+=" -fstack-size-section" - fi - - local args=( - -B"${BUILD_DIR}" -H"${MYDIR}" - -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" - -G Ninja - -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" - -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" - -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" - -DCMAKE_MODULE_LINKER_FLAGS="${CMAKE_MODULE_LINKER_FLAGS}" - -DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}" - -DJPEGXL_VERSION="${JPEGXL_VERSION}" - -DSANITIZER="${SANITIZER}" - # These are not enabled by default in cmake. - -DJPEGXL_ENABLE_VIEWERS=ON - -DJPEGXL_ENABLE_PLUGINS=ON - -DJPEGXL_ENABLE_DEVTOOLS=ON - # We always use libfuzzer in the ci.sh wrapper. - -DJPEGXL_FUZZER_LINK_FLAGS="-fsanitize=fuzzer" - ) - if [[ "${BUILD_TARGET}" != *mingw32 ]]; then - args+=( - -DJPEGXL_WARNINGS_AS_ERRORS=ON - ) - fi - if [[ -n "${BUILD_TARGET}" ]]; then - local system_name="Linux" - if [[ "${BUILD_TARGET}" == *mingw32 ]]; then - # When cross-compiling with mingw the target must be set to Windows and - # run programs with wine. - system_name="Windows" - args+=( - -DCMAKE_CROSSCOMPILING_EMULATOR="${WINE_BIN}" - # Normally CMake automatically defines MINGW=1 when building with the - # mingw compiler (x86_64-w64-mingw32-gcc) but we are normally compiling - # with clang. - -DMINGW=1 - ) - fi - # EMSCRIPTEN toolchain sets the right values itself - if [[ "${BUILD_TARGET}" != wasm* ]]; then - # If set, BUILD_TARGET must be the target triplet such as - # x86_64-unknown-linux-gnu. - args+=( - -DCMAKE_C_COMPILER_TARGET="${BUILD_TARGET}" - -DCMAKE_CXX_COMPILER_TARGET="${BUILD_TARGET}" - # Only the first element of the target triplet. - -DCMAKE_SYSTEM_PROCESSOR="${BUILD_TARGET%%-*}" - -DCMAKE_SYSTEM_NAME="${system_name}" - -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}" - ) - else - args+=( - # sjpeg confuses WASM SIMD with SSE. - -DSJPEG_ENABLE_SIMD=OFF - # Building shared libs is not very useful for WASM. - -DBUILD_SHARED_LIBS=OFF - ) - fi - args+=( - # These are needed to make googletest work when cross-compiling. - -DCMAKE_CROSSCOMPILING=1 - -DHAVE_STD_REGEX=0 - -DHAVE_POSIX_REGEX=0 - -DHAVE_GNU_POSIX_REGEX=0 - -DHAVE_STEADY_CLOCK=0 - -DHAVE_THREAD_SAFETY_ATTRIBUTES=0 - ) - if [[ -z "${CMAKE_FIND_ROOT_PATH}" ]]; then - # find_package() will look in this prefix for libraries. - CMAKE_FIND_ROOT_PATH="/usr/${BUILD_TARGET}" - fi - if [[ -z "${CMAKE_PREFIX_PATH}" ]]; then - CMAKE_PREFIX_PATH="/usr/${BUILD_TARGET}" - fi - # Use pkg-config for the target. If there's no pkg-config available for the - # target we can set the PKG_CONFIG_PATH to the appropriate path in most - # linux distributions. - local pkg_config=$(which "${BUILD_TARGET}-pkg-config" || true) - if [[ -z "${pkg_config}" ]]; then - pkg_config=$(which pkg-config) - export PKG_CONFIG_LIBDIR="/usr/${BUILD_TARGET}/lib/pkgconfig" - fi - if [[ -n "${pkg_config}" ]]; then - args+=(-DPKG_CONFIG_EXECUTABLE="${pkg_config}") - fi - fi - if [[ -n "${CMAKE_CROSSCOMPILING_EMULATOR}" ]]; then - args+=( - -DCMAKE_CROSSCOMPILING_EMULATOR="${CMAKE_CROSSCOMPILING_EMULATOR}" - ) - fi - if [[ -n "${CMAKE_FIND_ROOT_PATH}" ]]; then - args+=( - -DCMAKE_FIND_ROOT_PATH="${CMAKE_FIND_ROOT_PATH}" - ) - fi - if [[ -n "${CMAKE_PREFIX_PATH}" ]]; then - args+=( - -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" - ) - fi - if [[ -n "${CMAKE_C_COMPILER_LAUNCHER}" ]]; then - args+=( - -DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_C_COMPILER_LAUNCHER}" - ) - fi - if [[ -n "${CMAKE_CXX_COMPILER_LAUNCHER}" ]]; then - args+=( - -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CXX_COMPILER_LAUNCHER}" - ) - fi - if [[ -n "${CMAKE_MAKE_PROGRAM}" ]]; then - args+=( - -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}" - ) - fi - if [[ "${BUILD_TARGET}" == wasm* ]]; then - emcmake cmake "${args[@]}" "$@" - else - cmake "${args[@]}" "$@" - fi -} - -cmake_build_and_test() { - if [[ "${SKIP_BUILD}" -eq "1" ]]; then - return 0 - fi - # gtest_discover_tests() runs the test binaries to discover the list of tests - # at build time, which fails under qemu. - ASAN_OPTIONS=detect_leaks=0 cmake --build "${BUILD_DIR}" -- $TARGETS - # Pack test binaries if requested. - if [[ "${PACK_TEST:-}" == "1" ]]; then - (cd "${BUILD_DIR}" - ${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*' - # gtest / gtest_main shared libs - ${FIND_BIN} lib/ -name 'libg*.so*' - ${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*' - ) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \ - --use-compress-program="xz --threads=$(nproc --all || echo 1) -6" - du -h "${BUILD_DIR}/tests.tar.xz" - # Pack coverage data if also available. - touch "${BUILD_DIR}/gcno.sentinel" - (cd "${BUILD_DIR}"; echo gcno.sentinel; ${FIND_BIN} -name '*gcno') | \ - tar -C "${BUILD_DIR}" -cvf "${BUILD_DIR}/gcno.tar.xz" -T - \ - --use-compress-program="xz --threads=$(nproc --all || echo 1) -6" - fi - - if [[ "${SKIP_TEST}" -ne "1" ]]; then - (cd "${BUILD_DIR}" - export UBSAN_OPTIONS=print_stacktrace=1 - [[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}" - ctest -j $(nproc --all || echo 1) ${TEST_SELECTOR} --output-on-failure) - fi -} - -# Configure the build to strip unused functions. This considerably reduces the -# output size, specially for tests which only use a small part of the whole -# library. -strip_dead_code() { - # Emscripten does tree shaking without any extra flags. - if [[ "${BUILD_TARGET}" == wasm* ]]; then - return 0 - fi - # -ffunction-sections, -fdata-sections and -Wl,--gc-sections effectively - # discard all unreachable code, reducing the code size. For this to work, we - # need to also pass --no-export-dynamic to prevent it from exporting all the - # internal symbols (like functions) making them all reachable and thus not a - # candidate for removal. - CMAKE_CXX_FLAGS+=" -ffunction-sections -fdata-sections" - CMAKE_C_FLAGS+=" -ffunction-sections -fdata-sections" - if [[ "${OS}" == "Darwin" ]]; then - CMAKE_EXE_LINKER_FLAGS+=" -dead_strip" - CMAKE_SHARED_LINKER_FLAGS+=" -dead_strip" - else - CMAKE_EXE_LINKER_FLAGS+=" -Wl,--gc-sections -Wl,--no-export-dynamic" - CMAKE_SHARED_LINKER_FLAGS+=" -Wl,--gc-sections -Wl,--no-export-dynamic" - fi -} - -### Externally visible commands - -cmd_debug() { - CMAKE_BUILD_TYPE="DebugOpt" - cmake_configure "$@" - cmake_build_and_test -} - -cmd_release() { - CMAKE_BUILD_TYPE="Release" - strip_dead_code - cmake_configure "$@" - cmake_build_and_test -} - -cmd_opt() { - CMAKE_BUILD_TYPE="RelWithDebInfo" - CMAKE_CXX_FLAGS+=" -DJXL_IS_DEBUG_BUILD" - cmake_configure "$@" - cmake_build_and_test -} - -cmd_coverage() { - # -O0 prohibits stack space reuse -> causes stack-overflow on dozens of tests. - TEST_STACK_LIMIT="none" - - cmd_release -DJPEGXL_ENABLE_COVERAGE=ON "$@" - - if [[ "${SKIP_TEST}" -ne "1" ]]; then - # If we didn't run the test we also don't print a coverage report. - cmd_coverage_report - fi -} - -cmd_coverage_report() { - LLVM_COV=$("${CC:-clang}" -print-prog-name=llvm-cov) - local real_build_dir=$(realpath "${BUILD_DIR}") - local gcovr_args=( - -r "${real_build_dir}" - --gcov-executable "${LLVM_COV} gcov" - # Only print coverage information for the libjxl directories. The rest - # is not part of the code under test. - --filter '.*jxl/.*' - --exclude '.*_gbench.cc' - --exclude '.*_test.cc' - --exclude '.*_testonly..*' - --exclude '.*_debug.*' - --exclude '.*test_utils..*' - --object-directory "${real_build_dir}" - ) - - ( - cd "${real_build_dir}" - gcovr "${gcovr_args[@]}" --html --html-details \ - --output="${real_build_dir}/coverage.html" - gcovr "${gcovr_args[@]}" --print-summary | - tee "${real_build_dir}/coverage.txt" - gcovr "${gcovr_args[@]}" --xml --output="${real_build_dir}/coverage.xml" - ) -} - -cmd_test() { - export_env - # Unpack tests if needed. - if [[ -e "${BUILD_DIR}/tests.tar.xz" && ! -d "${BUILD_DIR}/tests" ]]; then - tar -C "${BUILD_DIR}" -Jxvf "${BUILD_DIR}/tests.tar.xz" - fi - if [[ -e "${BUILD_DIR}/gcno.tar.xz" && ! -d "${BUILD_DIR}/gcno.sentinel" ]]; then - tar -C "${BUILD_DIR}" -Jxvf "${BUILD_DIR}/gcno.tar.xz" - fi - (cd "${BUILD_DIR}" - export UBSAN_OPTIONS=print_stacktrace=1 - [[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}" - ctest -j $(nproc --all || echo 1) ${TEST_SELECTOR} --output-on-failure "$@") -} - -cmd_gbench() { - export_env - (cd "${BUILD_DIR}" - export UBSAN_OPTIONS=print_stacktrace=1 - lib/jxl_gbench \ - --benchmark_counters_tabular=true \ - --benchmark_out_format=json \ - --benchmark_out=gbench.json "$@" - ) -} - -cmd_asanfuzz() { - CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1" - CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1" - cmd_asan -DJPEGXL_ENABLE_FUZZERS=ON "$@" -} - -cmd_msanfuzz() { - # Install msan if needed before changing the flags. - detect_clang_version - local msan_prefix="${HOME}/.msan/${CLANG_VERSION}" - # TODO(eustas): why libc++abi.a is bad? - if [[ ! -d "${msan_prefix}" || -e "${msan_prefix}/lib/libc++abi.a" ]]; then - # Install msan libraries for this version if needed or if an older version - # with libc++abi was installed. - cmd_msan_install - fi - - CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1" - CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1" - cmd_msan -DJPEGXL_ENABLE_FUZZERS=ON "$@" -} - -cmd_asan() { - SANITIZER="asan" - CMAKE_C_FLAGS+=" -g -DADDRESS_SANITIZER \ - -fsanitize=address ${UBSAN_FLAGS[@]}" - CMAKE_CXX_FLAGS+=" -g -DADDRESS_SANITIZER \ - -fsanitize=address ${UBSAN_FLAGS[@]}" - strip_dead_code - cmake_configure "$@" -DJPEGXL_ENABLE_TCMALLOC=OFF - cmake_build_and_test -} - -cmd_tsan() { - SANITIZER="tsan" - local tsan_args=( - -g - -DTHREAD_SANITIZER - -fsanitize=thread - ) - CMAKE_C_FLAGS+=" ${tsan_args[@]}" - CMAKE_CXX_FLAGS+=" ${tsan_args[@]}" - - cmake_configure "$@" -DJPEGXL_ENABLE_TCMALLOC=OFF - cmake_build_and_test -} - -cmd_msan() { - SANITIZER="msan" - detect_clang_version - local msan_prefix="${HOME}/.msan/${CLANG_VERSION}" - if [[ ! -d "${msan_prefix}" || -e "${msan_prefix}/lib/libc++abi.a" ]]; then - # Install msan libraries for this version if needed or if an older version - # with libc++abi was installed. - cmd_msan_install - fi - - local msan_c_flags=( - -fsanitize=memory - -fno-omit-frame-pointer - - -g - -DMEMORY_SANITIZER - - # Force gtest to not use the cxxbai. - -DGTEST_HAS_CXXABI_H_=0 - ) - if [[ "${FASTER_MSAN_BUILD}" -ne "1" ]]; then - msan_c_flags=( - "${msan_c_flags[@]}" - -fsanitize-memory-track-origins - ) - fi - - local msan_cxx_flags=( - "${msan_c_flags[@]}" - - # Some C++ sources don't use the std at all, so the -stdlib=libc++ is unused - # in those cases. Ignore the warning. - -Wno-unused-command-line-argument - -stdlib=libc++ - - # We include the libc++ from the msan directory instead, so we don't want - # the std includes. - -nostdinc++ - -cxx-isystem"${msan_prefix}/include/c++/v1" - ) - - local msan_linker_flags=( - -L"${msan_prefix}"/lib - -Wl,-rpath -Wl,"${msan_prefix}"/lib/ - ) - - CMAKE_C_FLAGS+=" ${msan_c_flags[@]}" - CMAKE_CXX_FLAGS+=" ${msan_cxx_flags[@]}" - CMAKE_EXE_LINKER_FLAGS+=" ${msan_linker_flags[@]}" - CMAKE_MODULE_LINKER_FLAGS+=" ${msan_linker_flags[@]}" - CMAKE_SHARED_LINKER_FLAGS+=" ${msan_linker_flags[@]}" - strip_dead_code - - # MSAN share of stack size is non-negligible. - TEST_STACK_LIMIT="none" - - # TODO(eustas): investigate why fuzzers do not link when MSAN libc++ is used - cmake_configure "$@" \ - -DCMAKE_CROSSCOMPILING=1 -DRUN_HAVE_STD_REGEX=0 -DRUN_HAVE_POSIX_REGEX=0 \ - -DJPEGXL_ENABLE_TCMALLOC=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF \ - -DCMAKE_REQUIRED_LINK_OPTIONS="${msan_linker_flags[@]}" \ - -DJPEGXL_ENABLE_FUZZERS=OFF - cmake_build_and_test -} - -# Install libc++ libraries compiled with msan in the msan_prefix for the current -# compiler version. -cmd_msan_install() { - local tmpdir=$(mktemp -d) - CLEANUP_FILES+=("${tmpdir}") - local msan_root="${HOME}/.msan" - mkdir -p "${msan_root}" - # Detect the llvm to install: - export CC="${CC:-clang}" - export CXX="${CXX:-clang++}" - detect_clang_version - # Allow overriding the LLVM checkout. - local llvm_root="${LLVM_ROOT:-}" - if [ -z "${llvm_root}" ]; then - declare -A llvm_tag_by_version=( - ["6.0"]="6.0.1" - ["7"]="7.1.0" - ["8"]="8.0.1" - ["9"]="9.0.2" - ["10"]="10.0.1" - ["11"]="11.1.0" - ["12"]="12.0.1" - ["13"]="13.0.1" - ["14"]="14.0.6" - ["15"]="15.0.7" - ["16"]="16.0.6" - ["17"]="17.0.6" - ["18"]="18.1.6" - ) - local llvm_tag="${CLANG_VERSION}.0.0" - if [[ -n "${llvm_tag_by_version["${CLANG_VERSION}"]}" ]]; then - llvm_tag=${llvm_tag_by_version["${CLANG_VERSION}"]} - fi - llvm_tag="llvmorg-${llvm_tag}" - local llvm_targz="${msan_root}/${llvm_tag}.tar.gz" - if [ ! -f "${llvm_targz}" ]; then - curl -L --show-error -o "${llvm_targz}" \ - "https://github.com/llvm/llvm-project/archive/${llvm_tag}.tar.gz" - fi - tar -C "${tmpdir}" -zxf "${llvm_targz}" - llvm_root="${tmpdir}/llvm-project-${llvm_tag}" - fi - - local msan_prefix="${msan_root}/${CLANG_VERSION}" - rm -rf "${msan_prefix}" - - local TARGET_OPTS="" - if [[ -n "${BUILD_TARGET}" ]]; then - TARGET_OPTS=" \ - -DCMAKE_C_COMPILER_TARGET=\"${BUILD_TARGET}\" \ - -DCMAKE_CXX_COMPILER_TARGET=\"${BUILD_TARGET}\" \ - -DCMAKE_SYSTEM_PROCESSOR=\"${BUILD_TARGET%%-*}\" \ - " - fi - - local build_dir="${tmpdir}/build-llvm" - mkdir -p "${build_dir}" - cd ${llvm_root} - cmake -B"${build_dir}" \ - -G Ninja \ - -S runtimes \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_USE_SANITIZER=Memory \ - -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind;compiler-rt" \ - -DLIBCXXABI_ENABLE_SHARED=ON \ - -DLIBCXXABI_ENABLE_STATIC=OFF \ - -DLIBCXX_ENABLE_SHARED=ON \ - -DLIBCXX_ENABLE_STATIC=OFF \ - -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" \ - -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" \ - -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" \ - -DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}" \ - -DCMAKE_INSTALL_PREFIX="${msan_prefix}" \ - -DLLVM_PATH="${llvm_root}/llvm" \ - -DLLVM_CONFIG_PATH="$(which llvm-config-${CLANG_VERSION} llvm-config | head -n1)" \ - ${TARGET_OPTS} - cmake --build "${build_dir}" - ninja -C "${build_dir}" install -} - -# Internal build step shared between all cmd_ossfuzz_* commands. -_cmd_ossfuzz() { - local sanitizer="$1" - shift - mkdir -p "${BUILD_DIR}" - local real_build_dir=$(realpath "${BUILD_DIR}") - - # oss-fuzz defines three directories: - # * /work, with the working directory to do re-builds - # * /src, with the source code to build - # * /out, with the output directory where to copy over the built files. - # We use $BUILD_DIR as the /work and the script directory as the /src. The - # /out directory is ignored as developers are used to look for the fuzzers in - # $BUILD_DIR/tools/ directly. - - if [[ "${sanitizer}" = "memory" && ! -d "${BUILD_DIR}/msan" ]]; then - sudo docker run --rm -i \ - --user $(id -u):$(id -g) \ - -v "${real_build_dir}":/work \ - gcr.io/oss-fuzz-base/msan-libs-builder \ - bash -c "cp -r /msan /work" - fi - - # Args passed to ninja. These will be evaluated as a string separated by - # spaces. - local jpegxl_extra_args="$@" - - sudo docker run --rm -i \ - -e JPEGXL_UID=$(id -u) \ - -e JPEGXL_GID=$(id -g) \ - -e FUZZING_ENGINE="${FUZZING_ENGINE:-libfuzzer}" \ - -e SANITIZER="${sanitizer}" \ - -e ARCHITECTURE=x86_64 \ - -e FUZZING_LANGUAGE=c++ \ - -e MSAN_LIBS_PATH="/work/msan" \ - -e JPEGXL_EXTRA_ARGS="${jpegxl_extra_args}" \ - -v "${MYDIR}":/src/libjxl \ - -v "${MYDIR}/tools/scripts/ossfuzz-build.sh":/src/build.sh \ - -v "${real_build_dir}":/work \ - gcr.io/oss-fuzz/libjxl -} - -cmd_ossfuzz_asan() { - _cmd_ossfuzz address "$@" -} -cmd_ossfuzz_msan() { - _cmd_ossfuzz memory "$@" -} -cmd_ossfuzz_ubsan() { - _cmd_ossfuzz undefined "$@" -} - -cmd_ossfuzz_ninja() { - [[ -e "${BUILD_DIR}/build.ninja" ]] - local real_build_dir=$(realpath "${BUILD_DIR}") - - if [[ -e "${BUILD_DIR}/msan" ]]; then - echo "ossfuzz_ninja doesn't work with msan builds. Use ossfuzz_msan." >&2 - exit 1 - fi - - sudo docker run --rm -i \ - --user $(id -u):$(id -g) \ - -v "${MYDIR}":/src/libjxl \ - -v "${real_build_dir}":/work \ - gcr.io/oss-fuzz/libjxl \ - ninja -C /work "$@" -} - -cmd_fast_benchmark() { - local small_corpus_tar="${BENCHMARK_CORPORA}/jyrki-full.tar" - local small_corpus_url="https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar" - mkdir -p "${BENCHMARK_CORPORA}" - if [ -f "${small_corpus_tar}" ]; then - curl --show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" "${small_corpus_url}" - else - curl --show-error -o "${small_corpus_tar}" "${small_corpus_url}" - fi - - local tmpdir=$(mktemp -d) - CLEANUP_FILES+=("${tmpdir}") - tar -xf "${small_corpus_tar}" -C "${tmpdir}" - - run_benchmark "${tmpdir}" 1048576 -} - -cmd_benchmark() { - local nikon_corpus_tar="${BENCHMARK_CORPORA}/nikon-subset.tar" - mkdir -p "${BENCHMARK_CORPORA}" - curl --show-error -o "${nikon_corpus_tar}" -z "${nikon_corpus_tar}" \ - "https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/nikon-subset.tar" - - local tmpdir=$(mktemp -d) - CLEANUP_FILES+=("${tmpdir}") - tar -xvf "${nikon_corpus_tar}" -C "${tmpdir}" - - local sem_id="jpegxl_benchmark-$$" - local nprocs=$(nproc --all || echo 1) - images=() - local filename - while IFS= read -r filename; do - # This removes the './' - filename="${filename:2}" - local mode - if [[ "${filename:0:4}" == "srgb" ]]; then - mode="RGB_D65_SRG_Rel_SRG" - elif [[ "${filename:0:5}" == "adobe" ]]; then - mode="RGB_D65_Ado_Rel_Ado" - else - echo "Unknown image colorspace: ${filename}" >&2 - exit 1 - fi - png_filename="${filename%.ppm}.png" - png_filename=$(echo "${png_filename}" | tr '/' '_') - sem --bg --id "${sem_id}" -j"${nprocs}" -- \ - "${TOOLS_DIR}/decode_and_encode" \ - "${tmpdir}/${filename}" "${mode}" "${tmpdir}/${png_filename}" - images+=( "${png_filename}" ) - done < <(cd "${tmpdir}"; ${FIND_BIN} . -name '*.ppm' -type f) - sem --id "${sem_id}" --wait - - # We need about 10 GiB per thread on these images. - run_benchmark "${tmpdir}" 10485760 -} - -get_mem_available() { - if [[ "${OS}" == "Darwin" ]]; then - echo $(vm_stat | grep -F 'Pages free:' | awk '{print $3 * 4}') - elif [[ "${OS}" == MINGW* ]]; then - echo $(vmstat | tail -n 1 | awk '{print $4 * 4}') - else - echo $(grep -F MemAvailable: /proc/meminfo | awk '{print $2}') - fi -} - -run_benchmark() { - local src_img_dir="$1" - local mem_per_thread="${2:-10485760}" - - local output_dir="${BUILD_DIR}/benchmark_results" - mkdir -p "${output_dir}" - - if [[ "${OS}" == MINGW* ]]; then - src_img_dir=`cygpath -w "${src_img_dir}"` - fi - - local num_threads=1 - if [[ ${BENCHMARK_NUM_THREADS} -gt 0 ]]; then - num_threads=${BENCHMARK_NUM_THREADS} - else - # The memory available at the beginning of the benchmark run in kB. The number - # of threads depends on the available memory, and the passed memory per - # thread. We also add a 2 GiB of constant memory. - local mem_available="$(get_mem_available)" - # Check that we actually have a MemAvailable value. - [[ -n "${mem_available}" ]] - num_threads=$(( (${mem_available} - 1048576) / ${mem_per_thread} )) - if [[ ${num_threads} -le 0 ]]; then - num_threads=1 - fi - fi - - local benchmark_args=( - --input "${src_img_dir}/*.png" - --codec=jpeg:yuv420:q85,webp:q80,jxl:d1:6,jxl:d1:6:downsampling=8,jxl:d5:6,jxl:d5:6:downsampling=8,jxl:m:d0:2,jxl:m:d0:3,jxl:m:d2:2 - --output_dir "${output_dir}" - --show_progress - --num_threads="${num_threads}" - --decode_reps=11 - --encode_reps=11 - ) - if [[ "${STORE_IMAGES}" == "1" ]]; then - benchmark_args+=(--save_decompressed --save_compressed) - fi - ( - [[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}" - "${TOOLS_DIR}/benchmark_xl" "${benchmark_args[@]}" | \ - tee "${output_dir}/results.txt" - - # Check error code for benchmark_xl command. This will exit if not. - return ${PIPESTATUS[0]} - ) -} - -# Helper function to wait for the CPU temperature to cool down on ARM. -wait_for_temp() { - { set +x; } 2>/dev/null - local temp_limit=${1:-38000} - if [[ -z "${THERMAL_FILE:-}" ]]; then - echo "Must define the THERMAL_FILE with the thermal_zoneX/temp file" \ - "to read the temperature from. This is normally set in the runner." >&2 - exit 1 - fi - local org_temp=$(cat "${THERMAL_FILE}") - if [[ "${org_temp}" -ge "${temp_limit}" ]]; then - echo -n "Waiting for temp to get down from ${org_temp}... " - fi - local temp="${org_temp}" - local secs=0 - while [[ "${temp}" -ge "${temp_limit}" ]]; do - sleep 1 - temp=$(cat "${THERMAL_FILE}") - echo -n "${temp} " - secs=$((secs + 1)) - if [[ ${secs} -ge 5 ]]; then - break - fi - done - if [[ "${org_temp}" -ge "${temp_limit}" ]]; then - echo "Done, temp=${temp}" - fi - set -x -} - -# Helper function to set the cpuset restriction of the current process. -cmd_cpuset() { - [[ "${SKIP_CPUSET:-}" != "1" ]] || return 0 - local newset="$1" - local mycpuset=$(cat /proc/self/cpuset) - mycpuset="/dev/cpuset${mycpuset}" - # Check that the directory exists: - [[ -d "${mycpuset}" ]] - if [[ -e "${mycpuset}/cpuset.cpus" ]]; then - echo "${newset}" >"${mycpuset}/cpuset.cpus" - else - echo "${newset}" >"${mycpuset}/cpus" - fi -} - -# Return the encoding/decoding speed from the Stats output. -_speed_from_output() { - local speed="$1" - local unit="${2:-MP/s}" - if [[ "${speed}" == *"${unit}"* ]]; then - speed="${speed%% ${unit}*}" - speed="${speed##* }" - echo "${speed}" - fi -} - - -# Run benchmarks on ARM for the big and little CPUs. -cmd_arm_benchmark() { - # Flags used for cjxl encoder with .png inputs - local jxl_png_benchmarks=( - # Lossy options: - "--epf=0 --distance=1.0 --speed=cheetah" - "--epf=2 --distance=1.0 --speed=cheetah" - "--epf=0 --distance=8.0 --speed=cheetah" - "--epf=1 --distance=8.0 --speed=cheetah" - "--epf=2 --distance=8.0 --speed=cheetah" - "--epf=3 --distance=8.0 --speed=cheetah" - "--modular -Q 90" - "--modular -Q 50" - # Lossless options: - "--modular" - "--modular -E 0 -I 0" - "--modular -P 5" - "--modular --responsive=1" - # Near-lossless options: - "--epf=0 --distance=0.3 --speed=fast" - "--modular -Q 97" - ) - - # Flags used for cjxl encoder with .jpg inputs. These should do lossless - # JPEG recompression (of pixels or full jpeg). - local jxl_jpeg_benchmarks=( - "--num_reps=3" - ) - - local images=( - "testdata/jxl/flower/flower.png" - ) - - local jpg_images=( - "testdata/jxl/flower/flower.png.im_q85_420.jpg" - ) - - if [[ "${SKIP_CPUSET:-}" == "1" ]]; then - # Use a single cpu config in this case. - local cpu_confs=("?") - else - # Otherwise the CPU config comes from the environment: - local cpu_confs=( - "${RUNNER_CPU_LITTLE}" - "${RUNNER_CPU_BIG}" - # The CPU description is something like 3-7, so these configurations only - # take the first CPU of the group. - "${RUNNER_CPU_LITTLE%%-*}" - "${RUNNER_CPU_BIG%%-*}" - ) - # Check that RUNNER_CPU_ALL is defined. In the SKIP_CPUSET=1 case this will - # be ignored but still evaluated when calling cmd_cpuset. - [[ -n "${RUNNER_CPU_ALL}" ]] - fi - - local jpg_dirname="third_party/corpora/jpeg" - mkdir -p "${jpg_dirname}" - local jpg_qualities=( 50 80 95 ) - for src_img in "${images[@]}"; do - for q in "${jpg_qualities[@]}"; do - local jpeg_name="${jpg_dirname}/"$(basename "${src_img}" .png)"-q${q}.jpg" - convert -sampling-factor 1x1 -quality "${q}" \ - "${src_img}" "${jpeg_name}" - jpg_images+=("${jpeg_name}") - done - done - - local output_dir="${BUILD_DIR}/benchmark_results" - mkdir -p "${output_dir}" - local runs_file="${output_dir}/runs.txt" - - if [[ ! -e "${runs_file}" ]]; then - echo -e "binary\tflags\tsrc_img\tsrc size\tsrc pixels\tcpuset\tenc size (B)\tenc speed (MP/s)\tdec speed (MP/s)\tJPG dec speed (MP/s)\tJPG dec speed (MB/s)" | - tee -a "${runs_file}" - fi - - mkdir -p "${BUILD_DIR}/arm_benchmark" - local flags - local src_img - for src_img in "${jpg_images[@]}" "${images[@]}"; do - local src_img_hash=$(sha1sum "${src_img}" | cut -f 1 -d ' ') - local enc_binaries=("${TOOLS_DIR}/cjxl") - local src_ext="${src_img##*.}" - for enc_binary in "${enc_binaries[@]}"; do - local enc_binary_base=$(basename "${enc_binary}") - - # Select the list of flags to use for the current encoder/image pair. - local img_benchmarks - if [[ "${src_ext}" == "jpg" ]]; then - img_benchmarks=("${jxl_jpeg_benchmarks[@]}") - else - img_benchmarks=("${jxl_png_benchmarks[@]}") - fi - - for flags in "${img_benchmarks[@]}"; do - # Encoding step. - local enc_file_hash="${enc_binary_base} || $flags || ${src_img} || ${src_img_hash}" - enc_file_hash=$(echo "${enc_file_hash}" | sha1sum | cut -f 1 -d ' ') - local enc_file="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jxl" - - for cpu_conf in "${cpu_confs[@]}"; do - cmd_cpuset "${cpu_conf}" - # nproc returns the number of active CPUs, which is given by the cpuset - # mask. - local num_threads="$(nproc)" - - echo "Encoding with: ${enc_binary_base} img=${src_img} cpus=${cpu_conf} enc_flags=${flags}" - local enc_output - if [[ "${flags}" == *"modular"* ]]; then - # We don't benchmark encoding speed in this case. - if [[ ! -f "${enc_file}" ]]; then - cmd_cpuset "${RUNNER_CPU_ALL:-}" - "${enc_binary}" ${flags} "${src_img}" "${enc_file}.tmp" - mv "${enc_file}.tmp" "${enc_file}" - cmd_cpuset "${cpu_conf}" - fi - enc_output=" ?? MP/s" - else - wait_for_temp - enc_output=$("${enc_binary}" ${flags} "${src_img}" "${enc_file}.tmp" \ - 2>&1 | tee /dev/stderr | grep -F "MP/s [") - mv "${enc_file}.tmp" "${enc_file}" - fi - local enc_speed=$(_speed_from_output "${enc_output}") - local enc_size=$(stat -c "%s" "${enc_file}") - - echo "Decoding with: img=${src_img} cpus=${cpu_conf} enc_flags=${flags}" - - local dec_output - wait_for_temp - dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \ - --num_reps=5 --num_threads="${num_threads}" 2>&1 | tee /dev/stderr | - grep -E "M[BP]/s \[") - local img_size=$(echo "${dec_output}" | cut -f 1 -d ',') - local img_size_x=$(echo "${img_size}" | cut -f 1 -d ' ') - local img_size_y=$(echo "${img_size}" | cut -f 3 -d ' ') - local img_size_px=$(( ${img_size_x} * ${img_size_y} )) - local dec_speed=$(_speed_from_output "${dec_output}") - - # For JPEG lossless recompression modes (where the original is a JPEG) - # decode to JPG as well. - local jpeg_dec_mps_speed="" - local jpeg_dec_mbs_speed="" - if [[ "${src_ext}" == "jpg" ]]; then - wait_for_temp - local dec_file="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jpg" - dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \ - "${dec_file}" --num_reps=5 --num_threads="${num_threads}" 2>&1 | \ - tee /dev/stderr | grep -E "M[BP]/s \[") - local jpeg_dec_mps_speed=$(_speed_from_output "${dec_output}") - local jpeg_dec_mbs_speed=$(_speed_from_output "${dec_output}" MB/s) - if ! cmp --quiet "${src_img}" "${dec_file}"; then - # Add a start at the end to signal that the files are different. - jpeg_dec_mbs_speed+="*" - fi - fi - - # Record entry in a tab-separated file. - local src_img_base=$(basename "${src_img}") - echo -e "${enc_binary_base}\t${flags}\t${src_img_base}\t${img_size}\t${img_size_px}\t${cpu_conf}\t${enc_size}\t${enc_speed}\t${dec_speed}\t${jpeg_dec_mps_speed}\t${jpeg_dec_mbs_speed}" | - tee -a "${runs_file}" - done - done - done - done - cmd_cpuset "${RUNNER_CPU_ALL:-}" - cat "${runs_file}" - -} - -# Generate a corpus and run the fuzzer on that corpus. -cmd_fuzz() { - local corpus_dir=$(realpath "${BUILD_DIR}/fuzzer_corpus") - local fuzzer_crash_dir=$(realpath "${BUILD_DIR}/fuzzer_crash") - mkdir -p "${corpus_dir}" "${fuzzer_crash_dir}" - # Generate step. - "${TOOLS_DIR}/fuzzer_corpus" "${corpus_dir}" - # Run step: - local nprocs=$(nproc --all || echo 1) - ( - cd "${TOOLS_DIR}" - djxl_fuzzer "${fuzzer_crash_dir}" "${corpus_dir}" \ - -max_total_time="${FUZZER_MAX_TIME}" -jobs=${nprocs} \ - -artifact_prefix="${fuzzer_crash_dir}/" - ) -} - -# Runs the linters (clang-format, build_cleaner, buildirier) on the pending CLs. -cmd_lint() { - merge_request_commits - { set +x; } 2>/dev/null - local versions=(${1:-16 15 14 13 12 11 10 9 8 7 6.0}) - local clang_format_bins=("${versions[@]/#/clang-format-}" clang-format) - local tmpdir=$(mktemp -d) - CLEANUP_FILES+=("${tmpdir}") - - local ret=0 - local build_patch="${tmpdir}/build_cleaner.patch" - if ! "${MYDIR}/tools/scripts/build_cleaner.py" >"${build_patch}"; then - ret=1 - echo "build_cleaner.py findings:" >&2 - "${COLORDIFF_BIN}" <"${build_patch}" - echo "Run \`tools/scripts/build_cleaner.py --update\` to apply them" >&2 - fi - - # It is ok, if buildifier is not installed. - if which buildifier >/dev/null; then - local buildifier_patch="${tmpdir}/buildifier.patch" - local bazel_files=`git -C "${MYDIR}" ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"` - set -x - buildifier -d ${bazel_files} >"${buildifier_patch}"|| true - { set +x; } 2>/dev/null - if [ -s "${buildifier_patch}" ]; then - ret=1 - echo 'buildifier have found some problems in Bazel build files:' >&2 - "${COLORDIFF_BIN}" <"${buildifier_patch}" - echo 'To fix them run (from the base directory):' >&2 - echo ' buildifier `git ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"`' >&2 - fi - fi - - # It is ok, if spell-checker is not installed. - if which typos >/dev/null; then - local src_ext="bazel|bzl|c|cc|cmake|gni|h|html|in|java|js|m|md|nix|py|rst|sh|ts|txt|yaml|yml" - local sources=`git -C "${MYDIR}" ls-files | grep -E "\.(${src_ext})$"` - typos -c "${MYDIR}/tools/scripts/typos.toml" ${sources} - else - echo "Consider installing https://github.com/crate-ci/typos for spell-checking" - fi - - local installed=() - local clang_patch - local clang_format - for clang_format in "${clang_format_bins[@]}"; do - if ! which "${clang_format}" >/dev/null; then - continue - fi - installed+=("${clang_format}") - local tmppatch="${tmpdir}/${clang_format}.patch" - # We include in this linter all the changes including the uncommitted changes - # to avoid printing changes already applied. - set -x - # Ignoring the error that git-clang-format outputs. - git -C "${MYDIR}" "${clang_format}" --binary "${clang_format}" \ - --style=file --diff "${MR_ANCESTOR_SHA}" -- >"${tmppatch}" || true - { set +x; } 2>/dev/null - if grep -E '^--- ' "${tmppatch}" | grep -v 'a/third_party' >/dev/null; then - if [[ -n "${LINT_OUTPUT:-}" ]]; then - cp "${tmppatch}" "${LINT_OUTPUT}" - fi - clang_patch="${tmppatch}" - else - echo "clang-format check OK" >&2 - return ${ret} - fi - done - - if [[ ${#installed[@]} -eq 0 ]]; then - echo "You must install clang-format for \"git clang-format\"" >&2 - exit 1 - fi - - # clang-format is installed but found problems. - echo "clang-format findings:" >&2 - "${COLORDIFF_BIN}" < "${clang_patch}" - - echo "clang-format found issues in your patches from ${MR_ANCESTOR_SHA}" \ - "to the current patch. Run \`./ci.sh lint | patch -p1\` from the base" \ - "directory to apply them." >&2 - exit 1 -} - -# Runs clang-tidy on the pending CLs. If the "all" argument is passed it runs -# clang-tidy over all the source files instead. -cmd_tidy() { - local what="${1:-}" - - if [[ -z "${CLANG_TIDY_BIN}" ]]; then - echo "ERROR: You must install clang-tidy-7 or newer to use ci.sh tidy" >&2 - exit 1 - fi - - local git_args=() - if [[ "${what}" == "all" ]]; then - git_args=(ls-files) - shift - else - merge_request_commits - git_args=( - diff-tree --no-commit-id --name-only -r "${MR_ANCESTOR_SHA}" - "${MR_HEAD_SHA}" - ) - fi - - # Clang-tidy needs the compilation database generated by cmake. - if [[ ! -e "${BUILD_DIR}/compile_commands.json" ]]; then - # Generate the build options in debug mode, since we need the debug asserts - # enabled for the clang-tidy analyzer to use them. - CMAKE_BUILD_TYPE="Debug" - cmake_configure - # Build the autogen targets to generate the .h files from the .ui files. - local autogen_targets=( - $(ninja -C "${BUILD_DIR}" -t targets | grep -F _autogen: | - cut -f 1 -d :) - ) - if [[ ${#autogen_targets[@]} != 0 ]]; then - ninja -C "${BUILD_DIR}" "${autogen_targets[@]}" - fi - fi - - cd "${MYDIR}" - local nprocs=$(nproc --all || echo 1) - local ret=0 - if ! parallel -j"${nprocs}" --keep-order -- \ - "${CLANG_TIDY_BIN}" -p "${BUILD_DIR}" -format-style=file -quiet "$@" {} \ - < <(git "${git_args[@]}" | grep -E '(\.cc|\.cpp)$') \ - >"${BUILD_DIR}/clang-tidy.txt"; then - ret=1 - fi - { set +x; } 2>/dev/null - echo "Findings statistics:" >&2 - grep -E ' \[[A-Za-z\.,\-]+\]' -o "${BUILD_DIR}/clang-tidy.txt" | sort \ - | uniq -c >&2 - - if [[ $ret -ne 0 ]]; then - cat >&2 </dev/null - local debsdir="${BUILD_DIR}/debs" - local f - while IFS='' read -r -d '' f; do - echo "=====================================================================" - echo "Package $f:" - dpkg --info $f - dpkg --contents $f - done < <(find "${BUILD_DIR}/debs" -maxdepth 1 -mindepth 1 -type f \ - -name '*.deb' -print0) -} - -build_debian_pkg() { - local srcdir="$1" - local srcpkg="$2" - local options="${3:-}" - - local debsdir="${BUILD_DIR}/debs" - local builddir="${debsdir}/${srcpkg}" - - # debuild doesn't have an easy way to build out of tree, so we make a copy - # of with all symlinks on the first level. - mkdir -p "${builddir}" - for f in $(find "${srcdir}" -mindepth 1 -maxdepth 1 -printf '%P\n'); do - if [[ ! -L "${builddir}/$f" ]]; then - rm -f "${builddir}/$f" - ln -s "${srcdir}/$f" "${builddir}/$f" - fi - done - ( - cd "${builddir}" - debuild "${options}" -b -uc -us - ) -} - -cmd_debian_build() { - local srcpkg="${1:-}" - - case "${srcpkg}" in - jpeg-xl) - build_debian_pkg "${MYDIR}" "jpeg-xl" - ;; - highway) - build_debian_pkg "${MYDIR}/third_party/highway" "highway" "${HWY_PKG_OPTIONS}" - ;; - *) - echo "ERROR: Must pass a valid source package name to build." >&2 - ;; - esac -} - -get_version() { - local varname=$1 - local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1) - [[ -n "${line}" ]] - line="${line#set(${varname} }" - line="${line%)}" - echo "${line}" -} - -cmd_bump_version() { - local newver="${1:-}" - - if ! which dch >/dev/null; then - echo "Missing dch\nTo install it run:\n sudo apt install devscripts" - exit 1 - fi - - if [[ -z "${newver}" ]]; then - local major=$(get_version JPEGXL_MAJOR_VERSION) - local minor=$(get_version JPEGXL_MINOR_VERSION) - local patch=0 - minor=$(( ${minor} + 1)) - else - local major="${newver%%.*}" - newver="${newver#*.}" - local minor="${newver%%.*}" - newver="${newver#${minor}}" - local patch="${newver#.}" - if [[ -z "${patch}" ]]; then - patch=0 - fi - fi - - newver="${major}.${minor}.${patch}" - - echo "Bumping version to ${newver} (${major}.${minor}.${patch})" - sed -E \ - -e "s/(set\\(JPEGXL_MAJOR_VERSION) [0-9]+\\)/\\1 ${major})/" \ - -e "s/(set\\(JPEGXL_MINOR_VERSION) [0-9]+\\)/\\1 ${minor})/" \ - -e "s/(set\\(JPEGXL_PATCH_VERSION) [0-9]+\\)/\\1 ${patch})/" \ - -i lib/CMakeLists.txt - sed -E \ - -e "s/(LIBJXL_VERSION: )\"[0-9.]+\"/\\1\"${major}.${minor}.${patch}\"/" \ - -e "s/(LIBJXL_ABI_VERSION: )\"[0-9.]+\"/\\1\"${major}.${minor}\"/" \ - -i .github/workflows/conformance.yml - - # Update lib.gni - tools/scripts/build_cleaner.py --update - - # Mark the previous version as "unstable". - DEBCHANGE_RELEASE_HEURISTIC=log dch -M --distribution unstable --release '' - DEBCHANGE_RELEASE_HEURISTIC=log dch -M \ - --newversion "${newver}" \ - "Bump JPEG XL version to ${newver}." -} - -# Check that the AUTHORS file contains the email of the committer. -cmd_authors() { - merge_request_commits - local emails - local names - readarray -t emails < <(git log --format='%ae' "${MR_ANCESTOR_SHA}..${MR_HEAD_SHA}") - readarray -t names < <(git log --format='%an' "${MR_ANCESTOR_SHA}..${MR_HEAD_SHA}") - for i in "${!names[@]}"; do - echo "Checking name '${names[$i]}' with email '${emails[$i]}' ..." - "${MYDIR}"/tools/scripts/check_author.py "${emails[$i]}" "${names[$i]}" - done -} - -main() { - local cmd="${1:-}" - if [[ -z "${cmd}" ]]; then - cat >&2 < Build the given source package. - debian_stats Print stats about the built packages. - -oss-fuzz commands: - ossfuzz_asan Build the local source inside oss-fuzz docker with asan. - ossfuzz_msan Build the local source inside oss-fuzz docker with msan. - ossfuzz_ubsan Build the local source inside oss-fuzz docker with ubsan. - ossfuzz_ninja Run ninja on the BUILD_DIR inside the oss-fuzz docker. Extra - parameters are passed to ninja, for example "djxl_fuzzer" will - only build that ninja target. Use for faster build iteration - after one of the ossfuzz_*san commands. - -You can pass some optional environment variables as well: - - BUILD_DIR: The output build directory (by default "$$repo/build") - - BUILD_TARGET: The target triplet used when cross-compiling. - - CMAKE_FLAGS: Convenience flag to pass both CMAKE_C_FLAGS and CMAKE_CXX_FLAGS. - - CMAKE_PREFIX_PATH: Installation prefixes to be searched by the find_package. - - ENABLE_WASM_SIMD=1: enable experimental SIMD in WASM build (only). - - FUZZER_MAX_TIME: "fuzz" command fuzzer running timeout in seconds. - - LINT_OUTPUT: Path to the output patch from the "lint" command. - - SKIP_CPUSET=1: Skip modifying the cpuset in the arm_benchmark. - - SKIP_BUILD=1: Skip the build stage, cmake configure only. - - SKIP_TEST=1: Skip the test stage. - - STORE_IMAGES=0: Makes the benchmark discard the computed images. - - TEST_STACK_LIMIT: Stack size limit (ulimit -s) during tests, in KiB. - - TEST_SELECTOR: pass additional arguments to ctest, e.g. "-R .Resample.". - - STACK_SIZE=1: Generate binaries with the .stack_sizes sections. - -These optional environment variables are forwarded to the cmake call as -parameters: - - CMAKE_BUILD_TYPE - - CMAKE_C_FLAGS - - CMAKE_CXX_FLAGS - - CMAKE_C_COMPILER_LAUNCHER - - CMAKE_CXX_COMPILER_LAUNCHER - - CMAKE_CROSSCOMPILING_EMULATOR - - CMAKE_FIND_ROOT_PATH - - CMAKE_EXE_LINKER_FLAGS - - CMAKE_MAKE_PROGRAM - - CMAKE_MODULE_LINKER_FLAGS - - CMAKE_SHARED_LINKER_FLAGS - - CMAKE_TOOLCHAIN_FILE - -Example: - BUILD_DIR=/tmp/build $0 opt -EOF - exit 1 - fi - - cmd="cmd_${cmd}" - shift - set -x - "${cmd}" "$@" -} - -main "$@" diff --git a/third_party/jpeg-xl/cmake/FindAtomics.cmake b/third_party/jpeg-xl/cmake/FindAtomics.cmake deleted file mode 100644 index 9a6cdc39ec38d..0000000000000 --- a/third_party/jpeg-xl/cmake/FindAtomics.cmake +++ /dev/null @@ -1,53 +0,0 @@ -# Original issue: -# * https://gitlab.kitware.com/cmake/cmake/-/issues/23021#note_1098733 -# -# For reference: -# * https://gcc.gnu.org/wiki/Atomic/GCCMM -# -# riscv64 specific: -# * https://lists.debian.org/debian-riscv/2022/01/msg00009.html -# -# ATOMICS_FOUND - system has c++ atomics -# ATOMICS_LIBRARIES - libraries needed to use c++ atomics - -include(CheckCXXSourceCompiles) - -# RISC-V only has 32-bit and 64-bit atomic instructions. GCC is supposed -# to convert smaller atomics to those larger ones via masking and -# shifting like LLVM, but it’s a known bug that it does not. This means -# anything that wants to use atomics on 1-byte or 2-byte types needs -# -latomic, but not 4-byte or 8-byte (though it does no harm). -set(atomic_code - " - #include - #include - std::atomic n8 (0); // riscv64 - std::atomic n64 (0); // armel, mipsel, powerpc - int main() { - ++n8; - ++n64; - return 0; - }") - -check_cxx_source_compiles("${atomic_code}" ATOMICS_LOCK_FREE_INSTRUCTIONS) - -if(ATOMICS_LOCK_FREE_INSTRUCTIONS) - set(ATOMICS_FOUND TRUE) - set(ATOMICS_LIBRARIES) -else() - set(CMAKE_REQUIRED_LIBRARIES "-latomic") - check_cxx_source_compiles("${atomic_code}" ATOMICS_IN_LIBRARY) - set(CMAKE_REQUIRED_LIBRARIES) - if(ATOMICS_IN_LIBRARY) - set(ATOMICS_LIBRARY atomic) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Atomics DEFAULT_MSG ATOMICS_LIBRARY) - set(ATOMICS_LIBRARIES ${ATOMICS_LIBRARY}) - unset(ATOMICS_LIBRARY) - else() - if(Atomics_FIND_REQUIRED) - message(FATAL_ERROR "Neither lock free instructions nor -latomic found.") - endif() - endif() -endif() -unset(atomic_code) diff --git a/third_party/jpeg-xl/cmake/FindBrotli.cmake b/third_party/jpeg-xl/cmake/FindBrotli.cmake deleted file mode 100644 index 9fb78e47d8964..0000000000000 --- a/third_party/jpeg-xl/cmake/FindBrotli.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -set(brlibs brotlicommon brotlienc brotlidec) - -find_package(PkgConfig QUIET) -if (PkgConfig_FOUND) - foreach(brlib IN ITEMS ${brlibs}) - string(TOUPPER "${brlib}" BRPREFIX) - pkg_check_modules("PC_${BRPREFIX}" lib${brlib}) - endforeach() -endif() - -find_path(BROTLI_INCLUDE_DIR - NAMES brotli/decode.h - HINTS ${PC_BROTLICOMMON_INCLUDEDIR} ${PC_BROTLICOMMON_INCLUDE_DIRS} -) - -foreach(brlib IN ITEMS ${brlibs}) - string(TOUPPER "${brlib}" BRPREFIX) - find_library(${BRPREFIX}_LIBRARY - NAMES ${${BRPREFIX}_NAMES} ${brlib} - HINTS ${PC_${BRPREFIX}_LIBDIR} ${PC_${BRPREFIX}_LIBRARY_DIRS} - ) - - if (${BRPREFIX}_LIBRARY AND NOT TARGET ${brlib}) - if(CMAKE_VERSION VERSION_LESS "3.13.5") - add_library(${brlib} INTERFACE IMPORTED GLOBAL) - set_property(TARGET ${brlib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIR}) - target_link_libraries(${brlib} INTERFACE ${${BRPREFIX}_LIBRARY}) - set_property(TARGET ${brlib} PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_${BRPREFIX}_CFLAGS_OTHER}) - else() - add_library(${brlib} INTERFACE IMPORTED GLOBAL) - target_include_directories(${brlib} - INTERFACE ${BROTLI_INCLUDE_DIR}) - target_link_libraries(${brlib} - INTERFACE ${${BRPREFIX}_LIBRARY}) - target_link_options(${brlib} - INTERFACE ${PC_${BRPREFIX}_LDFLAGS_OTHER}) - target_compile_options(${brlib} - INTERFACE ${PC_${BRPREFIX}_CFLAGS_OTHER}) - endif() - endif() -endforeach() - -if (BROTLICOMMON_FOUND AND BROTLIENC_FOUND AND BROTLIDEC_FOUND) - set(Brotli_FOUND ON) -else () - set(Brotli_FOUND OFF) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Brotli - FOUND_VAR Brotli_FOUND - REQUIRED_VARS - BROTLI_INCLUDE_DIR - BROTLICOMMON_LIBRARY - BROTLIENC_LIBRARY - BROTLIDEC_LIBRARY - VERSION_VAR Brotli_VERSION -) - -mark_as_advanced( - BROTLI_INCLUDE_DIR - BROTLICOMMON_LIBRARY - BROTLIENC_LIBRARY - BROTLIDEC_LIBRARY -) - -if (Brotli_FOUND) - set(Brotli_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIENC_LIBRARY} ${BROTLIDEC_LIBRARY}) - set(Brotli_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) -endif() diff --git a/third_party/jpeg-xl/cmake/FindHWY.cmake b/third_party/jpeg-xl/cmake/FindHWY.cmake deleted file mode 100644 index eab306a8b7826..0000000000000 --- a/third_party/jpeg-xl/cmake/FindHWY.cmake +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -find_package(PkgConfig QUIET) -if (PkgConfig_FOUND) - pkg_check_modules(PC_HWY QUIET libhwy) - set(HWY_VERSION ${PC_HWY_VERSION}) -endif () - -find_path(HWY_INCLUDE_DIR - NAMES hwy/base.h hwy/highway.h - HINTS ${PC_HWY_INCLUDEDIR} ${PC_HWY_INCLUDE_DIRS} -) - -find_library(HWY_LIBRARY - NAMES ${HWY_NAMES} hwy - HINTS ${PC_HWY_LIBDIR} ${PC_HWY_LIBRARY_DIRS} -) - -# If version not found using pkg-config, try extracting it from header files -if (HWY_INCLUDE_DIR AND NOT HWY_VERSION) - set(HWY_VERSION "") - set(HWY_POSSIBLE_HEADERS "${HWY_INCLUDE_DIR}/hwy/base.h" "${HWY_INCLUDE_DIR}/hwy/highway.h") - foreach(HWY_HEADER_FILE IN LISTS HWY_POSSIBLE_HEADERS) - if (EXISTS "${HWY_HEADER_FILE}") - file(READ "${HWY_HEADER_FILE}" HWY_VERSION_CONTENT) - - string(REGEX MATCH "#define HWY_MAJOR +([0-9]+)" _sink "${HWY_VERSION_CONTENT}") - set(HWY_VERSION_MAJOR "${CMAKE_MATCH_1}") - - string(REGEX MATCH "#define +HWY_MINOR +([0-9]+)" _sink "${HWY_VERSION_CONTENT}") - set(HWY_VERSION_MINOR "${CMAKE_MATCH_1}") - - string(REGEX MATCH "#define +HWY_PATCH +([0-9]+)" _sink "${HWY_VERSION_CONTENT}") - set(HWY_VERSION_PATCH "${CMAKE_MATCH_1}") - if (NOT HWY_VERSION_MAJOR STREQUAL "" AND NOT HWY_VERSION_MINOR STREQUAL "" AND NOT HWY_VERSION_PATCH STREQUAL "") - set(HWY_VERSION "${HWY_VERSION_MAJOR}.${HWY_VERSION_MINOR}.${HWY_VERSION_PATCH}") - break() - endif() - endif () - endforeach () - if (NOT HWY_VERSION) - message(WARNING "Highway version not found.") - endif() -endif () - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(HWY - FOUND_VAR HWY_FOUND - REQUIRED_VARS HWY_LIBRARY HWY_INCLUDE_DIR - VERSION_VAR HWY_VERSION -) - -if (HWY_LIBRARY AND NOT TARGET hwy) - add_library(hwy INTERFACE IMPORTED GLOBAL) - - if(CMAKE_VERSION VERSION_LESS "3.13.5") - set_property(TARGET hwy PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${HWY_INCLUDE_DIR}) - target_link_libraries(hwy INTERFACE ${HWY_LIBRARY}) - set_property(TARGET hwy PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_HWY_CFLAGS_OTHER}) - else() - target_include_directories(hwy INTERFACE ${HWY_INCLUDE_DIR}) - target_link_libraries(hwy INTERFACE ${HWY_LIBRARY}) - target_link_options(hwy INTERFACE ${PC_HWY_LDFLAGS_OTHER}) - target_compile_options(hwy INTERFACE ${PC_HWY_CFLAGS_OTHER}) - endif() -endif() - -mark_as_advanced(HWY_INCLUDE_DIR HWY_LIBRARY) - -if (HWY_FOUND) - set(HWY_LIBRARIES ${HWY_LIBRARY}) - set(HWY_INCLUDE_DIRS ${HWY_INCLUDE_DIR}) -endif () diff --git a/third_party/jpeg-xl/cmake/FindLCMS2.cmake b/third_party/jpeg-xl/cmake/FindLCMS2.cmake deleted file mode 100644 index 0a7b54eb963cb..0000000000000 --- a/third_party/jpeg-xl/cmake/FindLCMS2.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -find_package(PkgConfig QUIET) -if (PkgConfig_FOUND) - pkg_check_modules(PC_LCMS2 QUIET libLCMS2) - set(LCMS2_VERSION ${PC_LCMS2_VERSION}) -endif () - -find_path(LCMS2_INCLUDE_DIR - NAMES lcms2.h - HINTS ${PC_LCMS2_INCLUDEDIR} ${PC_LCMS2_INCLUDE_DIRS} -) - -find_library(LCMS2_LIBRARY - NAMES ${LCMS2_NAMES} lcms2 liblcms2 lcms-2 liblcms-2 - HINTS ${PC_LCMS2_LIBDIR} ${PC_LCMS2_LIBRARY_DIRS} -) - -if (LCMS2_INCLUDE_DIR AND NOT LCMS_VERSION) - file(READ ${LCMS2_INCLUDE_DIR}/lcms2.h LCMS2_VERSION_CONTENT) - string(REGEX MATCH "#define[ \t]+LCMS_VERSION[ \t]+([0-9]+)[ \t]*\n" LCMS2_VERSION_MATCH ${LCMS2_VERSION_CONTENT}) - if (LCMS2_VERSION_MATCH) - string(SUBSTRING ${CMAKE_MATCH_1} 0 1 LCMS2_VERSION_MAJOR) - string(SUBSTRING ${CMAKE_MATCH_1} 1 2 LCMS2_VERSION_MINOR) - set(LCMS2_VERSION "${LCMS2_VERSION_MAJOR}.${LCMS2_VERSION_MINOR}") - endif () -endif () - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LCMS2 - FOUND_VAR LCMS2_FOUND - REQUIRED_VARS LCMS2_LIBRARY LCMS2_INCLUDE_DIR - VERSION_VAR LCMS2_VERSION -) - -if (LCMS2_LIBRARY AND NOT TARGET lcms2) - add_library(lcms2 INTERFACE IMPORTED GLOBAL) - - if(CMAKE_VERSION VERSION_LESS "3.13.5") - set_property(TARGET lcms2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LCMS2_INCLUDE_DIR}) - target_link_libraries(lcms2 INTERFACE ${LCMS2_LIBRARY}) - set_property(TARGET lcms2 PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_LCMS2_CFLAGS_OTHER}) - else() - target_include_directories(lcms2 INTERFACE ${LCMS2_INCLUDE_DIR}) - target_link_libraries(lcms2 INTERFACE ${LCMS2_LIBRARY}) - target_link_options(lcms2 INTERFACE ${PC_LCMS2_LDFLAGS_OTHER}) - target_compile_options(lcms2 INTERFACE ${PC_LCMS2_CFLAGS_OTHER}) - endif() -endif() - -mark_as_advanced(LCMS2_INCLUDE_DIR LCMS2_LIBRARY) - -if (LCMS2_FOUND) - set(LCMS2_LIBRARIES ${LCMS2_LIBRARY}) - set(LCMS2_INCLUDE_DIRS ${LCMS2_INCLUDE_DIR}) -endif () diff --git a/third_party/jpeg-xl/debian/changelog b/third_party/jpeg-xl/debian/changelog deleted file mode 100644 index 503d8f216575f..0000000000000 --- a/third_party/jpeg-xl/debian/changelog +++ /dev/null @@ -1,125 +0,0 @@ -jpeg-xl (0.11.1) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.11.1. - - -- JPEG XL Maintainers Tue, 26 Nov 2024 12:09:23 +0100 - -jpeg-xl (0.11.0) unstable; urgency=medium - - * Bump JPEG XL version to 0.11.0. - - -- JPEG XL Maintainers Tue, 06 Aug 2024 14:35:34 +0200 - -jpeg-xl (0.10.2) unstable; urgency=medium - - * Bump JPEG XL version to 0.10.2. - - -- JPEG XL Maintainers Thu, 07 Mar 2024 13:50:05 +0100 - -jpeg-xl (0.10.1) unstable; urgency=medium - - * Bump JPEG XL version to 0.10.1. - - -- JPEG XL Maintainers Wed, 28 Feb 2024 12:52:20 +0100 - -jpeg-xl (0.10.0) unstable; urgency=medium - - * Bump JPEG XL version to 0.10.0. - - -- JPEG XL Maintainers Wed, 21 Feb 2024 08:37:00 +0100 - -jpeg-xl (0.9.0) unstable; urgency=medium - - * Bump JPEG XL version to 0.9.0. - - -- JPEG XL Maintainers Tue, 21 Nov 2023 10:32:59 +0100 - -jpeg-xl (0.8) unstable; urgency=medium - - * Bump JPEG XL version to 0.8. - - -- JPEG XL Maintainers Wed, 11 Jan 2023 16:12:34 +0000 - -jpeg-xl (0.7) unstable; urgency=medium - - * Bump JPEG XL version to 0.7. - - -- JPEG XL Maintainers Mon, 08 Aug 2022 14:43:58 +0000 - -jpeg-xl (0.6) unstable; urgency=medium - - * Bump JPEG XL version to 0.6. - - -- JPEG XL Maintainers Fri, 10 Sep 2021 16:08:17 +0200 - -jpeg-xl (0.5.0) unstable; urgency=medium - - * Bump JPEG XL version to 0.5.0. - - -- JPEG XL Maintainers Thu, 12 Aug 2021 23:49:40 +0200 - -jpeg-xl (0.3.7) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.7. - - -- Sami Boukortt Mon, 29 Mar 2021 12:14:20 +0200 - -jpeg-xl (0.3.6) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.6. - - -- Sami Boukortt Thu, 25 Mar 2021 17:40:58 +0100 - -jpeg-xl (0.3.5) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.5. - - -- Sami Boukortt Tue, 23 Mar 2021 15:20:44 +0100 - -jpeg-xl (0.3.4) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.4. - - -- Sami Boukortt Tue, 16 Mar 2021 12:13:59 +0100 - -jpeg-xl (0.3.3) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.3. - - -- Sami Boukortt Fri, 5 Mar 2021 19:15:26 +0100 - -jpeg-xl (0.3.2) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.2. - - -- Alex Deymo Fri, 12 Feb 2021 21:00:12 +0100 - -jpeg-xl (0.3.1) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3.1. - - -- Alex Deymo Tue, 09 Feb 2021 09:48:43 +0100 - -jpeg-xl (0.3) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.3. - - -- Alex Deymo Wed, 27 Jan 2021 22:36:32 +0100 - -jpeg-xl (0.2) UNRELEASED; urgency=medium - - * Bump JPEG XL version to 0.2. - - -- Alex Deymo Wed, 23 Nov 2020 20:42:10 +0100 - -jpeg-xl (0.1) UNRELEASED; urgency=medium - - * JPEG XL format release candidate. - - -- Alex Deymo Fri, 13 Nov 2020 17:42:24 +0100 - -jpeg-xl (0.0.2-1) UNRELEASED; urgency=medium - - * Initial debian package. - - -- Alex Deymo Tue, 27 Oct 2020 15:27:59 +0100 diff --git a/third_party/jpeg-xl/debian/compat b/third_party/jpeg-xl/debian/compat deleted file mode 100644 index f599e28b8ab0d..0000000000000 --- a/third_party/jpeg-xl/debian/compat +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/third_party/jpeg-xl/debian/control b/third_party/jpeg-xl/debian/control deleted file mode 100644 index e0169a3488474..0000000000000 --- a/third_party/jpeg-xl/debian/control +++ /dev/null @@ -1,87 +0,0 @@ -Source: jpeg-xl -Maintainer: JPEG XL Maintainers -Section: misc -Priority: optional -Standards-Version: 3.9.8 -Build-Depends: - asciidoc, - cmake, - debhelper (>= 9), - libbrotli-dev, - libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev, - libgif-dev, - libgimp2.0-dev, - libgoogle-perftools-dev, - libgtest-dev, - libhwy-dev (>= 1.0.0), - libjpeg-dev, - libopenexr-dev, - libpng-dev, - libwebp-dev, - pkg-config, - xdg-utils, - xmlto, -Homepage: https://github.com/libjxl/libjxl -Rules-Requires-Root: no - -Package: jxl -Architecture: any -Section: utils -Depends: ${misc:Depends}, ${shlibs:Depends} -Description: JPEG XL Image Coding System - "JXL" (command line utility) - The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and - lossless image compression format. It has a rich feature set and is - particularly optimized for responsive web environments, so that - content renders well on a wide range of devices. Moreover, it includes - several features that help transition from the legacy JPEG format. - . - This package installs the command line utilities. - -Package: libjxl-dev -Architecture: any -Section: libdevel -Depends: libjxl (= ${binary:Version}), ${misc:Depends} - libhwy-dev, -Description: JPEG XL Image Coding System - "JXL" (development files) - The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and - lossless image compression format. It has a rich feature set and is - particularly optimized for responsive web environments, so that - content renders well on a wide range of devices. Moreover, it includes - several features that help transition from the legacy JPEG format. - . - This package installs development files. - -Package: libjxl -Architecture: any -Multi-Arch: same -Section: libs -Depends: ${shlibs:Depends}, ${misc:Depends} -Pre-Depends: ${misc:Pre-Depends} -Description: JPEG XL Image Coding System - "JXL" (shared libraries) - The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and - lossless image compression format. It has a rich feature set and is - particularly optimized for responsive web environments, so that - content renders well on a wide range of devices. Moreover, it includes - several features that help transition from the legacy JPEG format. - . - This package installs shared libraries. - -Package: libjxl-gdk-pixbuf -Architecture: any -Multi-Arch: same -Section: libs -Depends: ${shlibs:Depends}, ${misc:Depends} -Pre-Depends: ${misc:Pre-Depends} -Description: JPEG XL Plugin for gdk-pixbuf - This package installs the required files for reading JPEG XL files in - GTK applications. - -Package: libjxl-gimp-plugin -Architecture: any -Multi-Arch: same -Section: graphics -Depends: ${shlibs:Depends}, ${misc:Depends} -Pre-Depends: ${misc:Pre-Depends} -Enhances: gimp -Description: JPEG XL Import and Export Plugin for GIMP - This is a plugin for GIMP version 2.10.x to import and export JPEG XL images. diff --git a/third_party/jpeg-xl/debian/copyright b/third_party/jpeg-xl/debian/copyright deleted file mode 100644 index 7786a8775b058..0000000000000 --- a/third_party/jpeg-xl/debian/copyright +++ /dev/null @@ -1,199 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: jpeg-xl - -Files: * -Copyright: 2020 the JPEG XL Project -License: BSD-3-clause - -Files: third_party/libjpeg-turbo/* -Copyright (C)2009-2023 D. R. Commander. All Rights Reserved. -Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. -License: BSD-3-clause - -Files: third_party/sjpeg/* -Copyright: 2017 Google, Inc -License: Apache-2.0 - -Files: third_party/skcms/* -Copyright: 2018 Google Inc. -License: BSD-3-clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - . - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Files: testdata/external/pngsuite/* -Copyright: Willem van Schaik, 1996, 2011 -License: PngSuite License - See http://www.schaik.com/pngsuite/ for details. - . - Permission to use, copy, modify and distribute these images for any - purpose and without fee is hereby granted. - -Files: testdata/external/raw.pixls/* -Copyright: their respective owners listed in https://raw.pixls.us/ -License: CC0-1.0 - -Files: testdata/external/wesaturate/* -Copyright: their respective owners listed in https://www.wesaturate.com/ -License: CC0-1.0 - -Files: testdata/external/wide-gamut-tests/ -Copyright: github.com/codelogic/wide-gamut-tests authors. -License: Apache-2.0 - -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the complete text of the Apache License, Version 2 - can be found in "/usr/share/common-licenses/Apache-2.0". - -License: CC0 - Creative Commons Zero v1.0 Universal - . - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL - SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT - RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" - BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS - DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS - LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE - INFORMATION OR WORKS PROVIDED HEREUNDER. - . - Statement of Purpose - . - The laws of most jurisdictions throughout the world automatically confer - exclusive Copyright and Related Rights (defined below) upon the creator and - subsequent owner(s) (each and all, an "owner") of an original work of - authorship and/or a database (each, a "Work"). - . - Certain owners wish to permanently relinquish those rights to a Work for the - purpose of contributing to a commons of creative, cultural and scientific - works ("Commons") that the public can reliably and without fear of later - claims of infringement build upon, modify, incorporate in other works, reuse - and redistribute as freely as possible in any form whatsoever and for any - purposes, including without limitation commercial purposes. These owners may - contribute to the Commons to promote the ideal of a free culture and the - further production of creative, cultural and scientific works, or to gain - reputation or greater distribution for their Work in part through the use - and efforts of others. - . - For these and/or other purposes and motivations, and without any expectation - of additional consideration or compensation, the person associating CC0 with - a Work (the "Affirmer"), to the extent that he or she is an owner of - Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to - the Work and publicly distribute the Work under its terms, with knowledge of - his or her Copyright and Related Rights in the Work and the meaning and - intended legal effect of CC0 on those rights. - . - 1. Copyright and Related Rights. A Work made available under CC0 may be - protected by copyright and related or neighboring rights ("Copyright and - Related Rights"). Copyright and Related Rights include, but are not limited - to, the following: - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); - iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation thereof, - including any amended or successor version of such directive); and - vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national implementations - thereof. - . - 2. Waiver. To the greatest extent permitted by, but not in contravention of, - applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and - unconditionally waives, abandons, and surrenders all of Affirmer's Copyright - and Related Rights and associated claims and causes of action, whether now - known or unknown (including existing as well as future claims and causes of - action), in the Work (i) in all territories worldwide, (ii) for the maximum - duration provided by applicable law or treaty (including future time - extensions), (iii) in any current or future medium and for any number of - copies, and (iv) for any purpose whatsoever, including without limitation - commercial, advertising or promotional purposes (the "Waiver"). Affirmer - makes the Waiver for the benefit of each member of the public at large and - to the detriment of Affirmer's heirs and successors, fully intending that - such Waiver shall not be subject to revocation, rescission, cancellation, - termination, or any other legal or equitable action to disrupt the quiet - enjoyment of the Work by the public as contemplated by Affirmer's express - Statement of Purpose. - . - 3. Public License Fallback. Should any part of the Waiver for any reason be - judged legally invalid or ineffective under applicable law, then the Waiver - shall be preserved to the maximum extent permitted taking into account - Affirmer's express Statement of Purpose. In addition, to the extent the - Waiver is so judged Affirmer hereby grants to each affected person a - royalty-free, non transferable, non sublicensable, non exclusive, - irrevocable and unconditional license to exercise Affirmer's Copyright and - Related Rights in the Work (i) in all territories worldwide, (ii) for the - maximum duration provided by applicable law or treaty (including future time - extensions), (iii) in any current or future medium and for any number of - copies, and (iv) for any purpose whatsoever, including without limitation - commercial, advertising or promotional purposes (the "License"). The License - shall be deemed effective as of the date CC0 was applied by Affirmer to the - Work. Should any part of the License for any reason be judged legally - invalid or ineffective under applicable law, such partial invalidity or - ineffectiveness shall not invalidate the remainder of the License, and in - such case Affirmer hereby affirms that he or she will not (i) exercise any - of his or her remaining Copyright and Related Rights in the Work or (ii) - assert any associated claims and causes of action with respect to the Work, - in either case contrary to Affirmer's express Statement of Purpose. - . - 4. Limitations and Disclaimers. - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, statutory or - otherwise, including without limitation warranties of title, - merchantability, fitness for a particular purpose, non infringement, or the - absence of latent or other defects, accuracy, or the present or absence of - errors, whether or not discoverable, all to the greatest extent permissible - under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without limitation - any person's Copyright and Related Rights in the Work. Further, Affirmer - disclaims responsibility for obtaining any necessary consents, permissions - or other rights required for any use of the Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to this - CC0 or use of the Work. - . - For more information, please see: - http://creativecommons.org/publicdomain/zero/1.0/> - diff --git a/third_party/jpeg-xl/debian/jxl.install b/third_party/jpeg-xl/debian/jxl.install deleted file mode 100644 index c3bae3ed10b98..0000000000000 --- a/third_party/jpeg-xl/debian/jxl.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/bin/* -usr/share/man/man1/cjxl.1 -usr/share/man/man1/djxl.1 diff --git a/third_party/jpeg-xl/debian/libjxl-dev.install b/third_party/jpeg-xl/debian/libjxl-dev.install deleted file mode 100644 index ebe4ac4ae577c..0000000000000 --- a/third_party/jpeg-xl/debian/libjxl-dev.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/include/jxl/*.h -usr/lib/*/*.so -usr/lib/*/pkgconfig/*.pc diff --git a/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install b/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install deleted file mode 100644 index 12d2ab250f97b..0000000000000 --- a/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/lib/*/gdk-pixbuf-*/*/loaders/* -usr/share/mime/packages/image-jxl.xml -usr/share/thumbnailers/jxl.thumbnailer diff --git a/third_party/jpeg-xl/debian/libjxl-gimp-plugin.install b/third_party/jpeg-xl/debian/libjxl-gimp-plugin.install deleted file mode 100644 index 353431dba3607..0000000000000 --- a/third_party/jpeg-xl/debian/libjxl-gimp-plugin.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/gimp diff --git a/third_party/jpeg-xl/debian/libjxl.install b/third_party/jpeg-xl/debian/libjxl.install deleted file mode 100644 index cd157a7a5cbf7..0000000000000 --- a/third_party/jpeg-xl/debian/libjxl.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/libjxl*.so.* diff --git a/third_party/jpeg-xl/debian/rules b/third_party/jpeg-xl/debian/rules deleted file mode 100755 index 6259dbfc61e5c..0000000000000 --- a/third_party/jpeg-xl/debian/rules +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/make -f - -include /usr/share/dpkg/pkg-info.mk - -%: - dh $@ --buildsystem=cmake - -override_dh_auto_configure: - # TODO(deymo): Remove the DCMAKE_BUILD_TYPE once builds without NDEBUG - # are as useful as Release builds. - # TODO(szabadka) Re-enable jpegli after tests are fixed on Ubuntu 20.04, - # and debian:buster - dh_auto_configure -- \ - -DJPEGXL_VERSION=$(DEB_VERSION) \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DJPEGXL_FORCE_SYSTEM_GTEST=ON \ - -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \ - -DJPEGXL_FORCE_SYSTEM_HWY=ON \ - -DJPEGXL_ENABLE_JPEGLI=OFF \ - -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF \ - -DJPEGXL_ENABLE_PLUGINS=ON diff --git a/third_party/jpeg-xl/debian/source/format b/third_party/jpeg-xl/debian/source/format deleted file mode 100644 index 163aaf8d82b6c..0000000000000 --- a/third_party/jpeg-xl/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/third_party/jpeg-xl/deps.sh b/third_party/jpeg-xl/deps.sh deleted file mode 100755 index 069c721de1500..0000000000000 --- a/third_party/jpeg-xl/deps.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# This file downloads the dependencies needed to build JPEG XL into third_party. -# These dependencies are normally pulled by git. - -set -eu - -SELF=$(realpath "$0") -MYDIR=$(dirname "${SELF}") - -# Git revisions we use for the given submodules. Update these whenever you -# update a git submodule. -TESTDATA="873045a9c42ed60721756e26e2a6b32e17415205" -THIRD_PARTY_BROTLI="36533a866ed1ca4b75cf049f4521e4ec5fe24727" -THIRD_PARTY_GOOGLETEST="58d77fa8070e8cec2dc1ed015d66b454c8d78850" -THIRD_PARTY_HIGHWAY="457c891775a7397bdb0376bb1031e6e027af1c48" -THIRD_PARTY_SKCMS="42030a771244ba67f86b1c1c76a6493f873c5f91" -THIRD_PARTY_SJPEG="e5ab13008bb214deb66d5f3e17ca2f8dbff150bf" -THIRD_PARTY_ZLIB="51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf" # v1.3.1 -THIRD_PARTY_LIBPNG="f135775ad4e5d4408d2e12ffcc71bb36e6b48551" # v1.6.40 -THIRD_PARTY_LIBJPEG_TURBO="8ecba3647edb6dd940463fedf38ca33a8e2a73d1" # 2.1.5.1 - -# Download the target revision from GitHub. -download_github() { - local path="$1" - local project="$2" - - local varname=`echo "$path" | tr '[:lower:]' '[:upper:]'` - varname="${varname//[\/-]/_}" - local sha - eval "sha=\${${varname}}" - - local down_dir="${MYDIR}/downloads" - local local_fn="${down_dir}/${sha}.tar.gz" - if [[ -e "${local_fn}" && -d "${MYDIR}/${path}" ]]; then - echo "${path} already up to date." >&2 - return 0 - fi - - local url - local strip_components=0 - if [[ "${project:0:4}" == "http" ]]; then - # "project" is a googlesource.com base url. - url="${project}${sha}.tar.gz" - else - # GitHub files have a top-level directory - strip_components=1 - url="https://github.com/${project}/tarball/${sha}" - fi - - echo "Downloading ${path} version ${sha}..." >&2 - mkdir -p "${down_dir}" - curl -L --show-error -o "${local_fn}.tmp" "${url}" - mkdir -p "${MYDIR}/${path}" - tar -zxf "${local_fn}.tmp" -C "${MYDIR}/${path}" \ - --strip-components="${strip_components}" - mv "${local_fn}.tmp" "${local_fn}" -} - -is_git_repository() { - local dir="$1" - local toplevel=$(git rev-parse --show-toplevel) - - [[ "${dir}" == "${toplevel}" ]] -} - - -main() { - if is_git_repository "${MYDIR}"; then - cat >&2 < -#include -#include -#include -#include -#include - -#include - -bool DecodeJpegXlExif(const uint8_t* jxl, size_t size, - std::vector* exif) { - auto dec = JxlDecoderMake(nullptr); - - // We're only interested in the Exif boxes in this example, so don't - // subscribe to events related to pixel data. - if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents( - dec.get(), JXL_DEC_BOX | JXL_DEC_BOX_COMPLETE)) { - fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); - return false; - } - bool support_decompression = true; - if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec.get(), JXL_TRUE)) { - fprintf(stderr, - "NOTE: decompressing brob boxes not supported with the currently " - "used jxl library.\n"); - support_decompression = false; - } - - JxlDecoderSetInput(dec.get(), jxl, size); - JxlDecoderCloseInput(dec.get()); - - const constexpr size_t kChunkSize = 65536; - size_t output_pos = 0; - - for (;;) { - JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); - if (status == JXL_DEC_ERROR) { - fprintf(stderr, "Decoder error\n"); - return false; - } else if (status == JXL_DEC_NEED_MORE_INPUT) { - fprintf(stderr, "Error, already provided all input\n"); - return false; - } else if (status == JXL_DEC_BOX) { - if (!exif->empty()) { - size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); - exif->resize(exif->size() - remaining); - // No need to wait for JXL_DEC_SUCCESS or decode other boxes. - return true; - } - JxlBoxType type; - status = JxlDecoderGetBoxType(dec.get(), type, - TO_JXL_BOOL(support_decompression)); - if (JXL_DEC_SUCCESS != status) { - fprintf(stderr, "Error, failed to get box type\n"); - return false; - } - if (!memcmp(type, "Exif", 4)) { - exif->resize(kChunkSize); - JxlDecoderSetBoxBuffer(dec.get(), exif->data(), exif->size()); - } - } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { - size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); - output_pos += kChunkSize - remaining; - exif->resize(exif->size() + kChunkSize); - JxlDecoderSetBoxBuffer(dec.get(), exif->data() + output_pos, - exif->size() - output_pos); - } else if (status == JXL_DEC_BOX_COMPLETE) { - if (!exif->empty()) { - size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); - exif->resize(exif->size() - remaining); - return true; - } - return true; - } else { - fprintf(stderr, "Unknown decoder status\n"); - return false; - } - } -} - -bool LoadFile(const char* filename, std::vector* out) { - FILE* file = fopen(filename, "rb"); - if (!file) { - return false; - } - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return false; - } - - long size = ftell(file); // NOLINT - // Avoid invalid file or directory. - if (size >= LONG_MAX || size < 0) { - fclose(file); - return false; - } - - if (fseek(file, 0, SEEK_SET) != 0) { - fclose(file); - return false; - } - - out->resize(size); - size_t readsize = fread(out->data(), 1, size, file); - if (fclose(file) != 0) { - return false; - } - - return readsize == static_cast(size); -} - -bool WriteFile(const char* filename, const uint8_t* data, size_t size) { - FILE* file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Could not open %s for writing", filename); - return false; - } - fwrite(data, 1, size, file); - if (fclose(file) != 0) { - return false; - } - return true; -} - -int main(int argc, char* argv[]) { - if (argc != 3) { - fprintf(stderr, - "Usage: %s \n" - "Where:\n" - " jxl = input JPEG XL image filename\n" - " exif = output exif filename\n" - "Output files will be overwritten.\n", - argv[0]); - return 1; - } - - const char* jxl_filename = argv[1]; - const char* exif_filename = argv[2]; - - std::vector jxl; - if (!LoadFile(jxl_filename, &jxl)) { - fprintf(stderr, "couldn't load %s\n", jxl_filename); - return 1; - } - - std::vector exif; - if (!DecodeJpegXlExif(jxl.data(), jxl.size(), &exif)) { - fprintf(stderr, "Error while decoding the jxl file\n"); - return 1; - } - if (exif.empty()) { - printf("No exif data present in this image\n"); - } else { - // TODO(lode): the exif box data contains the 4-byte TIFF header at the - // beginning, check whether this is desired to be part of the output, or - // should be removed. - if (!WriteFile(exif_filename, exif.data(), exif.size())) { - fprintf(stderr, "Error while writing the exif file\n"); - return 1; - } - printf("Successfully wrote %s\n", exif_filename); - } - return 0; -} diff --git a/third_party/jpeg-xl/examples/decode_oneshot.cc b/third_party/jpeg-xl/examples/decode_oneshot.cc deleted file mode 100644 index b7e17c21a497a..0000000000000 --- a/third_party/jpeg-xl/examples/decode_oneshot.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This C++ example decodes a JPEG XL image in one shot (all input bytes -// available at once). The example outputs the pixels and color information to a -// floating point image and an ICC profile on disk. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are - * stored as floating point, as interleaved RGBA (4 floating point values per - * pixel), line per line from top to bottom. Pixel values have nominal range - * 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile - * describes the color format of the pixel data. - */ -bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size, - std::vector* pixels, size_t* xsize, - size_t* ysize, std::vector* icc_profile) { - // Multi-threaded parallel runner. - auto runner = JxlResizableParallelRunnerMake(nullptr); - - auto dec = JxlDecoderMake(nullptr); - if (JXL_DEC_SUCCESS != - JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | - JXL_DEC_COLOR_ENCODING | - JXL_DEC_FULL_IMAGE)) { - fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); - return false; - } - - if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), - JxlResizableParallelRunner, - runner.get())) { - fprintf(stderr, "JxlDecoderSetParallelRunner failed\n"); - return false; - } - - JxlBasicInfo info; - JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; - - JxlDecoderSetInput(dec.get(), jxl, size); - JxlDecoderCloseInput(dec.get()); - - for (;;) { - JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); - - if (status == JXL_DEC_ERROR) { - fprintf(stderr, "Decoder error\n"); - return false; - } else if (status == JXL_DEC_NEED_MORE_INPUT) { - fprintf(stderr, "Error, already provided all input\n"); - return false; - } else if (status == JXL_DEC_BASIC_INFO) { - if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) { - fprintf(stderr, "JxlDecoderGetBasicInfo failed\n"); - return false; - } - *xsize = info.xsize; - *ysize = info.ysize; - JxlResizableParallelRunnerSetThreads( - runner.get(), - JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize)); - } else if (status == JXL_DEC_COLOR_ENCODING) { - // Get the ICC color profile of the pixel data - size_t icc_size; - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, - &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - return false; - } - icc_profile->resize(icc_size); - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( - dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, - icc_profile->data(), icc_profile->size())) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } - } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { - size_t buffer_size; - if (JXL_DEC_SUCCESS != - JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) { - fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n"); - return false; - } - if (buffer_size != *xsize * *ysize * 16) { - fprintf(stderr, "Invalid out buffer size %d %d\n", - static_cast(buffer_size), - static_cast(*xsize * *ysize * 16)); - return false; - } - pixels->resize(*xsize * *ysize * 4); - void* pixels_buffer = static_cast(pixels->data()); - size_t pixels_buffer_size = pixels->size() * sizeof(float); - if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, - pixels_buffer, - pixels_buffer_size)) { - fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n"); - return false; - } - } else if (status == JXL_DEC_FULL_IMAGE) { - // Nothing to do. Do not yet return. If the image is an animation, more - // full frames may be decoded. This example only keeps the last one. - } else if (status == JXL_DEC_SUCCESS) { - // All decoding successfully finished. - // It's not required to call JxlDecoderReleaseInput(dec.get()) here since - // the decoder will be destroyed. - return true; - } else { - fprintf(stderr, "Unknown decoder status\n"); - return false; - } - } -} - -/** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick - * support viewing this format. - * The input pixels are given as 32-bit floating point with 4-channel RGBA. - * The alpha channel will not be written since .pfm does not support it. - */ -bool WritePFM(const char* filename, const float* pixels, size_t xsize, - size_t ysize) { - FILE* file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Could not open %s for writing", filename); - return false; - } - uint32_t endian_test = 1; - uint8_t little_endian[4]; - memcpy(little_endian, &endian_test, 4); - - fprintf(file, "PF\n%d %d\n%s\n", static_cast(xsize), - static_cast(ysize), little_endian[0] ? "-1.0" : "1.0"); - for (int y = ysize - 1; y >= 0; y--) { - for (size_t x = 0; x < xsize; x++) { - for (size_t c = 0; c < 3; c++) { - const float* f = &pixels[(y * xsize + x) * 4 + c]; - fwrite(f, 4, 1, file); - } - } - } - if (fclose(file) != 0) { - return false; - } - return true; -} - -bool LoadFile(const char* filename, std::vector* out) { - FILE* file = fopen(filename, "rb"); - if (!file) { - return false; - } - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return false; - } - - long size = ftell(file); // NOLINT - // Avoid invalid file or directory. - if (size >= LONG_MAX || size < 0) { - fclose(file); - return false; - } - - if (fseek(file, 0, SEEK_SET) != 0) { - fclose(file); - return false; - } - - out->resize(size); - size_t readsize = fread(out->data(), 1, size, file); - if (fclose(file) != 0) { - return false; - } - - return readsize == static_cast(size); -} - -bool WriteFile(const char* filename, const uint8_t* data, size_t size) { - FILE* file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Could not open %s for writing", filename); - return false; - } - fwrite(data, 1, size, file); - if (fclose(file) != 0) { - return false; - } - return true; -} - -int main(int argc, char* argv[]) { - if (argc != 4) { - fprintf(stderr, - "Usage: %s \n" - "Where:\n" - " jxl = input JPEG XL image filename\n" - " pfm = output Portable FloatMap image filename\n" - " icc = output ICC color profile filename\n" - "Output files will be overwritten.\n", - argv[0]); - return 1; - } - - const char* jxl_filename = argv[1]; - const char* pfm_filename = argv[2]; - const char* icc_filename = argv[3]; - - std::vector jxl; - if (!LoadFile(jxl_filename, &jxl)) { - fprintf(stderr, "couldn't load %s\n", jxl_filename); - return 1; - } - - std::vector pixels; - std::vector icc_profile; - size_t xsize = 0; - size_t ysize = 0; - if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize, - &icc_profile)) { - fprintf(stderr, "Error while decoding the jxl file\n"); - return 1; - } - if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) { - fprintf(stderr, "Error while writing the PFM image file\n"); - return 1; - } - if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) { - fprintf(stderr, "Error while writing the ICC profile file\n"); - return 1; - } - printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename); - return 0; -} diff --git a/third_party/jpeg-xl/examples/decode_progressive.cc b/third_party/jpeg-xl/examples/decode_progressive.cc deleted file mode 100644 index 7a3a9aa33b702..0000000000000 --- a/third_party/jpeg-xl/examples/decode_progressive.cc +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This C++ example decodes a JPEG XL image progressively (input bytes are -// passed in chunks). The example outputs the intermediate steps to PAM files. - -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#include -#include -#include -#include -#include - -#include // PRIu64 -#include -#include -#include -#include - -bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) { - FILE* fp = fopen(filename, "wb"); - if (!fp) { - fprintf(stderr, "Could not open %s for writing", filename); - return false; - } - fprintf(fp, - "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE " - "RGB_ALPHA\nENDHDR\n", - static_cast(w), static_cast(h)); - size_t num_bytes = w * h * 4; - if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) { - fclose(fp); - return false; - }; - if (fclose(fp) != 0) { - return false; - } - return true; -} - -/** Decodes JPEG XL image to 8-bit integer RGBA pixels and an ICC Profile, in a - * progressive way, saving the intermediate steps. - */ -bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size, - const char* filename, size_t chunksize) { - std::vector pixels; - std::vector icc_profile; - size_t xsize = 0; - size_t ysize = 0; - - // Multi-threaded parallel runner. - auto runner = JxlResizableParallelRunnerMake(nullptr); - - auto dec = JxlDecoderMake(nullptr); - if (JXL_DEC_SUCCESS != - JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | - JXL_DEC_COLOR_ENCODING | - JXL_DEC_FULL_IMAGE)) { - fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); - return false; - } - - if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), - JxlResizableParallelRunner, - runner.get())) { - fprintf(stderr, "JxlDecoderSetParallelRunner failed\n"); - return false; - } - - JxlBasicInfo info; - JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0}; - - size_t seen = 0; - JxlDecoderSetInput(dec.get(), jxl, chunksize); - size_t remaining = chunksize; - - for (;;) { - JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); - - if (status == JXL_DEC_ERROR) { - fprintf(stderr, "Decoder error\n"); - return false; - } else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS || - status == JXL_DEC_FULL_IMAGE) { - seen += remaining - JxlDecoderReleaseInput(dec.get()); - printf("Flushing after %" PRIu64 " bytes\n", static_cast(seen)); - if (status == JXL_DEC_NEED_MORE_INPUT && - JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec.get())) { - printf("flush error (no preview yet)\n"); - } else { - char fname[1024]; - if (snprintf(fname, 1024, "%s-%" PRIu64 ".pam", filename, - static_cast(seen)) >= 1024) { - fprintf(stderr, "Filename too long\n"); - return false; - }; - if (!WritePAM(fname, pixels.data(), xsize, ysize)) { - fprintf(stderr, "Error writing progressive output\n"); - } - } - remaining = size - seen; - if (remaining > chunksize) remaining = chunksize; - if (remaining == 0) { - if (status == JXL_DEC_NEED_MORE_INPUT) { - fprintf(stderr, "Error, already provided all input\n"); - return false; - } else { - return true; - } - } - JxlDecoderSetInput(dec.get(), jxl + seen, remaining); - } else if (status == JXL_DEC_BASIC_INFO) { - if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) { - fprintf(stderr, "JxlDecoderGetBasicInfo failed\n"); - return false; - } - xsize = info.xsize; - ysize = info.ysize; - JxlResizableParallelRunnerSetThreads( - runner.get(), - JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize)); - } else if (status == JXL_DEC_COLOR_ENCODING) { - // Get the ICC color profile of the pixel data - size_t icc_size; - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize( - dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL, &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - return false; - } - icc_profile.resize(icc_size); - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( - dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL, - icc_profile.data(), icc_profile.size())) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } - } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { - size_t buffer_size; - if (JXL_DEC_SUCCESS != - JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) { - fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n"); - return false; - } - if (buffer_size != xsize * ysize * 4) { - fprintf(stderr, "Invalid out buffer size %" PRIu64 " != %" PRIu64 "\n", - static_cast(buffer_size), - static_cast(xsize * ysize * 4)); - return false; - } - pixels.resize(xsize * ysize * 4); - if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, - pixels.data(), - pixels.size())) { - fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n"); - return false; - } - } else { - fprintf(stderr, "Unknown decoder status\n"); - return false; - } - } -} - -bool LoadFile(const char* filename, std::vector* out) { - FILE* file = fopen(filename, "rb"); - if (!file) { - return false; - } - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return false; - } - - long size = ftell(file); // NOLINT - // Avoid invalid file or directory. - if (size >= LONG_MAX || size < 0) { - fclose(file); - return false; - } - - if (fseek(file, 0, SEEK_SET) != 0) { - fclose(file); - return false; - } - - out->resize(size); - size_t readsize = fread(out->data(), 1, size, file); - if (fclose(file) != 0) { - return false; - } - - return readsize == static_cast(size); -} - -int main(int argc, char* argv[]) { - if (argc < 3) { - fprintf( - stderr, - "Usage: %s [chunksize]\n" - "Where:\n" - " jxl = input JPEG XL image filename\n" - " basename = prefix of output filenames\n" - " chunksize = loads chunksize bytes at a time and writes\n" - " intermediate results to basename-[bytes loaded].pam\n" - "Output files will be overwritten.\n", - argv[0]); - return 1; - } - - const char* jxl_filename = argv[1]; - const char* png_filename = argv[2]; - - std::vector jxl; - if (!LoadFile(jxl_filename, &jxl)) { - fprintf(stderr, "couldn't load %s\n", jxl_filename); - return 1; - } - size_t chunksize = jxl.size(); - if (argc > 3) { - long cs = atol(argv[3]); // NOLINT - if (cs < 100) { - fprintf(stderr, "Chunk size is too low, try at least 100 bytes\n"); - return 1; - } - chunksize = cs; - } - - if (!DecodeJpegXlProgressive(jxl.data(), jxl.size(), png_filename, - chunksize)) { - fprintf(stderr, "Error while decoding the jxl file\n"); - return 1; - } - return 0; -} diff --git a/third_party/jpeg-xl/examples/encode_oneshot.cc b/third_party/jpeg-xl/examples/encode_oneshot.cc deleted file mode 100644 index 6c45a70411e63..0000000000000 --- a/third_party/jpeg-xl/examples/encode_oneshot.cc +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This example encodes a file containing a floating point image to another -// file containing JPEG XL image with a single frame. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/** - * Reads from .pfm file (Portable FloatMap) - * - * @param filename name of the file to read - * @param pixels vector to fill with loaded pixels as 32-bit floating point with - * 3-channel RGB - * @param xsize set to width of loaded image - * @param ysize set to height of loaded image - */ -bool ReadPFM(const char* filename, std::vector* pixels, uint32_t* xsize, - uint32_t* ysize) { - FILE* file = fopen(filename, "rb"); - if (!file) { - fprintf(stderr, "Could not open %s for reading.\n", filename); - return false; - } - uint32_t endian_test = 1; - uint8_t little_endian_check[4]; - memcpy(little_endian_check, &endian_test, 4); - bool little_endian = (little_endian_check[0] == 1); - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return false; - } - - long size = ftell(file); // NOLINT - // Avoid invalid file or directory. - if (size >= LONG_MAX || size < 0) { - fclose(file); - return false; - } - - if (fseek(file, 0, SEEK_SET) != 0) { - fclose(file); - return false; - } - - std::vector data; - data.resize(size); - - size_t readsize = fread(data.data(), 1, size, file); - if (static_cast(readsize) != size) { // NOLINT - fclose(file); - return false; - } - if (fclose(file) != 0) { - return false; - } - - std::stringstream datastream; - std::string datastream_content(data.data(), data.size()); - datastream.str(datastream_content); - - std::string pf_token; - getline(datastream, pf_token, '\n'); - if (pf_token != "PF") { - fprintf(stderr, - "%s doesn't seem to be a 3 channel Portable FloatMap file (missing " - "'PF\\n' " - "bytes).\n", - filename); - return false; - } - - std::string xsize_token; - getline(datastream, xsize_token, ' '); - *xsize = std::stoi(xsize_token); - - std::string ysize_token; - getline(datastream, ysize_token, '\n'); - *ysize = std::stoi(ysize_token); - - std::string endianness_token; - getline(datastream, endianness_token, '\n'); - bool input_little_endian; - if (endianness_token == "1.0") { - input_little_endian = false; - } else if (endianness_token == "-1.0") { - input_little_endian = true; - } else { - fprintf(stderr, - "%s doesn't seem to be a Portable FloatMap file (endianness token " - "isn't '1.0' or '-1.0').\n", - filename); - return false; - } - - size_t offset = pf_token.size() + 1 + xsize_token.size() + 1 + - ysize_token.size() + 1 + endianness_token.size() + 1; - - if (data.size() != *ysize * *xsize * 3 * 4 + offset) { - fprintf(stderr, - "%s doesn't seem to be a Portable FloatMap file (pixel data bytes " - "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n", - filename, static_cast(data.size()), static_cast(*ysize), - static_cast(*xsize), static_cast(offset), - static_cast(*ysize * *xsize * 3 * 4 + offset)); - return false; - } - - if (little_endian != input_little_endian) { - fprintf(stderr, - "%s has a different endianness than we do, conversion is not " - "supported.\n", - filename); - return false; - } - - pixels->resize(*ysize * *xsize * 3); - - for (int y = *ysize - 1; y >= 0; y--) { - for (int x = 0; x < static_cast(*xsize); x++) { - for (int c = 0; c < 3; c++) { - memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset, - sizeof(float)); - offset += sizeof(float); - } - } - } - - return true; -} - -/** - * Compresses the provided pixels. - * - * @param pixels input pixels - * @param xsize width of the input image - * @param ysize height of the input image - * @param compressed will be populated with the compressed bytes - */ -bool EncodeJxlOneshot(const std::vector& pixels, const uint32_t xsize, - const uint32_t ysize, std::vector* compressed) { - auto enc = JxlEncoderMake(/*memory_manager=*/nullptr); - auto runner = JxlThreadParallelRunnerMake( - /*memory_manager=*/nullptr, - JxlThreadParallelRunnerDefaultNumWorkerThreads()); - if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), - JxlThreadParallelRunner, - runner.get())) { - fprintf(stderr, "JxlEncoderSetParallelRunner failed\n"); - return false; - } - - JxlPixelFormat pixel_format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; - - JxlBasicInfo basic_info; - JxlEncoderInitBasicInfo(&basic_info); - basic_info.xsize = xsize; - basic_info.ysize = ysize; - basic_info.bits_per_sample = 32; - basic_info.exponent_bits_per_sample = 8; - basic_info.uses_original_profile = JXL_FALSE; - if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) { - fprintf(stderr, "JxlEncoderSetBasicInfo failed\n"); - return false; - } - - JxlColorEncoding color_encoding = {}; - JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); - JxlColorEncodingSetToSRGB(&color_encoding, is_gray); - if (JXL_ENC_SUCCESS != - JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { - fprintf(stderr, "JxlEncoderSetColorEncoding failed\n"); - return false; - } - - JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), nullptr); - - if (JXL_ENC_SUCCESS != - JxlEncoderAddImageFrame(frame_settings, &pixel_format, - static_cast(pixels.data()), - sizeof(float) * pixels.size())) { - fprintf(stderr, "JxlEncoderAddImageFrame failed\n"); - return false; - } - JxlEncoderCloseInput(enc.get()); - - compressed->resize(64); - uint8_t* next_out = compressed->data(); - size_t avail_out = compressed->size() - (next_out - compressed->data()); - JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT; - while (process_result == JXL_ENC_NEED_MORE_OUTPUT) { - process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out); - if (process_result == JXL_ENC_NEED_MORE_OUTPUT) { - size_t offset = next_out - compressed->data(); - compressed->resize(compressed->size() * 2); - next_out = compressed->data() + offset; - avail_out = compressed->size() - offset; - } - } - compressed->resize(next_out - compressed->data()); - if (JXL_ENC_SUCCESS != process_result) { - fprintf(stderr, "JxlEncoderProcessOutput failed\n"); - return false; - } - - return true; -} - -/** - * Writes bytes to file. - */ -bool WriteFile(const std::vector& bytes, const char* filename) { - FILE* file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Could not open %s for writing\n", filename); - return false; - } - if (fwrite(bytes.data(), sizeof(uint8_t), bytes.size(), file) != - bytes.size()) { - fprintf(stderr, "Could not write bytes to %s\n", filename); - fclose(file); - return false; - } - if (fclose(file) != 0) { - fprintf(stderr, "Could not close %s\n", filename); - return false; - } - return true; -} - -int main(int argc, char* argv[]) { - if (argc != 3) { - fprintf(stderr, - "Usage: %s \n" - "Where:\n" - " pfm = input Portable FloatMap image filename\n" - " jxl = output JPEG XL image filename\n" - "Output files will be overwritten.\n", - argv[0]); - return 1; - } - - const char* pfm_filename = argv[1]; - const char* jxl_filename = argv[2]; - - std::vector pixels; - uint32_t xsize; - uint32_t ysize; - if (!ReadPFM(pfm_filename, &pixels, &xsize, &ysize)) { - fprintf(stderr, "Couldn't load %s\n", pfm_filename); - return 2; - } - - std::vector compressed; - if (!EncodeJxlOneshot(pixels, xsize, ysize, &compressed)) { - fprintf(stderr, "Couldn't encode jxl\n"); - return 3; - } - - if (!WriteFile(compressed, jxl_filename)) { - fprintf(stderr, "Couldn't write jxl file\n"); - return 4; - } - - return 0; -} diff --git a/third_party/jpeg-xl/examples/examples.cmake b/third_party/jpeg-xl/examples/examples.cmake deleted file mode 100644 index b13731ca3c1eb..0000000000000 --- a/third_party/jpeg-xl/examples/examples.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -add_executable(decode_exif_metadata ${CMAKE_CURRENT_LIST_DIR}/decode_exif_metadata.cc) -target_link_libraries(decode_exif_metadata jxl_dec jxl_threads) -add_executable(decode_oneshot ${CMAKE_CURRENT_LIST_DIR}/decode_oneshot.cc) -target_link_libraries(decode_oneshot jxl_dec jxl_threads) -add_executable(decode_progressive ${CMAKE_CURRENT_LIST_DIR}/decode_progressive.cc) -target_link_libraries(decode_progressive jxl_dec jxl_threads) -add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc) -target_link_libraries(encode_oneshot jxl jxl_threads) diff --git a/third_party/jpeg-xl/flake.lock b/third_party/jpeg-xl/flake.lock deleted file mode 100644 index 3bfd004b14863..0000000000000 --- a/third_party/jpeg-xl/flake.lock +++ /dev/null @@ -1,61 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1702312524, - "narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "a9bf124c46ef298113270b1f84a164865987a91c", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/third_party/jpeg-xl/flake.nix b/third_party/jpeg-xl/flake.nix deleted file mode 100644 index 64a36311ab13b..0000000000000 --- a/third_party/jpeg-xl/flake.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem - (system: - let - pkgs = import nixpkgs { - inherit system; - }; - in - with pkgs; - { - devShells.default = mkShell { - buildInputs = [ - clang - cmake - pkg-config - gtest - doxygen - graphviz - python3 - libclang.python - libpng - giflib - lcms2 - brotli - ]; - shellHook = '' - export CC=clang - export CXX=clang++ - ''; - }; - } - ); -} diff --git a/third_party/jpeg-xl/lib/BUILD b/third_party/jpeg-xl/lib/BUILD deleted file mode 100644 index 3d48919f7775e..0000000000000 --- a/third_party/jpeg-xl/lib/BUILD +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -load("@bazel_skylib//rules:copy_file.bzl", "copy_file") -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") - -# Load sources/headers/tests lists. -load( - "jxl_lists.bzl", - "libjxl_base_sources", - "libjxl_cms_sources", - "libjxl_codec_apng_sources", - "libjxl_codec_exr_sources", - "libjxl_codec_gif_sources", - "libjxl_codec_jpegli_sources", - "libjxl_codec_jpg_sources", - "libjxl_codec_jxl_sources", - "libjxl_codec_npy_sources", - "libjxl_codec_pgx_sources", - "libjxl_codec_pnm_sources", - "libjxl_dec_box_sources", - "libjxl_dec_jpeg_sources", - "libjxl_dec_sources", - "libjxl_enc_sources", - "libjxl_extras_for_tools_sources", - "libjxl_extras_sources", - # "libjxl_gbench_sources", - # "libjxl_jpegli_lib_version", - "libjxl_jpegli_libjpeg_helper_files", - "libjxl_jpegli_sources", - "libjxl_jpegli_testlib_files", - "libjxl_jpegli_tests", - "libjxl_major_version", - "libjxl_minor_version", - "libjxl_patch_version", - "libjxl_public_headers", - "libjxl_testlib_files", - "libjxl_tests", - "libjxl_threads_public_headers", - "libjxl_threads_sources", -) -load( - "jxl_vars.bzl", - "libjxl_deps_brotli", - "libjxl_deps_exr", - "libjxl_deps_gif", - "libjxl_deps_gtest", - "libjxl_deps_hwy", - "libjxl_deps_hwy_nanobenchmark", - "libjxl_deps_hwy_test_util", - "libjxl_deps_jpeg", - "libjxl_deps_png", - "libjxl_deps_runfiles", - "libjxl_deps_skcms", - # "libjxl_deps_testdata", - # "libjxl_deps_webp", - "libjxl_root_package", - "libjxl_test_shards", - "libjxl_test_timeouts", -) - -DEFAULT_VISIBILITY = ["//:__subpackages__"] - -DEFAULT_COMPATIBILITY = [] - -INCLUDES_DIR = "include" - -package( - default_visibility = DEFAULT_VISIBILITY, -) - -licenses(["notice"]) - -exports_files(["LICENSE"]) - -EXPORT_TEMPLATE = """ -#ifndef @_EXPORT_H -#define @_EXPORT_H - -#define @_EXPORT -#define @_NO_EXPORT - -#ifndef @_DEPRECATED -# define @_DEPRECATED __attribute__ ((__deprecated__)) -#endif - -#endif -""" - -JXL_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_export.h" - -genrule( - name = "create_jxl_export", - outs = [JXL_EXPORT_H], - cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL") + "' > $@", - compatible_with = DEFAULT_COMPATIBILITY, -) - -JXL_CMS_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_cms_export.h" - -genrule( - name = "create_jxl_cms_export", - outs = [JXL_CMS_EXPORT_H], - cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL_CMS") + "' > $@", - compatible_with = DEFAULT_COMPATIBILITY, -) - -JXL_THREADS_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_threads_export.h" - -genrule( - name = "create_jxl_threads_export", - outs = [JXL_THREADS_EXPORT_H], - cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL_THREADS") + "' > $@", - compatible_with = DEFAULT_COMPATIBILITY, -) - -JXL_VERSION_H = INCLUDES_DIR + "/jxl/version.h" - -expand_template( - name = "expand_jxl_version", - out = JXL_VERSION_H, - compatible_with = DEFAULT_COMPATIBILITY, - substitutions = { - "@JPEGXL_MAJOR_VERSION@": str(libjxl_major_version), - "@JPEGXL_MINOR_VERSION@": str(libjxl_minor_version), - "@JPEGXL_PATCH_VERSION@": str(libjxl_patch_version), - }, - template = "jxl/version.h.in", -) - -cc_library( - name = "jxl_version", - hdrs = [JXL_VERSION_H], - compatible_with = DEFAULT_COMPATIBILITY, - strip_include_prefix = INCLUDES_DIR, -) - -JPEGLI_JCONFIG_H = INCLUDES_DIR + "/jpegli/jconfig.h" - -JPEGLI_JMORECFG_H = INCLUDES_DIR + "/jpegli/jmorecfg.h" - -JPEGLI_JPEGLIB_H = INCLUDES_DIR + "/jpegli/jpeglib.h" - -copy_file( - name = "expand_jconfig", - src = "@libjpeg_turbo//:jconfig.h", - out = JPEGLI_JCONFIG_H, - compatible_with = DEFAULT_COMPATIBILITY, -) - -copy_file( - name = "copy_jmorecfg", - src = "@libjpeg_turbo//:jmorecfg.h", - out = JPEGLI_JMORECFG_H, - compatible_with = DEFAULT_COMPATIBILITY, -) - -copy_file( - name = "copy_jpeglib", - src = "@libjpeg_turbo//:jpeglib.h", - out = JPEGLI_JPEGLIB_H, - compatible_with = DEFAULT_COMPATIBILITY, -) - -cc_library( - name = "includes", - hdrs = libjxl_public_headers + [ - JXL_EXPORT_H, - JXL_CMS_EXPORT_H, - ], - compatible_with = DEFAULT_COMPATIBILITY, - strip_include_prefix = INCLUDES_DIR, - deps = [":jxl_version"], -) - -cc_library( - name = "libjpeg_includes", - hdrs = [ - JPEGLI_JCONFIG_H, - JPEGLI_JMORECFG_H, - JPEGLI_JPEGLIB_H, - ], - compatible_with = DEFAULT_COMPATIBILITY, - strip_include_prefix = INCLUDES_DIR + "/jpegli", -) - -cc_library( - name = "base", - srcs = [path for path in libjxl_base_sources if path.endswith(".cc")], - hdrs = [path for path in libjxl_base_sources if path.endswith(".h")], - compatible_with = DEFAULT_COMPATIBILITY, - deps = [ - ":includes", - ] + libjxl_deps_hwy, -) - -cc_library( - name = "jpegxl", - srcs = libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources + libjxl_cms_sources, - compatible_with = DEFAULT_COMPATIBILITY, - defines = [ - "JPEGXL_ENABLE_SKCMS=1", - ], - deps = [ - ":base", - ":includes", - ] + libjxl_deps_brotli + libjxl_deps_hwy + libjxl_deps_skcms, -) - -cc_library( - name = "jpegxl_private", - hdrs = [ - path - for path in libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources + libjxl_cms_sources - if path.endswith(".h") and not path.endswith("-inl.h") - ], - compatible_with = DEFAULT_COMPATIBILITY, - deps = [":jpegxl"], -) - -cc_library( - name = "jpegxl_threads", - srcs = libjxl_threads_sources, - hdrs = libjxl_threads_public_headers + [JXL_THREADS_EXPORT_H], - compatible_with = DEFAULT_COMPATIBILITY, - strip_include_prefix = INCLUDES_DIR, - deps = [ - ":base", - ":includes", - ], -) - -CODEC_FILES = libjxl_codec_apng_sources + libjxl_codec_exr_sources + libjxl_codec_gif_sources + libjxl_codec_jpegli_sources + libjxl_codec_jpg_sources + libjxl_codec_jxl_sources + libjxl_codec_npy_sources + libjxl_codec_pgx_sources + libjxl_codec_pnm_sources - -CODEC_SRCS = [path for path in CODEC_FILES if path.endswith(".cc")] - -CODEC_HDRS = [path for path in CODEC_FILES if path.endswith(".h")] - -cc_library( - name = "jpegli", - srcs = libjxl_jpegli_sources, - hdrs = [ - "jpegli/common_internal.h", # TODO(eustas): should not be here - ], - compatible_with = DEFAULT_COMPATIBILITY, - deps = [ - ":jpegxl_private", - ":libjpeg_includes", - ] + libjxl_deps_hwy, -) - -# TODO(eustas): build codecs separately? -cc_library( - name = "jpegxl_extras", - srcs = libjxl_extras_sources + libjxl_extras_for_tools_sources + CODEC_SRCS, - hdrs = CODEC_HDRS, - compatible_with = DEFAULT_COMPATIBILITY, - defines = [ - "JPEGXL_ENABLE_APNG=1", - "JPEGXL_ENABLE_EXR=1", - "JPEGXL_ENABLE_GIF=1", - "JPEGXL_ENABLE_JPEG=1", - "JPEGXL_ENABLE_JPEGLI=1", - ], - deps = [ - ":jpegli", - ":jpegxl_private", - ":jpegxl_threads", - ":jxl_version", - ] + libjxl_deps_exr + libjxl_deps_gif + libjxl_deps_jpeg + libjxl_deps_png, -) - -TESTLIB_FILES = libjxl_testlib_files + libjxl_jpegli_testlib_files + libjxl_jpegli_libjpeg_helper_files - -cc_library( - name = "test_utils", - testonly = 1, - srcs = [path for path in TESTLIB_FILES if not path.endswith(".h")], - hdrs = [path for path in TESTLIB_FILES if path.endswith(".h")], - compatible_with = DEFAULT_COMPATIBILITY, - defines = [ - 'JPEGXL_ROOT_PACKAGE=\'"' + libjxl_root_package + '"\'', - ], - deps = [ - ":jpegli", - ":jpegxl_extras", - ":jpegxl_private", - ] + libjxl_deps_runfiles, -) - -TESTS = [path.partition(".")[0] for path in libjxl_tests + libjxl_jpegli_tests] - -[ - cc_test( - name = test, - timeout = libjxl_test_timeouts.get(test, "moderate"), - srcs = [ - test + ".cc", - "jpegli/testing.h", - "jxl/testing.h", - ], - data = ["//:testdata"], - shard_count = libjxl_test_shards.get(test, 1), - deps = [ - ":jpegxl_extras", - ":jpegxl_private", - ":jpegxl_threads", - ":test_utils", - ] + libjxl_deps_gtest + libjxl_deps_hwy_test_util + libjxl_deps_hwy_nanobenchmark, - ) - for test in TESTS -] diff --git a/third_party/jpeg-xl/lib/CMakeLists.txt b/third_party/jpeg-xl/lib/CMakeLists.txt deleted file mode 100644 index 8973ea6ece922..0000000000000 --- a/third_party/jpeg-xl/lib/CMakeLists.txt +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -set(JPEGXL_MAJOR_VERSION 0) -set(JPEGXL_MINOR_VERSION 11) -set(JPEGXL_PATCH_VERSION 1) -set(JPEGXL_LIBRARY_VERSION - "${JPEGXL_MAJOR_VERSION}.${JPEGXL_MINOR_VERSION}.${JPEGXL_PATCH_VERSION}") - -# This is the library API/ABI compatibility version. Changing this value makes -# the shared library incompatible with previous version. A program linked -# against this shared library SOVERSION will not run with an older SOVERSION. -# It is important to update this value when making incompatible API/ABI changes -# so that programs that depend on libjxl can update their dependencies. Semantic -# versioning allows 0.y.z to have incompatible changes in minor versions. -set(JPEGXL_SO_MINOR_VERSION 11) -if (JPEGXL_MAJOR_VERSION EQUAL 0) - set(JPEGXL_LIBRARY_SOVERSION - "${JPEGXL_MAJOR_VERSION}.${JPEGXL_SO_MINOR_VERSION}") -else() - set(JPEGXL_LIBRARY_SOVERSION "${JPEGXL_MAJOR_VERSION}") -endif() - - -# List of warning and feature flags for our library and tests. -if (MSVC) - set(JPEGXL_INTERNAL_FLAGS - # TODO(janwas): add flags - ) -else () - set(JPEGXL_INTERNAL_FLAGS - # F_FLAGS - -fmerge-all-constants - -fno-builtin-fwrite - -fno-builtin-fread - - # WARN_FLAGS - -Wall - -Wextra - -Wc++11-compat - -Warray-bounds - -Wformat-security - -Wimplicit-fallthrough - -Wno-register # Needed by public headers in lcms - -Wno-unused-function - -Wno-unused-parameter - -Wnon-virtual-dtor - -Woverloaded-virtual - -Wvla - ) - - # Warning flags supported by clang. - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - list(APPEND JPEGXL_INTERNAL_FLAGS - -Wdeprecated-increment-bool - # TODO(deymo): Add -Wextra-semi once we update third_party/highway. - # -Wextra-semi - -Wfloat-overflow-conversion - -Wfloat-zero-conversion - -Wfor-loop-analysis - -Wgnu-redeclared-enum - -Winfinite-recursion - -Wliteral-conversion - -Wno-c++98-compat - -Wno-unused-command-line-argument - -Wprivate-header - -Wself-assign - -Wstring-conversion - -Wtautological-overlap-compare - -Wthread-safety-analysis - -Wundefined-func-template - -Wunreachable-code - -Wunused-comparison - ) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0) - list(APPEND HWY_FLAGS -Wc++2a-extensions) - endif() - endif() # Clang - - if (WIN32) - list(APPEND JPEGXL_INTERNAL_FLAGS - -Wno-cast-align - -Wno-double-promotion - -Wno-float-equal - -Wno-format-nonliteral - -Wno-shadow - -Wno-sign-conversion - -Wno-zero-as-null-pointer-constant - ) - - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - list(APPEND JPEGXL_INTERNAL_FLAGS - -Wno-used-but-marked-unused - -Wno-unused-template - -Wno-unused-member-function - -Wno-shadow-field-in-constructor - -Wno-language-extension-token - -Wno-global-constructors - -Wno-c++98-compat-pedantic - ) - endif() # Clang - else() # WIN32 - list(APPEND JPEGXL_INTERNAL_FLAGS - -fsized-deallocation - -fno-exceptions - - # Language flags - -fmath-errno - ) - - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - list(APPEND JPEGXL_INTERNAL_FLAGS - -fnew-alignment=8 - -fno-cxx-exceptions - -fno-slp-vectorize - -fno-vectorize - - -disable-free - -disable-llvm-verifier - ) - endif() # Clang - endif() # WIN32 -endif() #!MSVC - -if (JPEGXL_ENABLE_SKCMS) - list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_SKCMS=1) -endif () - -# strips the -internal suffix from all the elements in LIST -function(strip_internal OUTPUT_VAR LIB_LIST) - foreach(lib IN LISTS ${LIB_LIST}) - string(REGEX REPLACE "-internal$" "" lib "${lib}") - list(APPEND out_list "${lib}") - endforeach() - set(${OUTPUT_VAR} ${out_list} PARENT_SCOPE) -endfunction() - -# set variables for jxl_cms.cmake and jxl.cmake -if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") - set(PKGCONFIG_TARGET_INCLUDES "${CMAKE_INSTALL_INCLUDEDIR}") -else() - set(PKGCONFIG_TARGET_INCLUDES "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") -endif() -if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") - set(PKGCONFIG_TARGET_LIBS "${CMAKE_INSTALL_LIBDIR}") -else() - set(PKGCONFIG_TARGET_LIBS "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") -endif() - -include(CheckCXXSymbolExists) -set(PKGCONFIG_CXX_LIB "") -check_cxx_symbol_exists(__GLIBCXX__ iostream LIBSTDCXX) -check_cxx_symbol_exists(_LIBCPP_VERSION iostream LIBCXX) -if(LIBSTDCXX) - set(PKGCONFIG_CXX_LIB "-lstdc++") -elseif(LIBCXX) - set(PKGCONFIG_CXX_LIB "-lc++") -endif() - -# The jxl_cms library definition. -include(jxl_cms.cmake) -# The jxl library definition. -include(jxl.cmake) - -# Other libraries outside the core jxl library. -if(JPEGXL_ENABLE_TOOLS OR BUILD_TESTING) - include(jxl_extras.cmake) -endif() -include(jxl_threads.cmake) -if (JPEGXL_ENABLE_JPEGLI) - include(jpegli.cmake) -endif() - -# For simplicity all the library headers, both source and generated ones, are -# gathered in the binary folder. There is no distinction on which libraries use -# which header since it is expected that all developer libraries are available -# together at build time. -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/jxl - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -if(BUILD_TESTING) - include(GoogleTest) -endif() - -# Tests for the jxl library. -include(jxl_tests.cmake) - -if(BUILD_TESTING) - # Google benchmark for the jxl library - include(jxl_benchmark.cmake) -endif() diff --git a/third_party/jpeg-xl/lib/extras/LICENSE.apngdis b/third_party/jpeg-xl/lib/extras/LICENSE.apngdis deleted file mode 100644 index eb0ba7c07b180..0000000000000 --- a/third_party/jpeg-xl/lib/extras/LICENSE.apngdis +++ /dev/null @@ -1,27 +0,0 @@ -APNG Disassembler 2.8 - -Deconstructs APNG files into individual frames. - -http://apngdis.sourceforge.net - -Copyright (c) 2010-2015 Max Stepin -maxst at users.sourceforge.net - -zlib license ------------- - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. diff --git a/third_party/jpeg-xl/lib/extras/README.md b/third_party/jpeg-xl/lib/extras/README.md deleted file mode 100644 index 06a9b5ea0782b..0000000000000 --- a/third_party/jpeg-xl/lib/extras/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## JPEG XL "extras" - -The files in this directory do not form part of the library or codec and are -only used by tests or specific internal tools that have access to the internals -of the library. diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.cc b/third_party/jpeg-xl/lib/extras/alpha_blend.cc deleted file mode 100644 index 8e34d56cecbb9..0000000000000 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/alpha_blend.h" - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -namespace { - -Status AlphaBlend(PackedFrame* frame, const float background[3]) { - if (!frame) return true; - const PackedImage& im = frame->color; - JxlPixelFormat format = im.format; - if (format.num_channels != 2 && format.num_channels != 4) { - return true; - } - --format.num_channels; - JXL_ASSIGN_OR_RETURN(PackedImage blended, - PackedImage::Create(im.xsize, im.ysize, format)); - // TODO(szabadka) SIMDify this and make it work for float16. - for (size_t y = 0; y < im.ysize; ++y) { - for (size_t x = 0; x < im.xsize; ++x) { - if (format.num_channels == 2) { - float g = im.GetPixelValue(y, x, 0); - float a = im.GetPixelValue(y, x, 1); - float out = g * a + background[0] * (1 - a); - blended.SetPixelValue(y, x, 0, out); - } else { - float r = im.GetPixelValue(y, x, 0); - float g = im.GetPixelValue(y, x, 1); - float b = im.GetPixelValue(y, x, 2); - float a = im.GetPixelValue(y, x, 3); - float out_r = r * a + background[0] * (1 - a); - float out_g = g * a + background[1] * (1 - a); - float out_b = b * a + background[2] * (1 - a); - blended.SetPixelValue(y, x, 0, out_r); - blended.SetPixelValue(y, x, 1, out_g); - blended.SetPixelValue(y, x, 2, out_b); - } - } - } - frame->color = blended.Copy(); - return true; -} - -} // namespace - -Status AlphaBlend(PackedPixelFile* ppf, const float background[3]) { - if (!ppf || ppf->info.alpha_bits == 0) { - return true; - } - ppf->info.alpha_bits = 0; - JXL_RETURN_IF_ERROR(AlphaBlend(ppf->preview_frame.get(), background)); - for (auto& frame : ppf->frames) { - JXL_RETURN_IF_ERROR(AlphaBlend(&frame, background)); - } - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.h b/third_party/jpeg-xl/lib/extras/alpha_blend.h deleted file mode 100644 index b388a5e22aa48..0000000000000 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ALPHA_BLEND_H_ -#define LIB_EXTRAS_ALPHA_BLEND_H_ - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -Status AlphaBlend(PackedPixelFile* ppf, const float background[3]); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ALPHA_BLEND_H_ diff --git a/third_party/jpeg-xl/lib/extras/codec.cc b/third_party/jpeg-xl/lib/extras/codec.cc deleted file mode 100644 index ba4bcfebbc475..0000000000000 --- a/third_party/jpeg-xl/lib/extras/codec.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/codec.h" - -#include -#include - -#include "lib/extras/dec/decode.h" -#include "lib/extras/enc/apng.h" -#include "lib/extras/enc/exr.h" -#include "lib/extras/enc/jpg.h" -#include "lib/extras/enc/pgx.h" -#include "lib/extras/enc/pnm.h" -#include "lib/extras/packed_image.h" -#include "lib/extras/packed_image_convert.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace { - -// Any valid encoding is larger (ensures codecs can read the first few bytes) -constexpr size_t kMinBytes = 9; - -} // namespace - -Status SetFromBytes(const Span bytes, - const extras::ColorHints& color_hints, CodecInOut* io, - ThreadPool* pool, const SizeConstraints* constraints, - extras::Codec* orig_codec) { - if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes"); - - extras::PackedPixelFile ppf; - if (extras::DecodeBytes(bytes, color_hints, &ppf, constraints, orig_codec)) { - return ConvertPackedPixelFileToCodecInOut(ppf, pool, io); - } - return JXL_FAILURE("Codecs failed to decode"); -} - -Status Encode(const extras::PackedPixelFile& ppf, const extras::Codec codec, - std::vector* bytes, ThreadPool* pool) { - bytes->clear(); - std::unique_ptr encoder; - switch (codec) { - case extras::Codec::kPNG: - encoder = extras::GetAPNGEncoder(); - if (encoder) { - break; - } else { - return JXL_FAILURE("JPEG XL was built without (A)PNG support"); - } - case extras::Codec::kJPG: - encoder = extras::GetJPEGEncoder(); - if (encoder) { - break; - } else { - return JXL_FAILURE("JPEG XL was built without JPEG support"); - } - case extras::Codec::kPNM: - if (ppf.info.alpha_bits > 0) { - encoder = extras::GetPAMEncoder(); - } else if (ppf.info.num_color_channels == 1) { - encoder = extras::GetPGMEncoder(); - } else if (ppf.info.bits_per_sample <= 16) { - encoder = extras::GetPPMEncoder(); - } else { - encoder = extras::GetPFMEncoder(); - } - break; - case extras::Codec::kPGX: - encoder = extras::GetPGXEncoder(); - break; - case extras::Codec::kGIF: - return JXL_FAILURE("Encoding to GIF is not implemented"); - case extras::Codec::kEXR: - encoder = extras::GetEXREncoder(); - if (encoder) { - break; - } else { - return JXL_FAILURE("JPEG XL was built without OpenEXR support"); - } - case extras::Codec::kJXL: - // TODO(user): implement - return JXL_FAILURE("Codec::kJXL is not supported yet"); - - case extras::Codec::kUnknown: - return JXL_FAILURE("Cannot encode using Codec::kUnknown"); - } - - if (!encoder) { - return JXL_FAILURE("Invalid codec."); - } - extras::EncodedImage encoded_image; - JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded_image, pool)); - JXL_ENSURE(encoded_image.bitstreams.size() == 1); - *bytes = encoded_image.bitstreams[0]; - - return true; -} - -Status Encode(const extras::PackedPixelFile& ppf, const std::string& pathname, - std::vector* bytes, ThreadPool* pool) { - std::string extension; - const extras::Codec codec = - extras::CodecFromPath(pathname, nullptr, &extension); - return Encode(ppf, codec, bytes, pool); -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/codec.h b/third_party/jpeg-xl/lib/extras/codec.h deleted file mode 100644 index 9fd61503cb458..0000000000000 --- a/third_party/jpeg-xl/lib/extras/codec.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_CODEC_H_ -#define LIB_EXTRAS_CODEC_H_ - -// Facade for image encoders/decoders (PNG, PNM, ...). - -#include -#include - -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/dec/decode.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/codec_in_out.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/field_encodings.h" // MakeBit - -namespace jxl { - -struct SizeConstraints; - -// Decodes "bytes" and sets io->metadata.m. -// color_space_hint may specify the color space, otherwise, defaults to sRGB. -Status SetFromBytes(Span bytes, - const extras::ColorHints& color_hints, CodecInOut* io, - ThreadPool* pool = nullptr, - const SizeConstraints* constraints = nullptr, - extras::Codec* orig_codec = nullptr); -// Helper function to use no color_space_hint. -JXL_INLINE Status SetFromBytes(const Span bytes, CodecInOut* io, - ThreadPool* pool = nullptr, - const SizeConstraints* constraints = nullptr, - extras::Codec* orig_codec = nullptr) { - return SetFromBytes(bytes, extras::ColorHints(), io, pool, constraints, - orig_codec); -} - -Status Encode(const extras::PackedPixelFile& ppf, extras::Codec codec, - std::vector* bytes, ThreadPool* pool); - -Status Encode(const extras::PackedPixelFile& ppf, const std::string& pathname, - std::vector* bytes, ThreadPool* pool = nullptr); - -} // namespace jxl - -#endif // LIB_EXTRAS_CODEC_H_ diff --git a/third_party/jpeg-xl/lib/extras/codec_test.cc b/third_party/jpeg-xl/lib/extras/codec_test.cc deleted file mode 100644 index 6cbed220975ef..0000000000000 --- a/third_party/jpeg-xl/lib/extras/codec_test.cc +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/extras/common.h" -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/dec/decode.h" -#include "lib/extras/enc/encode.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/random.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { - -using ::jxl::test::ThreadPoolForTests; - -namespace extras { - -Status PnmParseSigned(Bytes str, double* v); -Status PnmParseUnsigned(Bytes str, size_t* v); - -namespace { - -Span MakeSpan(const char* str) { - return Bytes(reinterpret_cast(str), strlen(str)); -} - -std::string ExtensionFromCodec(Codec codec, const bool is_gray, - const bool has_alpha, - const size_t bits_per_sample) { - switch (codec) { - case Codec::kJPG: - return ".jpg"; - case Codec::kPGX: - return ".pgx"; - case Codec::kPNG: - return ".png"; - case Codec::kPNM: - if (bits_per_sample == 32) return ".pfm"; - if (has_alpha) return ".pam"; - return is_gray ? ".pgm" : ".ppm"; - case Codec::kEXR: - return ".exr"; - default: - return std::string(); - } -} - -void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0, - const PackedImage& im1, size_t bits_per_sample1, - bool lossless = true) { - ASSERT_EQ(im0.xsize, im1.xsize); - ASSERT_EQ(im0.ysize, im1.ysize); - ASSERT_EQ(im0.format.num_channels, im1.format.num_channels); - auto get_factor = [](JxlPixelFormat f, size_t bits) -> double { - return 1.0 / ((1u << std::min(test::GetPrecision(f.data_type), bits)) - 1); - }; - double factor0 = get_factor(im0.format, bits_per_sample0); - double factor1 = get_factor(im1.format, bits_per_sample1); - const auto* pixels0 = static_cast(im0.pixels()); - const auto* pixels1 = static_cast(im1.pixels()); - auto rgba0 = - test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0); - auto rgba1 = - test::ConvertToRGBA32(pixels1, im1.xsize, im1.ysize, im1.format, factor1); - double tolerance = - lossless ? 0.5 * std::min(factor0, factor1) : 3.0f / 255.0f; - if (bits_per_sample0 == 32 || bits_per_sample1 == 32) { - tolerance = 0.5 * std::max(factor0, factor1); - } - for (size_t y = 0; y < im0.ysize; ++y) { - for (size_t x = 0; x < im0.xsize; ++x) { - for (size_t c = 0; c < im0.format.num_channels; ++c) { - size_t ix = (y * im0.xsize + x) * 4 + c; - double val0 = rgba0[ix]; - double val1 = rgba1[ix]; - ASSERT_NEAR(val1, val0, tolerance) - << "y = " << y << " x = " << x << " c = " << c; - } - } - } -} - -JxlColorEncoding CreateTestColorEncoding(bool is_gray) { - JxlColorEncoding c; - c.color_space = is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB; - c.white_point = JXL_WHITE_POINT_D65; - c.primaries = JXL_PRIMARIES_P3; - c.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - c.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; - // Roundtrip through internal color encoding to fill in primaries and white - // point CIE xy coordinates. - ColorEncoding c_internal; - EXPECT_TRUE(c_internal.FromExternal(c)); - c = c_internal.ToExternal(); - return c; -} - -std::vector GenerateICC(JxlColorEncoding color_encoding) { - ColorEncoding c; - EXPECT_TRUE(c.FromExternal(color_encoding)); - EXPECT_TRUE(!c.ICC().empty()); - return c.ICC(); -} - -void StoreRandomValue(uint8_t* out, Rng* rng, JxlPixelFormat format, - size_t bits_per_sample) { - uint64_t max_val = (1ull << bits_per_sample) - 1; - if (format.data_type == JXL_TYPE_UINT8) { - *out = rng->UniformU(0, max_val); - } else if (format.data_type == JXL_TYPE_UINT16) { - uint32_t val = rng->UniformU(0, max_val); - if (format.endianness == JXL_BIG_ENDIAN) { - StoreBE16(val, out); - } else { - StoreLE16(val, out); - } - } else { - ASSERT_EQ(format.data_type, JXL_TYPE_FLOAT); - float val = rng->UniformF(0.0, 1.0); - uint32_t uval; - memcpy(&uval, &val, 4); - if (format.endianness == JXL_BIG_ENDIAN) { - StoreBE32(uval, out); - } else { - StoreLE32(uval, out); - } - } -} - -void FillPackedImage(size_t bits_per_sample, PackedImage* image) { - JxlPixelFormat format = image->format; - size_t bytes_per_channel = PackedImage::BitsPerChannel(format.data_type) / 8; - uint8_t* out = static_cast(image->pixels()); - size_t stride = image->xsize * format.num_channels * bytes_per_channel; - ASSERT_EQ(image->pixels_size, image->ysize * stride); - Rng rng(129); - for (size_t y = 0; y < image->ysize; ++y) { - for (size_t x = 0; x < image->xsize; ++x) { - for (size_t c = 0; c < format.num_channels; ++c) { - StoreRandomValue(out, &rng, format, bits_per_sample); - out += bytes_per_channel; - } - } - } -} - -struct TestImageParams { - Codec codec; - size_t xsize; - size_t ysize; - size_t bits_per_sample; - bool is_gray; - bool add_alpha; - bool big_endian; - bool add_extra_channels; - - bool ShouldTestRoundtrip() const { - if (codec == Codec::kPNG) { - return bits_per_sample <= 16; - } else if (codec == Codec::kPNM) { - // TODO(szabadka) Make PNM encoder endianness-aware. - return ((bits_per_sample <= 16 && big_endian) || - (bits_per_sample == 32 && !add_alpha && !big_endian)); - } else if (codec == Codec::kPGX) { - return ((bits_per_sample == 8 || bits_per_sample == 16) && is_gray && - !add_alpha); - } else if (codec == Codec::kEXR) { -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(THREAD_SANITIZER) - // OpenEXR 2.3 has a memory leak in IlmThread_2_3::ThreadPool - return false; -#else - return bits_per_sample == 32 && !is_gray; -#endif - } else if (codec == Codec::kJPG) { - return bits_per_sample == 8 && !add_alpha; - } else { - return false; - } - } - - JxlPixelFormat PixelFormat() const { - JxlPixelFormat format; - format.num_channels = (is_gray ? 1 : 3) + (add_alpha ? 1 : 0); - format.data_type = (bits_per_sample == 32 ? JXL_TYPE_FLOAT - : bits_per_sample > 8 ? JXL_TYPE_UINT16 - : JXL_TYPE_UINT8); - format.endianness = big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN; - format.align = 0; - return format; - } - - std::string DebugString() const { - std::ostringstream os; - os << "bps:" << bits_per_sample << " gr:" << is_gray << " al:" << add_alpha - << " be: " << big_endian << " ec: " << add_extra_channels; - return os.str(); - } -}; - -void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) { - ppf->info.xsize = params.xsize; - ppf->info.ysize = params.ysize; - ppf->info.bits_per_sample = params.bits_per_sample; - ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0; - ppf->info.num_color_channels = params.is_gray ? 1 : 3; - ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0; - ppf->info.alpha_premultiplied = TO_JXL_BOOL(params.codec == Codec::kEXR); - - JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray); - ppf->icc = GenerateICC(color_encoding); - ppf->color_encoding = color_encoding; - - JXL_TEST_ASSIGN_OR_DIE( - PackedFrame frame, - PackedFrame::Create(params.xsize, params.ysize, params.PixelFormat())); - FillPackedImage(params.bits_per_sample, &frame.color); - if (params.add_extra_channels) { - for (size_t i = 0; i < 7; ++i) { - JxlPixelFormat ec_format = params.PixelFormat(); - ec_format.num_channels = 1; - JXL_TEST_ASSIGN_OR_DIE( - PackedImage ec, - PackedImage::Create(params.xsize, params.ysize, ec_format)); - FillPackedImage(params.bits_per_sample, &ec); - frame.extra_channels.emplace_back(std::move(ec)); - PackedExtraChannel pec; - pec.ec_info.bits_per_sample = params.bits_per_sample; - pec.ec_info.type = static_cast(i); - ppf->extra_channels_info.emplace_back(std::move(pec)); - } - } - ppf->frames.emplace_back(std::move(frame)); -} - -// Ensures reading a newly written file leads to the same image pixels. -void TestRoundTrip(const TestImageParams& params, ThreadPool* pool) { - if (!params.ShouldTestRoundtrip()) return; - - std::string extension = ExtensionFromCodec( - params.codec, params.is_gray, params.add_alpha, params.bits_per_sample); - printf("Codec %s %s\n", extension.c_str(), params.DebugString().c_str()); - - PackedPixelFile ppf_in; - CreateTestImage(params, &ppf_in); - - EncodedImage encoded; - auto encoder = Encoder::FromExtension(extension); - if (!encoder) { - fprintf(stderr, "Skipping test because of missing codec support.\n"); - return; - } - ASSERT_TRUE(encoder->Encode(ppf_in, &encoded, pool)); - ASSERT_EQ(encoded.bitstreams.size(), 1); - - PackedPixelFile ppf_out; - ColorHints color_hints; - if (params.codec == Codec::kPNM || params.codec == Codec::kPGX) { - color_hints.Add("color_space", - params.is_gray ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG"); - } - ASSERT_TRUE(DecodeBytes(Bytes(encoded.bitstreams[0]), color_hints, &ppf_out)); - if (params.codec == Codec::kPNG && ppf_out.icc.empty()) { - // Decoding a PNG may drop the ICC profile if there's a valid cICP chunk. - // Rendering intent is not preserved in this case. - EXPECT_EQ(ppf_in.color_encoding.color_space, - ppf_out.color_encoding.color_space); - EXPECT_EQ(ppf_in.color_encoding.white_point, - ppf_out.color_encoding.white_point); - if (ppf_in.color_encoding.color_space != JXL_COLOR_SPACE_GRAY) { - EXPECT_EQ(ppf_in.color_encoding.primaries, - ppf_out.color_encoding.primaries); - } - EXPECT_EQ(ppf_in.color_encoding.transfer_function, - ppf_out.color_encoding.transfer_function); - EXPECT_EQ(ppf_out.color_encoding.rendering_intent, - JXL_RENDERING_INTENT_RELATIVE); - } else if (params.codec != Codec::kPNM && params.codec != Codec::kPGX && - params.codec != Codec::kEXR) { - EXPECT_EQ(ppf_in.icc, ppf_out.icc); - } - - ASSERT_EQ(ppf_out.frames.size(), 1); - const auto& frame_in = ppf_in.frames[0]; - const auto& frame_out = ppf_out.frames[0]; - VerifySameImage(frame_in.color, ppf_in.info.bits_per_sample, frame_out.color, - ppf_out.info.bits_per_sample, - /*lossless=*/params.codec != Codec::kJPG); - ASSERT_EQ(frame_in.extra_channels.size(), frame_out.extra_channels.size()); - ASSERT_EQ(ppf_out.extra_channels_info.size(), - frame_out.extra_channels.size()); - for (size_t i = 0; i < frame_in.extra_channels.size(); ++i) { - VerifySameImage(frame_in.extra_channels[i], ppf_in.info.bits_per_sample, - frame_out.extra_channels[i], ppf_out.info.bits_per_sample, - /*lossless=*/true); - EXPECT_EQ(ppf_out.extra_channels_info[i].ec_info.type, - ppf_in.extra_channels_info[i].ec_info.type); - } -} - -TEST(CodecTest, TestRoundTrip) { - ThreadPoolForTests pool(12); - - TestImageParams params; - params.xsize = 7; - params.ysize = 4; - - for (Codec codec : - {Codec::kPNG, Codec::kPNM, Codec::kPGX, Codec::kEXR, Codec::kJPG}) { - for (int bits_per_sample : {4, 8, 10, 12, 16, 32}) { - for (bool is_gray : {false, true}) { - for (bool add_alpha : {false, true}) { - for (bool big_endian : {false, true}) { - params.codec = codec; - params.bits_per_sample = static_cast(bits_per_sample); - params.is_gray = is_gray; - params.add_alpha = add_alpha; - params.big_endian = big_endian; - params.add_extra_channels = false; - TestRoundTrip(params, pool.get()); - if (codec == Codec::kPNM && add_alpha) { - params.add_extra_channels = true; - TestRoundTrip(params, pool.get()); - } - } - } - } - } - } -} - -TEST(CodecTest, LosslessPNMRoundtrip) { - ThreadPoolForTests pool(12); - - static const char* kChannels[] = {"", "g", "ga", "rgb", "rgba"}; - static const char* kExtension[] = {"", ".pgm", ".pam", ".ppm", ".pam"}; - for (size_t bit_depth = 1; bit_depth <= 16; ++bit_depth) { - for (size_t channels = 1; channels <= 4; ++channels) { - if (bit_depth == 1 && (channels == 2 || channels == 4)) continue; - std::string extension(kExtension[channels]); - std::string filename = "jxl/flower/flower_small." + - std::string(kChannels[channels]) + ".depth" + - std::to_string(bit_depth) + extension; - const std::vector orig = jxl::test::ReadTestData(filename); - - PackedPixelFile ppf; - ColorHints color_hints; - color_hints.Add("color_space", - channels < 3 ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG"); - ASSERT_TRUE( - DecodeBytes(Bytes(orig.data(), orig.size()), color_hints, &ppf)); - - EncodedImage encoded; - auto encoder = Encoder::FromExtension(extension); - ASSERT_TRUE(encoder.get()); - ASSERT_TRUE(encoder->Encode(ppf, &encoded, pool.get())); - ASSERT_EQ(encoded.bitstreams.size(), 1); - ASSERT_EQ(orig.size(), encoded.bitstreams[0].size()); - EXPECT_EQ(0, - memcmp(orig.data(), encoded.bitstreams[0].data(), orig.size())); - } - } -} - -TEST(CodecTest, TestPNM) { - size_t u = 77777; // Initialized to wrong value. - double d = 77.77; -// Failing to parse invalid strings results in a crash if `JXL_CRASH_ON_ERROR` -// is defined and hence the tests fail. Therefore we only run these tests if -// `JXL_CRASH_ON_ERROR` is not defined. -#if (!JXL_CRASH_ON_ERROR) - ASSERT_FALSE(PnmParseUnsigned(MakeSpan(""), &u)); - ASSERT_FALSE(PnmParseUnsigned(MakeSpan("+"), &u)); - ASSERT_FALSE(PnmParseUnsigned(MakeSpan("-"), &u)); - ASSERT_FALSE(PnmParseUnsigned(MakeSpan("A"), &u)); - - ASSERT_FALSE(PnmParseSigned(MakeSpan(""), &d)); - ASSERT_FALSE(PnmParseSigned(MakeSpan("+"), &d)); - ASSERT_FALSE(PnmParseSigned(MakeSpan("-"), &d)); - ASSERT_FALSE(PnmParseSigned(MakeSpan("A"), &d)); -#endif - ASSERT_TRUE(PnmParseUnsigned(MakeSpan("1"), &u)); - ASSERT_TRUE(u == 1); - - ASSERT_TRUE(PnmParseUnsigned(MakeSpan("32"), &u)); - ASSERT_TRUE(u == 32); - - ASSERT_TRUE(PnmParseSigned(MakeSpan("1"), &d)); - ASSERT_TRUE(d == 1.0); - ASSERT_TRUE(PnmParseSigned(MakeSpan("+2"), &d)); - ASSERT_TRUE(d == 2.0); - ASSERT_TRUE(PnmParseSigned(MakeSpan("-3"), &d)); - ASSERT_TRUE(std::abs(d - -3.0) < 1E-15); - ASSERT_TRUE(PnmParseSigned(MakeSpan("3.141592"), &d)); - ASSERT_TRUE(std::abs(d - 3.141592) < 1E-15); - ASSERT_TRUE(PnmParseSigned(MakeSpan("-3.141592"), &d)); - ASSERT_TRUE(std::abs(d - -3.141592) < 1E-15); -} - -TEST(CodecTest, FormatNegotiation) { - const std::vector accepted_formats = { - {/*num_channels=*/4, - /*data_type=*/JXL_TYPE_UINT16, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0}, - {/*num_channels=*/3, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0}, - {/*num_channels=*/3, - /*data_type=*/JXL_TYPE_UINT16, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0}, - {/*num_channels=*/1, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0}, - }; - - JxlBasicInfo info; - JxlEncoderInitBasicInfo(&info); - info.bits_per_sample = 12; - info.num_color_channels = 2; - - JxlPixelFormat format; - EXPECT_FALSE(SelectFormat(accepted_formats, info, &format)); - - info.num_color_channels = 3; - ASSERT_TRUE(SelectFormat(accepted_formats, info, &format)); - EXPECT_EQ(format.num_channels, info.num_color_channels); - // 16 is the smallest accepted format that can accommodate the 12-bit data. - EXPECT_EQ(format.data_type, JXL_TYPE_UINT16); -} - -TEST(CodecTest, EncodeToPNG) { - ThreadPool* const pool = nullptr; - - std::unique_ptr png_encoder = Encoder::FromExtension(".png"); - if (!png_encoder) { - fprintf(stderr, "Skipping test because of missing codec support.\n"); - return; - } - - const std::vector original_png = jxl::test::ReadTestData( - "external/wesaturate/500px/tmshre_riaphotographs_srgb8.png"); - PackedPixelFile ppf; - ASSERT_TRUE(extras::DecodeBytes(Bytes(original_png), ColorHints(), &ppf)); - - const JxlPixelFormat& format = ppf.frames.front().color.format; - const auto& format_matcher = [&format](const JxlPixelFormat& candidate) { - return (candidate.num_channels == format.num_channels) && - (candidate.data_type == format.data_type) && - (candidate.endianness == format.endianness); - }; - const auto formats = png_encoder->AcceptedFormats(); - ASSERT_TRUE(std::any_of(formats.begin(), formats.end(), format_matcher)); - EncodedImage encoded_png; - ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool)); - EXPECT_TRUE(encoded_png.icc.empty()); - ASSERT_EQ(encoded_png.bitstreams.size(), 1); - - PackedPixelFile decoded_ppf; - ASSERT_TRUE(extras::DecodeBytes(Bytes(encoded_png.bitstreams.front()), - ColorHints(), &decoded_ppf)); - - ASSERT_EQ(decoded_ppf.info.bits_per_sample, ppf.info.bits_per_sample); - ASSERT_EQ(decoded_ppf.frames.size(), 1); - VerifySameImage(ppf.frames[0].color, ppf.info.bits_per_sample, - decoded_ppf.frames[0].color, - decoded_ppf.info.bits_per_sample); -} - -} // namespace -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/common.cc b/third_party/jpeg-xl/lib/extras/common.cc deleted file mode 100644 index 56abd13ea6699..0000000000000 --- a/third_party/jpeg-xl/lib/extras/common.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/common.h" - -#include -#include - -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -Status SelectFormat(const std::vector& accepted_formats, - const JxlBasicInfo& basic_info, JxlPixelFormat* format) { - const size_t original_bit_depth = basic_info.bits_per_sample; - size_t current_bit_depth = 0; - size_t num_alpha_channels = (basic_info.alpha_bits != 0 ? 1 : 0); - size_t num_channels = basic_info.num_color_channels + num_alpha_channels; - for (;;) { - for (const JxlPixelFormat& candidate : accepted_formats) { - if (candidate.num_channels != num_channels) continue; - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(candidate.data_type)); - const size_t candidate_bit_depth = - PackedImage::BitsPerChannel(candidate.data_type); - if ( - // Candidate bit depth is less than what we have and still enough - (original_bit_depth <= candidate_bit_depth && - candidate_bit_depth < current_bit_depth) || - // Or larger than the too-small bit depth we currently have - (current_bit_depth < candidate_bit_depth && - current_bit_depth < original_bit_depth)) { - *format = candidate; - current_bit_depth = candidate_bit_depth; - } - } - if (current_bit_depth == 0) { - if (num_channels > basic_info.num_color_channels) { - // Try dropping the alpha channel. - --num_channels; - continue; - } - return JXL_FAILURE("no appropriate format found"); - } - break; - } - if (current_bit_depth < original_bit_depth) { - JXL_WARNING("encoding %" PRIuS "-bit original to %" PRIuS " bits", - original_bit_depth, current_bit_depth); - } - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/common.h b/third_party/jpeg-xl/lib/extras/common.h deleted file mode 100644 index 88ed581af3dc1..0000000000000 --- a/third_party/jpeg-xl/lib/extras/common.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_COMMON_H_ -#define LIB_EXTRAS_COMMON_H_ - -#include -#include - -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -// TODO(sboukortt): consider exposing this as part of the C API. -Status SelectFormat(const std::vector& accepted_formats, - const JxlBasicInfo& basic_info, JxlPixelFormat* format); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/extras/compressed_icc.cc b/third_party/jpeg-xl/lib/extras/compressed_icc.cc deleted file mode 100644 index 52d433d4f9d30..0000000000000 --- a/third_party/jpeg-xl/lib/extras/compressed_icc.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include "lib/jxl/base/span.h" -#include "lib/jxl/enc_aux_out.h" -#include "lib/jxl/enc_icc_codec.h" -#include "lib/jxl/icc_codec.h" - -JXL_BOOL JxlICCProfileEncode(const JxlMemoryManager* memory_manager, - const uint8_t* icc, size_t icc_size, - uint8_t** compressed_icc, - size_t* compressed_icc_size) { - JxlMemoryManager local_memory_manager; - if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager)) { - return JXL_FALSE; - } - jxl::BitWriter writer(&local_memory_manager); - JXL_RETURN_IF_ERROR(jxl::WriteICC(jxl::Span(icc, icc_size), - &writer, jxl::LayerType::Header, nullptr)); - writer.ZeroPadToByte(); - jxl::Bytes bytes = writer.GetSpan(); - *compressed_icc_size = bytes.size(); - *compressed_icc = static_cast( - jxl::MemoryManagerAlloc(&local_memory_manager, *compressed_icc_size)); - memcpy(*compressed_icc, bytes.data(), bytes.size()); - return JXL_TRUE; -} - -JXL_BOOL JxlICCProfileDecode(const JxlMemoryManager* memory_manager, - const uint8_t* compressed_icc, - size_t compressed_icc_size, uint8_t** icc, - size_t* icc_size) { - JxlMemoryManager local_memory_manager; - if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager)) { - return JXL_FALSE; - } - jxl::ICCReader icc_reader(&local_memory_manager); - jxl::PaddedBytes decompressed(&local_memory_manager); - jxl::BitReader bit_reader( - jxl::Span(compressed_icc, compressed_icc_size)); - JXL_RETURN_IF_ERROR(icc_reader.Init(&bit_reader)); - JXL_RETURN_IF_ERROR(icc_reader.Process(&bit_reader, &decompressed)); - JXL_RETURN_IF_ERROR(bit_reader.Close()); - *icc_size = decompressed.size(); - *icc = static_cast( - jxl::MemoryManagerAlloc(&local_memory_manager, *icc_size)); - memcpy(*icc, decompressed.data(), *icc_size); - return JXL_TRUE; -} diff --git a/third_party/jpeg-xl/lib/extras/compressed_icc_test.cc b/third_party/jpeg-xl/lib/extras/compressed_icc_test.cc deleted file mode 100644 index 2aa994d6e69e8..0000000000000 --- a/third_party/jpeg-xl/lib/extras/compressed_icc_test.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "jxl/compressed_icc.h" - -#include - -#include -#include -#include - -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/test_memory_manager.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace { - -TEST(CompressedIccTest, Roundtrip) { - JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); - uint8_t* compressed_icc; - size_t compressed_icc_size; - const IccBytes icc = jxl::test::GetIccTestProfile(); - ASSERT_TRUE(JxlICCProfileEncode(memory_manager, icc.data(), icc.size(), - &compressed_icc, &compressed_icc_size)); - - EXPECT_LT(compressed_icc_size, icc.size()); - - uint8_t* decompressed_icc; - size_t decompressed_icc_size; - ASSERT_TRUE(JxlICCProfileDecode(memory_manager, compressed_icc, - compressed_icc_size, &decompressed_icc, - &decompressed_icc_size)); - - ASSERT_EQ(decompressed_icc_size, icc.size()); - - EXPECT_EQ(0, memcmp(decompressed_icc, icc.data(), decompressed_icc_size)); - - memory_manager->free(memory_manager->opaque, compressed_icc); - memory_manager->free(memory_manager->opaque, decompressed_icc); -} - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc deleted file mode 100644 index 24b4795d03dc6..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/apng.cc +++ /dev/null @@ -1,1293 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/apng.h" - -// Parts of this code are taken from apngdis, which has the following license: -/* APNG Disassembler 2.8 - * - * Deconstructs APNG files into individual frames. - * - * http://apngdis.sourceforge.net - * - * Copyright (c) 2010-2015 Max Stepin - * maxst at users.sourceforge.net - * - * zlib license - * ------------ - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/extras/size_constraints.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/sanitizers.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" -#if JPEGXL_ENABLE_APNG -#include "png.h" /* original (unpatched) libpng is ok */ -#endif - -namespace jxl { -namespace extras { - -#if !JPEGXL_ENABLE_APNG - -bool CanDecodeAPNG() { return false; } -Status DecodeImageAPNG(const Span bytes, - const ColorHints& color_hints, PackedPixelFile* ppf, - const SizeConstraints* constraints) { - return false; -} - -#else // JPEGXL_ENABLE_APNG - -namespace { - -constexpr std::array kPngSignature = {137, 'P', 'N', 'G', - '\r', '\n', 26, '\n'}; - -// Returns floating-point value from the PNG encoding (times 10^5). -double F64FromU32(const uint32_t x) { return static_cast(x) * 1E-5; } - -/** Extract information from 'sRGB' chunk. */ -Status DecodeSrgbChunk(const Bytes payload, JxlColorEncoding* color_encoding) { - if (payload.size() != 1) return JXL_FAILURE("Wrong sRGB size"); - uint8_t ri = payload[0]; - // (PNG uses the same values as ICC.) - if (ri >= 4) return JXL_FAILURE("Invalid Rendering Intent"); - color_encoding->white_point = JXL_WHITE_POINT_D65; - color_encoding->primaries = JXL_PRIMARIES_SRGB; - color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - color_encoding->rendering_intent = static_cast(ri); - return true; -} - -/** - * Extract information from 'cICP' chunk. - * - * If the cICP profile is not fully supported, return `false` and leave - * `color_encoding` unmodified. - */ -Status DecodeCicpChunk(const Bytes payload, JxlColorEncoding* color_encoding) { - if (payload.size() != 4) return JXL_FAILURE("Wrong cICP size"); - JxlColorEncoding color_enc = *color_encoding; - - // From https://www.itu.int/rec/T-REC-H.273-202107-I/en - if (payload[0] == 1) { - // IEC 61966-2-1 sRGB - color_enc.primaries = JXL_PRIMARIES_SRGB; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else if (payload[0] == 4) { - // Rec. ITU-R BT.470-6 System M - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 0.67; - color_enc.primaries_red_xy[1] = 0.33; - color_enc.primaries_green_xy[0] = 0.21; - color_enc.primaries_green_xy[1] = 0.71; - color_enc.primaries_blue_xy[0] = 0.14; - color_enc.primaries_blue_xy[1] = 0.08; - color_enc.white_point = JXL_WHITE_POINT_CUSTOM; - color_enc.white_point_xy[0] = 0.310; - color_enc.white_point_xy[1] = 0.316; - } else if (payload[0] == 5) { - // Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 0.64; - color_enc.primaries_red_xy[1] = 0.33; - color_enc.primaries_green_xy[0] = 0.29; - color_enc.primaries_green_xy[1] = 0.60; - color_enc.primaries_blue_xy[0] = 0.15; - color_enc.primaries_blue_xy[1] = 0.06; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else if (payload[0] == 6 || payload[0] == 7) { - // SMPTE ST 170 (2004) / SMPTE ST 240 (1999) - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 0.630; - color_enc.primaries_red_xy[1] = 0.340; - color_enc.primaries_green_xy[0] = 0.310; - color_enc.primaries_green_xy[1] = 0.595; - color_enc.primaries_blue_xy[0] = 0.155; - color_enc.primaries_blue_xy[1] = 0.070; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else if (payload[0] == 8) { - // Generic film (colour filters using Illuminant C) - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 0.681; - color_enc.primaries_red_xy[1] = 0.319; - color_enc.primaries_green_xy[0] = 0.243; - color_enc.primaries_green_xy[1] = 0.692; - color_enc.primaries_blue_xy[0] = 0.145; - color_enc.primaries_blue_xy[1] = 0.049; - color_enc.white_point = JXL_WHITE_POINT_CUSTOM; - color_enc.white_point_xy[0] = 0.310; - color_enc.white_point_xy[1] = 0.316; - } else if (payload[0] == 9) { - // Rec. ITU-R BT.2100-2 - color_enc.primaries = JXL_PRIMARIES_2100; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else if (payload[0] == 10) { - // CIE 1931 XYZ - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 1; - color_enc.primaries_red_xy[1] = 0; - color_enc.primaries_green_xy[0] = 0; - color_enc.primaries_green_xy[1] = 1; - color_enc.primaries_blue_xy[0] = 0; - color_enc.primaries_blue_xy[1] = 0; - color_enc.white_point = JXL_WHITE_POINT_E; - } else if (payload[0] == 11) { - // SMPTE RP 431-2 (2011) - color_enc.primaries = JXL_PRIMARIES_P3; - color_enc.white_point = JXL_WHITE_POINT_DCI; - } else if (payload[0] == 12) { - // SMPTE EG 432-1 (2010) - color_enc.primaries = JXL_PRIMARIES_P3; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else if (payload[0] == 22) { - color_enc.primaries = JXL_PRIMARIES_CUSTOM; - color_enc.primaries_red_xy[0] = 0.630; - color_enc.primaries_red_xy[1] = 0.340; - color_enc.primaries_green_xy[0] = 0.295; - color_enc.primaries_green_xy[1] = 0.605; - color_enc.primaries_blue_xy[0] = 0.155; - color_enc.primaries_blue_xy[1] = 0.077; - color_enc.white_point = JXL_WHITE_POINT_D65; - } else { - JXL_WARNING("Unsupported primaries specified in cICP chunk: %d", - static_cast(payload[0])); - return false; - } - - if (payload[1] == 1 || payload[1] == 6 || payload[1] == 14 || - payload[1] == 15) { - // Rec. ITU-R BT.709-6 - color_enc.transfer_function = JXL_TRANSFER_FUNCTION_709; - } else if (payload[1] == 4) { - // Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM - color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - color_enc.gamma = 1 / 2.2; - } else if (payload[1] == 5) { - // Rec. ITU-R BT.470-6 System B, G - color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - color_enc.gamma = 1 / 2.8; - } else if (payload[1] == 8 || payload[1] == 13 || payload[1] == 16 || - payload[1] == 17 || payload[1] == 18) { - // These codes all match the corresponding JXL enum values - color_enc.transfer_function = static_cast(payload[1]); - } else { - JXL_WARNING("Unsupported transfer function specified in cICP chunk: %d", - static_cast(payload[1])); - return false; - } - - if (payload[2] != 0) { - JXL_WARNING("Unsupported color space specified in cICP chunk: %d", - static_cast(payload[2])); - return false; - } - if (payload[3] != 1) { - JXL_WARNING("Unsupported full-range flag specified in cICP chunk: %d", - static_cast(payload[3])); - return false; - } - // cICP has no rendering intent, so use the default - color_enc.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - *color_encoding = color_enc; - return true; -} - -/** Extract information from 'gAMA' chunk. */ -Status DecodeGamaChunk(Bytes payload, JxlColorEncoding* color_encoding) { - if (payload.size() != 4) return JXL_FAILURE("Wrong gAMA size"); - color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - color_encoding->gamma = F64FromU32(LoadBE32(payload.data())); - return true; -} - -/** Extract information from 'cHTM' chunk. */ -Status DecodeChrmChunk(Bytes payload, JxlColorEncoding* color_encoding) { - if (payload.size() != 32) return JXL_FAILURE("Wrong cHRM size"); - const uint8_t* data = payload.data(); - color_encoding->white_point = JXL_WHITE_POINT_CUSTOM; - color_encoding->white_point_xy[0] = F64FromU32(LoadBE32(data + 0)); - color_encoding->white_point_xy[1] = F64FromU32(LoadBE32(data + 4)); - - color_encoding->primaries = JXL_PRIMARIES_CUSTOM; - color_encoding->primaries_red_xy[0] = F64FromU32(LoadBE32(data + 8)); - color_encoding->primaries_red_xy[1] = F64FromU32(LoadBE32(data + 12)); - color_encoding->primaries_green_xy[0] = F64FromU32(LoadBE32(data + 16)); - color_encoding->primaries_green_xy[1] = F64FromU32(LoadBE32(data + 20)); - color_encoding->primaries_blue_xy[0] = F64FromU32(LoadBE32(data + 24)); - color_encoding->primaries_blue_xy[1] = F64FromU32(LoadBE32(data + 28)); - return true; -} - -/** Extracts information from 'cLLi' chunk. */ -Status DecodeClliChunk(Bytes payload, float* max_content_light_level) { - if (payload.size() != 8) return JXL_FAILURE("Wrong cLLi size"); - const uint8_t* data = payload.data(); - const uint32_t maxcll_png = - Clamp1(png_get_uint_32(data), uint32_t{0}, uint32_t{10000 * 10000}); - // Ignore MaxFALL value. - *max_content_light_level = static_cast(maxcll_png) / 10000.f; - return true; -} - -/** Returns false if invalid. */ -JXL_INLINE Status DecodeHexNibble(const char c, uint32_t* JXL_RESTRICT nibble) { - if ('a' <= c && c <= 'f') { - *nibble = 10 + c - 'a'; - } else if ('0' <= c && c <= '9') { - *nibble = c - '0'; - } else { - *nibble = 0; - return JXL_FAILURE("Invalid metadata nibble"); - } - JXL_ENSURE(*nibble < 16); - return true; -} - -/** Returns false if invalid. */ -JXL_INLINE Status DecodeDecimal(const char** pos, const char* end, - uint32_t* JXL_RESTRICT value) { - size_t len = 0; - *value = 0; - while (*pos < end) { - char next = **pos; - if (next >= '0' && next <= '9') { - *value = (*value * 10) + static_cast(next - '0'); - len++; - if (len > 8) { - break; - } - } else { - // Do not consume terminator (non-decimal digit). - break; - } - (*pos)++; - } - if (len == 0 || len > 8) { - return JXL_FAILURE("Failed to parse decimal"); - } - return true; -} - -/** - * Parses a PNG text chunk with key of the form "Raw profile type ####", with - * #### a type. - * - * Returns whether it could successfully parse the content. - * We trust key and encoded are null-terminated because they come from - * libpng. - */ -Status MaybeDecodeBase16(const char* key, const char* encoded, - std::string* type, std::vector* bytes) { - const char* encoded_end = encoded + strlen(encoded); - - const char* kKey = "Raw profile type "; - if (strncmp(key, kKey, strlen(kKey)) != 0) return false; - *type = key + strlen(kKey); - const size_t kMaxTypeLen = 20; - if (type->length() > kMaxTypeLen) return false; // Type too long - - // Header: freeform string and number of bytes - // Expected format is: - // \n - // profile name/description\n - // 40\n (the number of bytes after hex-decoding) - // 01234566789abcdef....\n (72 bytes per line max). - // 012345667\n (last line) - const char* pos = encoded; - - if (*(pos++) != '\n') return false; - while (pos < encoded_end && *pos != '\n') { - pos++; - } - if (pos == encoded_end) return false; - // We parsed so far a \n, some number of non \n characters and are now - // pointing at a \n. - if (*(pos++) != '\n') return false; - // Skip leading spaces - while (pos < encoded_end && *pos == ' ') { - pos++; - } - uint32_t bytes_to_decode = 0; - JXL_RETURN_IF_ERROR(DecodeDecimal(&pos, encoded_end, &bytes_to_decode)); - - // We need 2*bytes for the hex values plus 1 byte every 36 values, - // plus terminal \n for length. - size_t tail = static_cast(encoded_end - pos); - bool ok = ((tail / 2) >= bytes_to_decode); - if (ok) tail -= 2 * static_cast(bytes_to_decode); - ok = ok && (tail == 1 + DivCeil(bytes_to_decode, 36)); - if (!ok) { - return JXL_FAILURE("Not enough bytes to parse %d bytes in hex", - bytes_to_decode); - } - JXL_ENSURE(bytes->empty()); - bytes->reserve(bytes_to_decode); - - // Encoding: base16 with newline after 72 chars. - // pos points to the \n before the first line of hex values. - for (size_t i = 0; i < bytes_to_decode; ++i) { - if (i % 36 == 0) { - if (pos + 1 >= encoded_end) return false; // Truncated base16 1 - if (*pos != '\n') return false; // Expected newline - ++pos; - } - - if (pos + 2 >= encoded_end) return false; // Truncated base16 2; - uint32_t nibble0; - uint32_t nibble1; - JXL_RETURN_IF_ERROR(DecodeHexNibble(pos[0], &nibble0)); - JXL_RETURN_IF_ERROR(DecodeHexNibble(pos[1], &nibble1)); - bytes->push_back(static_cast((nibble0 << 4) + nibble1)); - pos += 2; - } - if (pos + 1 != encoded_end) return false; // Too many encoded bytes - if (pos[0] != '\n') return false; // Incorrect metadata terminator - return true; -} - -/** Retrieves XMP and EXIF/IPTC from itext and text. */ -Status DecodeBlob(const png_text_struct& info, PackedMetadata* metadata) { - // We trust these are properly null-terminated by libpng. - const char* key = info.key; - const char* value = info.text; - if (strstr(key, "XML:com.adobe.xmp")) { - metadata->xmp.resize(strlen(value)); // safe, see above - memcpy(metadata->xmp.data(), value, metadata->xmp.size()); - } - - std::string type; - std::vector bytes; - - // Handle text chunks annotated with key "Raw profile type ####", with - // #### a type, which may contain metadata. - const char* kKey = "Raw profile type "; - if (strncmp(key, kKey, strlen(kKey)) != 0) return false; - - if (!MaybeDecodeBase16(key, value, &type, &bytes)) { - JXL_WARNING("Couldn't parse 'Raw format type' text chunk"); - return false; - } - if (type == "exif") { - // Remove prefix if present. - constexpr std::array kExifPrefix = {'E', 'x', 'i', 'f', 0, 0}; - if (bytes.size() >= kExifPrefix.size() && - memcmp(bytes.data(), kExifPrefix.data(), kExifPrefix.size()) == 0) { - bytes.erase(bytes.begin(), bytes.begin() + kExifPrefix.size()); - } - if (!metadata->exif.empty()) { - JXL_DEBUG_V(2, - "overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS - " bytes)", - metadata->exif.size(), bytes.size()); - } - metadata->exif = std::move(bytes); - } else if (type == "iptc") { - // TODO(jon): Deal with IPTC in some way - } else if (type == "8bim") { - // TODO(jon): Deal with 8bim in some way - } else if (type == "xmp") { - if (!metadata->xmp.empty()) { - JXL_DEBUG_V(2, - "overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS - " bytes)", - metadata->xmp.size(), bytes.size()); - } - metadata->xmp = std::move(bytes); - } else { - JXL_DEBUG_V( - 2, "Unknown type in 'Raw format type' text chunk: %s: %" PRIuS " bytes", - type.c_str(), bytes.size()); - } - return true; -} - -constexpr bool isAbc(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} - -/** Wrap 4-char tag name into ID. */ -constexpr uint32_t MakeTag(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - return a | (b << 8) | (c << 16) | (d << 24); -} - -/** Reusable image data container. */ -struct Pixels { - // Use array instead of vector to avoid memory initialization. - std::unique_ptr pixels; - size_t pixels_size = 0; - std::vector rows; - std::atomic has_error{false}; - - Status Resize(size_t row_bytes, size_t num_rows) { - size_t new_size = row_bytes * num_rows; // it is assumed size is sane - if (new_size > pixels_size) { - pixels.reset(new uint8_t[new_size]); - if (!pixels) { - // TODO(szabadka): use specialized OOM error code - return JXL_FAILURE("Failed to allocate memory for image buffer"); - } - pixels_size = new_size; - } - rows.resize(num_rows); - for (size_t y = 0; y < num_rows; y++) { - rows[y] = pixels.get() + y * row_bytes; - } - return true; - } -}; - -/** - * Helper that chunks in-memory input. - */ -struct Reader { - explicit Reader(Span data) : data_(data) {} - - const Span data_; - size_t offset_ = 0; - - Bytes Peek(size_t len) const { - size_t cap = data_.size() - offset_; - size_t to_copy = std::min(cap, len); - return {data_.data() + offset_, to_copy}; - } - - Bytes Read(size_t len) { - Bytes result = Peek(len); - offset_ += result.size(); - return result; - } - - /* Returns empty Span on error. */ - Bytes ReadChunk() { - Bytes len = Peek(4); - if (len.size() != 4) { - return Bytes(); - } - const auto size = png_get_uint_32(len.data()); - // NB: specification allows 2^31 - 1 - constexpr size_t kMaxPNGChunkSize = 1u << 30; // 1 GB - // Check first, to avoid overflow. - if (size > kMaxPNGChunkSize) { - JXL_WARNING("APNG chunk size is too big"); - return Bytes(); - } - size_t full_size = size + 12; // size does not include itself, tag and CRC. - Bytes result = Read(full_size); - return (result.size() == full_size) ? result : Bytes(); - } - - bool Eof() const { return offset_ == data_.size(); } -}; - -void ProgressiveRead_OnInfo(png_structp png_ptr, png_infop info_ptr) { - png_set_expand(png_ptr); - png_set_palette_to_rgb(png_ptr); - png_set_tRNS_to_alpha(png_ptr); - (void)png_set_interlace_handling(png_ptr); - png_read_update_info(png_ptr, info_ptr); -} - -void ProgressiveRead_OnRow(png_structp png_ptr, png_bytep new_row, - png_uint_32 row_num, int pass) { - Pixels* frame = reinterpret_cast(png_get_progressive_ptr(png_ptr)); - if (!frame) { - JXL_DEBUG_ABORT("Internal logic error"); - return; - } - if (row_num >= frame->rows.size()) { - frame->has_error = true; - return; - } - png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row); -} - -// Holds intermediate state during parsing APNG file. -struct Context { - ~Context() { - // Make sure png memory is released in any case. - ResetPngDecoder(); - } - - bool CreatePngDecoder() { - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, - nullptr); - info_ptr = png_create_info_struct(png_ptr); - return (png_ptr != nullptr && info_ptr != nullptr); - } - - /** - * Initialize PNG decoder. - * - * TODO(eustas): add details - */ - bool InitPngDecoder(const std::vector& chunksInfo, - const RectT& viewport) { - ResetPngDecoder(); - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, - nullptr); - info_ptr = png_create_info_struct(png_ptr); - if (png_ptr == nullptr || info_ptr == nullptr) { - return false; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - return false; - } - - /* hIST chunk tail is not processed properly; skip this chunk completely; - see https://github.com/glennrp/libpng/pull/413 */ - constexpr std::array kIgnoredChunks = {'h', 'I', 'S', 'T', 0}; - png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredChunks.data(), - static_cast(kIgnoredChunks.size() / 5)); - - png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); - png_set_progressive_read_fn(png_ptr, static_cast(&frameRaw), - ProgressiveRead_OnInfo, ProgressiveRead_OnRow, - nullptr); - - png_process_data(png_ptr, info_ptr, - const_cast(kPngSignature.data()), - kPngSignature.size()); - - // Patch dimensions. - png_save_uint_32(ihdr.data() + 8, static_cast(viewport.xsize())); - png_save_uint_32(ihdr.data() + 12, static_cast(viewport.ysize())); - png_process_data(png_ptr, info_ptr, ihdr.data(), ihdr.size()); - - for (const auto& chunk : chunksInfo) { - png_process_data(png_ptr, info_ptr, const_cast(chunk.data()), - chunk.size()); - } - - return true; - } - - /** - * Pass chunk to PNG decoder. - */ - bool FeedChunks(const Bytes& chunk1, const Bytes& chunk2 = Bytes()) { - // TODO(eustas): turn to DCHECK - if (!png_ptr || !info_ptr) return false; - - if (setjmp(png_jmpbuf(png_ptr))) { - return false; - } - - for (const auto& chunk : {chunk1, chunk2}) { - if (!chunk.empty()) { - png_process_data(png_ptr, info_ptr, const_cast(chunk.data()), - chunk.size()); - } - } - return true; - } - - bool FinalizeStream(PackedMetadata* metadata) { - // TODO(eustas): turn to DCHECK - if (!png_ptr || !info_ptr) return false; - - if (setjmp(png_jmpbuf(png_ptr))) { - return false; - } - - const std::array kFooter = {0, 0, 0, 0, 73, 69, - 78, 68, 174, 66, 96, 130}; - png_process_data(png_ptr, info_ptr, const_cast(kFooter.data()), - kFooter.size()); - // before destroying: check if we encountered any metadata chunks - png_textp text_ptr = nullptr; - int num_text = 0; - if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) != 0) { - msan::UnpoisonMemory(text_ptr, sizeof(png_text_struct) * num_text); - for (int i = 0; i < num_text; i++) { - Status result = DecodeBlob(text_ptr[i], metadata); - // Ignore unknown / malformed blob. - (void)result; - } - } - - return true; - } - - void ResetPngDecoder() { - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - // Just in case. Not all versions on libpng wipe-out the pointers. - png_ptr = nullptr; - info_ptr = nullptr; - } - - std::array ihdr; // (modified) copy of file IHDR chunk - png_structp png_ptr = nullptr; - png_infop info_ptr = nullptr; - Pixels frameRaw = {}; -}; - -enum class DisposeOp : uint8_t { NONE = 0, BACKGROUND = 1, PREVIOUS = 2 }; - -constexpr uint8_t kLastDisposeOp = static_cast(DisposeOp::PREVIOUS); - -enum class BlendOp : uint8_t { SOURCE = 0, OVER = 1 }; - -constexpr uint8_t kLastBlendOp = static_cast(BlendOp::OVER); - -// fcTL -struct FrameControl { - uint32_t delay_num; - uint32_t delay_den; - RectT viewport; - DisposeOp dispose_op; - BlendOp blend_op; -}; - -struct Frame { - PackedImage pixels; - FrameControl metadata; -}; - -bool ValidateViewport(const RectT& r) { - constexpr uint32_t kMaxPngDim = 1000000UL; - return (r.xsize() <= kMaxPngDim) && (r.ysize() <= kMaxPngDim); -} - -/** - * Setup #channels, bpp, colorspace, etc. from PNG values. - */ -void SetColorData(PackedPixelFile* ppf, uint8_t color_type, uint8_t bit_depth, - png_color_8p sig_bits, uint32_t has_transparency) { - bool palette_used = ((color_type & 1) != 0); - bool color_used = ((color_type & 2) != 0); - bool alpha_channel_used = ((color_type & 4) != 0); - if (palette_used) { - if (!color_used || alpha_channel_used) { - JXL_DEBUG_V(2, "Unexpected PNG color type"); - } - } - - ppf->info.bits_per_sample = bit_depth; - - if (palette_used) { - // palette will actually be 8-bit regardless of the index bitdepth - ppf->info.bits_per_sample = 8; - } - if (color_used) { - ppf->info.num_color_channels = 3; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; - if (sig_bits) { - if (sig_bits->red == sig_bits->green && - sig_bits->green == sig_bits->blue) { - ppf->info.bits_per_sample = sig_bits->red; - } else { - int maxbps = - std::max(sig_bits->red, std::max(sig_bits->green, sig_bits->blue)); - JXL_DEBUG_V(2, - "sBIT chunk: bit depths for R, G, and B are not the same " - "(%i %i %i), while in JPEG XL they have to be the same. " - "Setting RGB bit depth to %i.", - sig_bits->red, sig_bits->green, sig_bits->blue, maxbps); - ppf->info.bits_per_sample = maxbps; - } - } - } else { - ppf->info.num_color_channels = 1; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY; - if (sig_bits) ppf->info.bits_per_sample = sig_bits->gray; - } - if (alpha_channel_used || has_transparency) { - ppf->info.alpha_bits = ppf->info.bits_per_sample; - if (sig_bits && sig_bits->alpha != ppf->info.bits_per_sample) { - JXL_DEBUG_V(2, - "sBIT chunk: bit depths for RGBA are inconsistent " - "(%i %i %i %i). Setting A bitdepth to %i.", - sig_bits->red, sig_bits->green, sig_bits->blue, - sig_bits->alpha, ppf->info.bits_per_sample); - } - } else { - ppf->info.alpha_bits = 0; - } - ppf->color_encoding.color_space = (ppf->info.num_color_channels == 1) - ? JXL_COLOR_SPACE_GRAY - : JXL_COLOR_SPACE_RGB; -} - -// Color profile chunks: cICP has the highest priority, followed by -// iCCP and sRGB (which shouldn't co-exist, but if they do, we use -// iCCP), followed finally by gAMA and cHRM. -enum class ColorInfoType { - NONE = 0, - GAMA_OR_CHRM = 1, - ICCP_OR_SRGB = 2, - CICP = 3 -}; - -} // namespace - -bool CanDecodeAPNG() { return true; } - -/** - * Parse and decode PNG file. - * - * Useful PNG chunks: - * acTL : animation control (#frames, loop count) - * fcTL : frame control (seq#, viewport, delay, disposal blending) - * bKGD : preferable background - * IDAT : "default image" - * if single fcTL goes before IDAT, then it is also first frame - * fdAT : seq# + IDAT-like content - * PLTE : palette - * cICP : coding-independent code points for video signal type identification - * iCCP : embedded ICC profile - * sRGB : standard RGB colour space - * eXIf : exchangeable image file profile - * gAMA : image gamma - * cHRM : primary chromaticities and white point - * tRNS : transparency - * - * PNG chunk ordering: - * - IHDR first - * - IEND last - * - acTL, cHRM, cICP, gAMA, iCCP, sRGB, bKGD, eXIf, PLTE before IDAT - * - fdAT after IDAT - * - * More rules: - * - iCCP and sRGB are exclusive - * - fcTL and fdAT seq# must be in order fro 0, with no gaps or duplicates - * - fcTL before corresponding IDAT / fdAT - */ -Status DecodeImageAPNG(const Span bytes, - const ColorHints& color_hints, PackedPixelFile* ppf, - const SizeConstraints* constraints) { - // Initialize output (default settings in case e.g. only gAMA is given). - ppf->frames.clear(); - ppf->info.exponent_bits_per_sample = 0; - ppf->info.alpha_exponent_bits = 0; - ppf->info.orientation = JXL_ORIENT_IDENTITY; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; - ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; - ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; - ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - - Reader input(bytes); - - // Check signature. - Bytes sig = input.Read(kPngSignature.size()); - if (sig.size() != 8 || - memcmp(sig.data(), kPngSignature.data(), kPngSignature.size()) != 0) { - return false; // Return silently if it is not a PNG - } - - // Check IHDR chunk. - Context ctx; - Bytes ihdr = input.ReadChunk(); - if (ihdr.size() != ctx.ihdr.size()) { - return JXL_FAILURE("Unexpected first chunk payload size"); - } - memcpy(ctx.ihdr.data(), ihdr.data(), ihdr.size()); - uint32_t id = LoadLE32(ihdr.data() + 4); - if (id != MakeTag('I', 'H', 'D', 'R')) { - return JXL_FAILURE("First chunk is not IHDR"); - } - const RectT image_rect(0, 0, png_get_uint_32(ihdr.data() + 8), - png_get_uint_32(ihdr.data() + 12)); - if (!ValidateViewport(image_rect)) { - return JXL_FAILURE("PNG image dimensions are too large"); - } - - // Chunks we supply to PNG decoder for every animation frame. - std::vector passthrough_chunks; - if (!ctx.InitPngDecoder(passthrough_chunks, image_rect)) { - return JXL_FAILURE("Failed to initialize PNG decoder"); - } - - // Marker that this PNG is animated. - bool seen_actl = false; - // First IDAT is a very important milestone; at this moment we freeze - // gathered metadata. - bool seen_idat = false; - // fCTL can occur multiple times, but only once before IDAT. - bool seen_fctl = false; - // Logical EOF. - bool seen_iend = false; - - ColorInfoType color_info_type = ColorInfoType::NONE; - - // Flag that we processed some IDAT / fDAT after image / frame start. - bool seen_pixel_data = false; - - uint32_t num_channels; - JxlPixelFormat format = {}; - size_t bytes_per_pixel = 0; - std::vector frames; - FrameControl current_frame = {/*delay_num=*/1, /*delay_den=*/10, image_rect, - DisposeOp::NONE, BlendOp::SOURCE}; - - // Copies frame pixels / metadata from temporary storage. - // TODO(eustas): avoid copying. - const auto finalize_frame = [&]() -> Status { - if (!seen_pixel_data) { - return JXL_FAILURE("Frame / image without fdAT / IDAT chunks"); - } - if (!ctx.FinalizeStream(&ppf->metadata)) { - return JXL_FAILURE("Failed to finalize PNG substream"); - } - if (ctx.frameRaw.has_error) { - return JXL_FAILURE("Internal error"); - } - // Allocates the frame buffer. - const RectT& vp = current_frame.viewport; - size_t xsize = static_cast(vp.xsize()); - size_t ysize = static_cast(vp.ysize()); - JXL_ASSIGN_OR_RETURN(PackedImage image, - PackedImage::Create(xsize, ysize, format)); - for (size_t y = 0; y < ysize; ++y) { - // TODO(eustas): ensure multiplication is safe - memcpy(static_cast(image.pixels()) + image.stride * y, - ctx.frameRaw.rows[y], bytes_per_pixel * xsize); - } - frames.push_back(Frame{std::move(image), current_frame}); - seen_pixel_data = false; - return true; - }; - - while (!input.Eof()) { - if (seen_iend) { - return JXL_FAILURE("Exuberant input after IEND chunk"); - } - Bytes chunk = input.ReadChunk(); - if (chunk.empty()) { - return JXL_FAILURE("Malformed chunk"); - } - Bytes type(chunk.data() + 4, 4); - id = LoadLE32(type.data()); - // Cut 'size' and 'type' at front and 'CRC' at the end. - Bytes payload(chunk.data() + 8, chunk.size() - 12); - - if (!isAbc(type[0]) || !isAbc(type[1]) || !isAbc(type[2]) || - !isAbc(type[3])) { - return JXL_FAILURE("Exotic PNG chunk"); - } - - switch (id) { - case MakeTag('a', 'c', 'T', 'L'): - if (seen_idat) { - JXL_DEBUG_V(2, "aCTL after IDAT ignored"); - continue; - } - if (seen_actl) { - JXL_DEBUG_V(2, "Duplicate aCTL chunk ignored"); - continue; - } - seen_actl = true; - ppf->info.have_animation = JXL_TRUE; - // TODO(eustas): decode from chunk? - ppf->info.animation.tps_numerator = 1000; - ppf->info.animation.tps_denominator = 1; - continue; - - case MakeTag('I', 'E', 'N', 'D'): - seen_iend = true; - JXL_RETURN_IF_ERROR(finalize_frame()); - continue; - - case MakeTag('f', 'c', 'T', 'L'): { - if (payload.size() != 26) { - return JXL_FAILURE("Unexpected fcTL payload size: %u", - static_cast(payload.size())); - } - if (seen_fctl && !seen_idat) { - return JXL_FAILURE("More than one fcTL before IDAT"); - } - if (seen_idat && !seen_actl) { - return JXL_FAILURE("fcTL after IDAT, but without acTL"); - } - seen_fctl = true; - - // TODO(eustas): check order? - // sequence_number = png_get_uint_32(payload.data()); - RectT raw_viewport(png_get_uint_32(payload.data() + 12), - png_get_uint_32(payload.data() + 16), - png_get_uint_32(payload.data() + 4), - png_get_uint_32(payload.data() + 8)); - uint8_t dispose_op = payload[24]; - if (dispose_op > kLastDisposeOp) { - return JXL_FAILURE("Invalid DisposeOp"); - } - uint8_t blend_op = payload[25]; - if (blend_op > kLastBlendOp) { - return JXL_FAILURE("Invalid BlendOp"); - } - FrameControl next_frame = { - /*delay_num=*/png_get_uint_16(payload.data() + 20), - /*delay_den=*/png_get_uint_16(payload.data() + 22), raw_viewport, - static_cast(dispose_op), static_cast(blend_op)}; - - if (!raw_viewport.Intersection(image_rect).IsSame(raw_viewport)) { - // Cropping happened. - return JXL_FAILURE("PNG frame is outside of image rect"); - } - - if (!seen_idat) { - // "Default" image is the first animation frame. Its viewport must - // cover the whole image area. - if (!raw_viewport.IsSame(image_rect)) { - return JXL_FAILURE( - "If the first animation frame is default image, its viewport " - "must cover full image"); - } - } else { - JXL_RETURN_IF_ERROR(finalize_frame()); - if (!ctx.InitPngDecoder(passthrough_chunks, next_frame.viewport)) { - return JXL_FAILURE("Failed to initialize PNG decoder"); - } - } - current_frame = next_frame; - continue; - } - - case MakeTag('I', 'D', 'A', 'T'): { - if (!frames.empty()) { - return JXL_FAILURE("IDAT after default image is over"); - } - if (!seen_idat) { - // First IDAT means that all metadata is ready. - seen_idat = true; - JXL_ENSURE(image_rect.xsize() == - png_get_image_width(ctx.png_ptr, ctx.info_ptr)); - JXL_ENSURE(image_rect.ysize() == - png_get_image_height(ctx.png_ptr, ctx.info_ptr)); - JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, image_rect.xsize(), - image_rect.ysize())); - ppf->info.xsize = image_rect.xsize(); - ppf->info.ysize = image_rect.ysize(); - - png_color_8p sig_bits = nullptr; - // Error is OK -> sig_bits remains nullptr. - png_get_sBIT(ctx.png_ptr, ctx.info_ptr, &sig_bits); - SetColorData(ppf, png_get_color_type(ctx.png_ptr, ctx.info_ptr), - png_get_bit_depth(ctx.png_ptr, ctx.info_ptr), sig_bits, - png_get_valid(ctx.png_ptr, ctx.info_ptr, PNG_INFO_tRNS)); - num_channels = - ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0); - format = { - /*num_channels=*/num_channels, - /*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16 - : JXL_TYPE_UINT8, - /*endianness=*/JXL_BIG_ENDIAN, - /*align=*/0, - }; - bytes_per_pixel = - num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); - // TODO(eustas): ensure multiplication is safe - uint64_t row_bytes = - static_cast(image_rect.xsize()) * bytes_per_pixel; - uint64_t max_rows = std::numeric_limits::max() / row_bytes; - if (image_rect.ysize() > max_rows) { - return JXL_FAILURE("Image too big."); - } - // TODO(eustas): drop frameRaw - JXL_RETURN_IF_ERROR( - ctx.frameRaw.Resize(row_bytes, image_rect.ysize())); - } - - if (!ctx.FeedChunks(chunk)) { - return JXL_FAILURE("Decoding IDAT failed"); - } - seen_pixel_data = true; - continue; - } - - case MakeTag('f', 'd', 'A', 'T'): { - if (!seen_idat) { - return JXL_FAILURE("fdAT chunk before IDAT"); - } - if (!seen_actl) { - return JXL_FAILURE("fdAT chunk before acTL"); - } - /* The 'fdAT' chunk has... the same structure as an 'IDAT' chunk, - * except preceded by a sequence number. */ - if (payload.size() < 4) { - return JXL_FAILURE("Corrupted fdAT chunk"); - } - // Turn 'fdAT' to 'IDAT' by cutting sequence number and replacing tag. - std::array preamble; - png_save_uint_32(preamble.data(), payload.size() - 4); - memcpy(preamble.data() + 4, "IDAT", 4); - // Cut-off 'size', 'type' and 'sequence_number' - Bytes chunk_tail(chunk.data() + 12, chunk.size() - 12); - if (!ctx.FeedChunks(Bytes(preamble), chunk_tail)) { - return JXL_FAILURE("Decoding fdAT failed"); - } - seen_pixel_data = true; - continue; - } - - case MakeTag('c', 'I', 'C', 'P'): - if (color_info_type == ColorInfoType::CICP) { - JXL_DEBUG_V(2, "Excessive colorspace definition; cICP chunk ignored"); - continue; - } - JXL_RETURN_IF_ERROR(DecodeCicpChunk(payload, &ppf->color_encoding)); - ppf->icc.clear(); - ppf->primary_color_representation = - PackedPixelFile::kColorEncodingIsPrimary; - color_info_type = ColorInfoType::CICP; - continue; - - case MakeTag('i', 'C', 'C', 'P'): { - if (color_info_type == ColorInfoType::ICCP_OR_SRGB) { - return JXL_FAILURE("Repeated iCCP / sRGB chunk"); - } - if (color_info_type > ColorInfoType::ICCP_OR_SRGB) { - JXL_DEBUG_V(2, "Excessive colorspace definition; iCCP chunk ignored"); - continue; - } - // Let PNG decoder deal with chunk processing. - if (!ctx.FeedChunks(chunk)) { - return JXL_FAILURE("Corrupt iCCP chunk"); - } - - // TODO(jon): catch special case of PQ and synthesize color encoding - // in that case - int compression_type = 0; - png_bytep profile = nullptr; - png_charp name = nullptr; - png_uint_32 profile_len = 0; - png_uint_32 ok = - png_get_iCCP(ctx.png_ptr, ctx.info_ptr, &name, &compression_type, - &profile, &profile_len); - if (!ok || !profile_len) { - return JXL_FAILURE("Malformed / incomplete iCCP chunk"); - } - ppf->icc.assign(profile, profile + profile_len); - ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - color_info_type = ColorInfoType::ICCP_OR_SRGB; - continue; - } - - case MakeTag('s', 'R', 'G', 'B'): - if (color_info_type == ColorInfoType::ICCP_OR_SRGB) { - return JXL_FAILURE("Repeated iCCP / sRGB chunk"); - } - if (color_info_type > ColorInfoType::ICCP_OR_SRGB) { - JXL_DEBUG_V(2, "Excessive colorspace definition; sRGB chunk ignored"); - continue; - } - JXL_RETURN_IF_ERROR(DecodeSrgbChunk(payload, &ppf->color_encoding)); - color_info_type = ColorInfoType::ICCP_OR_SRGB; - continue; - - case MakeTag('g', 'A', 'M', 'A'): - if (color_info_type >= ColorInfoType::GAMA_OR_CHRM) { - JXL_DEBUG_V(2, "Excessive colorspace definition; gAMA chunk ignored"); - continue; - } - JXL_RETURN_IF_ERROR(DecodeGamaChunk(payload, &ppf->color_encoding)); - color_info_type = ColorInfoType::GAMA_OR_CHRM; - continue; - - case MakeTag('c', 'H', 'R', 'M'): - if (color_info_type >= ColorInfoType::GAMA_OR_CHRM) { - JXL_DEBUG_V(2, "Excessive colorspace definition; cHRM chunk ignored"); - continue; - } - JXL_RETURN_IF_ERROR(DecodeChrmChunk(payload, &ppf->color_encoding)); - color_info_type = ColorInfoType::GAMA_OR_CHRM; - continue; - - case MakeTag('c', 'L', 'L', 'i'): - JXL_RETURN_IF_ERROR( - DecodeClliChunk(payload, &ppf->info.intensity_target)); - continue; - - case MakeTag('e', 'X', 'I', 'f'): - // TODO(eustas): next eXIF chunk overwrites current; is it ok? - ppf->metadata.exif.resize(payload.size()); - memcpy(ppf->metadata.exif.data(), payload.data(), payload.size()); - continue; - - default: - // We don't know what is that, just pass through. - if (!ctx.FeedChunks(chunk)) { - return JXL_FAILURE("PNG decoder failed to process chunk"); - } - // If it happens before IDAT, we consider it metadata and pass to all - // sub-decoders. - if (!seen_idat) { - passthrough_chunks.push_back(chunk); - } - continue; - } - } - - bool color_is_already_set = (color_info_type != ColorInfoType::NONE); - bool is_gray = (ppf->info.num_color_channels == 1); - JXL_RETURN_IF_ERROR( - ApplyColorHints(color_hints, color_is_already_set, is_gray, ppf)); - - if (ppf->color_encoding.transfer_function != JXL_TRANSFER_FUNCTION_PQ) { - // Reset intensity target, in case we set it from cLLi but TF is not PQ. - ppf->info.intensity_target = 0.f; - } - - bool has_nontrivial_background = false; - bool previous_frame_should_be_cleared = false; - for (size_t i = 0; i < frames.size(); i++) { - Frame& frame = frames[i]; - const FrameControl& fc = frame.metadata; - const RectT vp = fc.viewport; - const auto& pixels = frame.pixels; - size_t xsize = pixels.xsize; - size_t ysize = pixels.ysize; - JXL_ENSURE(xsize == vp.xsize()); - JXL_ENSURE(ysize == vp.ysize()); - - // Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with - // 0, so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent. - if (fc.dispose_op == DisposeOp::NONE) { - has_nontrivial_background = true; - } - bool should_blend = fc.blend_op == BlendOp::OVER; - bool use_for_next_frame = - has_nontrivial_background && fc.dispose_op != DisposeOp::PREVIOUS; - size_t x0 = vp.x0(); - size_t y0 = vp.y0(); - if (previous_frame_should_be_cleared) { - const auto& pvp = frames[i - 1].metadata.viewport; - size_t px0 = pvp.x0(); - size_t py0 = pvp.y0(); - size_t pxs = pvp.xsize(); - size_t pys = pvp.ysize(); - if (px0 >= x0 && py0 >= y0 && px0 + pxs <= x0 + xsize && - py0 + pys <= y0 + ysize && fc.blend_op == BlendOp::SOURCE && - use_for_next_frame) { - // If the previous frame is entirely contained in the current frame - // and we are using BLEND_OP_SOURCE, nothing special needs to be done. - ppf->frames.emplace_back(std::move(frame.pixels)); - } else if (px0 == x0 && py0 == y0 && px0 + pxs == x0 + xsize && - py0 + pys == y0 + ysize && use_for_next_frame) { - // If the new frame has the same size as the old one, but we are - // blending, we can instead just not blend. - should_blend = false; - ppf->frames.emplace_back(std::move(frame.pixels)); - } else if (px0 <= x0 && py0 <= y0 && px0 + pxs >= x0 + xsize && - py0 + pys >= y0 + ysize && use_for_next_frame) { - // If the new frame is contained within the old frame, we can pad the - // new frame with zeros and not blend. - JXL_ASSIGN_OR_RETURN(PackedImage new_data, - PackedImage::Create(pxs, pys, pixels.format)); - memset(new_data.pixels(), 0, new_data.pixels_size); - for (size_t y = 0; y < ysize; y++) { - JXL_RETURN_IF_ERROR( - PackedImage::ValidateDataType(new_data.format.data_type)); - size_t bytes_per_pixel = - PackedImage::BitsPerChannel(new_data.format.data_type) * - new_data.format.num_channels / 8; - memcpy( - static_cast(new_data.pixels()) + - new_data.stride * (y + y0 - py0) + - bytes_per_pixel * (x0 - px0), - static_cast(pixels.pixels()) + pixels.stride * y, - xsize * bytes_per_pixel); - } - - x0 = px0; - y0 = py0; - xsize = pxs; - ysize = pys; - should_blend = false; - ppf->frames.emplace_back(std::move(new_data)); - } else { - // If all else fails, insert a placeholder blank frame with kReplace. - JXL_ASSIGN_OR_RETURN(PackedImage blank, - PackedImage::Create(pxs, pys, pixels.format)); - memset(blank.pixels(), 0, blank.pixels_size); - ppf->frames.emplace_back(std::move(blank)); - auto& pframe = ppf->frames.back(); - pframe.frame_info.layer_info.crop_x0 = px0; - pframe.frame_info.layer_info.crop_y0 = py0; - pframe.frame_info.layer_info.xsize = pxs; - pframe.frame_info.layer_info.ysize = pys; - pframe.frame_info.duration = 0; - bool is_full_size = px0 == 0 && py0 == 0 && pxs == ppf->info.xsize && - pys == ppf->info.ysize; - pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1; - pframe.frame_info.layer_info.blend_info.blendmode = JXL_BLEND_REPLACE; - pframe.frame_info.layer_info.blend_info.source = 1; - pframe.frame_info.layer_info.save_as_reference = 1; - ppf->frames.emplace_back(std::move(frame.pixels)); - } - } else { - ppf->frames.emplace_back(std::move(frame.pixels)); - } - - auto& pframe = ppf->frames.back(); - pframe.frame_info.layer_info.crop_x0 = x0; - pframe.frame_info.layer_info.crop_y0 = y0; - pframe.frame_info.layer_info.xsize = xsize; - pframe.frame_info.layer_info.ysize = ysize; - pframe.frame_info.duration = - fc.delay_num * 1000 / (fc.delay_den ? fc.delay_den : 100); - pframe.frame_info.layer_info.blend_info.blendmode = - should_blend ? JXL_BLEND_BLEND : JXL_BLEND_REPLACE; - bool is_full_size = x0 == 0 && y0 == 0 && xsize == ppf->info.xsize && - ysize == ppf->info.ysize; - pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1; - pframe.frame_info.layer_info.blend_info.source = 1; - pframe.frame_info.layer_info.blend_info.alpha = 0; - pframe.frame_info.layer_info.save_as_reference = use_for_next_frame ? 1 : 0; - - previous_frame_should_be_cleared = - has_nontrivial_background && (fc.dispose_op == DisposeOp::BACKGROUND); - } - - if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded"); - ppf->frames.back().frame_info.is_last = JXL_TRUE; - - return true; -} - -#endif // JPEGXL_ENABLE_APNG - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.h b/third_party/jpeg-xl/lib/extras/dec/apng.h deleted file mode 100644 index 7ebc2ee7c8ae1..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/apng.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_APNG_H_ -#define LIB_EXTRAS_DEC_APNG_H_ - -// Decodes APNG images in memory. - -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -bool CanDecodeAPNG(); - -// Decodes `bytes` into `ppf`. -Status DecodeImageAPNG(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_APNG_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description.cc b/third_party/jpeg-xl/lib/extras/dec/color_description.cc deleted file mode 100644 index dd84455c41095..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/color_description.cc +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/color_description.h" - -#include - -#include - -#include "lib/jxl/base/common.h" - -namespace jxl { - -namespace { - -template -struct EnumName { - const char* name; - T value; -}; - -constexpr auto kJxlColorSpaceNames = - to_array>({{"RGB", JXL_COLOR_SPACE_RGB}, - {"Gra", JXL_COLOR_SPACE_GRAY}, - {"XYB", JXL_COLOR_SPACE_XYB}, - {"CS?", JXL_COLOR_SPACE_UNKNOWN}}); - -constexpr auto kJxlWhitePointNames = - to_array>({{"D65", JXL_WHITE_POINT_D65}, - {"Cst", JXL_WHITE_POINT_CUSTOM}, - {"EER", JXL_WHITE_POINT_E}, - {"DCI", JXL_WHITE_POINT_DCI}}); - -constexpr auto kJxlPrimariesNames = - to_array>({{"SRG", JXL_PRIMARIES_SRGB}, - {"Cst", JXL_PRIMARIES_CUSTOM}, - {"202", JXL_PRIMARIES_2100}, - {"DCI", JXL_PRIMARIES_P3}}); - -constexpr auto kJxlRenderingIntentNames = - to_array>( - {{"Per", JXL_RENDERING_INTENT_PERCEPTUAL}, - {"Rel", JXL_RENDERING_INTENT_RELATIVE}, - {"Sat", JXL_RENDERING_INTENT_SATURATION}, - {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}}); - -constexpr auto kJxlTransferFunctionNames = - to_array>( - {{"709", JXL_TRANSFER_FUNCTION_709}, - {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN}, - {"Lin", JXL_TRANSFER_FUNCTION_LINEAR}, - {"SRG", JXL_TRANSFER_FUNCTION_SRGB}, - {"PeQ", JXL_TRANSFER_FUNCTION_PQ}, - {"DCI", JXL_TRANSFER_FUNCTION_DCI}, - {"HLG", JXL_TRANSFER_FUNCTION_HLG}, - {"", JXL_TRANSFER_FUNCTION_GAMMA}}); - -template -Status ParseEnum(const std::string& token, - const std::array, N>& enum_values, T* value) { - for (size_t i = 0; i < enum_values.size(); i++) { - if (enum_values[i].name == token) { - *value = enum_values[i].value; - return true; - } - } - return false; -} - -class Tokenizer { - public: - Tokenizer(const std::string* input, char separator) - : input_(input), separator_(separator) {} - - Status Next(std::string* next) { - const size_t end = input_->find(separator_, start_); - if (end == std::string::npos) { - *next = input_->substr(start_); // rest of string - } else { - *next = input_->substr(start_, end - start_); - } - if (next->empty()) return JXL_FAILURE("Missing token"); - start_ = end + 1; - return true; - } - - private: - const std::string* const input_; // not owned - const char separator_; - size_t start_ = 0; // of next token -}; - -Status ParseDouble(const std::string& num, double* d) { - char* end; - errno = 0; - *d = strtod(num.c_str(), &end); - if (*d == 0.0 && end == num.c_str()) { - return JXL_FAILURE("Invalid double: %s", num.c_str()); - } - if (std::isnan(*d)) { - return JXL_FAILURE("Invalid double: %s", num.c_str()); - } - if (errno == ERANGE) { - return JXL_FAILURE("Double out of range: %s", num.c_str()); - } - return true; -} - -Status ParseDouble(Tokenizer* tokenizer, double* d) { - std::string num; - JXL_RETURN_IF_ERROR(tokenizer->Next(&num)); - return ParseDouble(num, d); -} - -Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) { - std::string str; - JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - JxlColorSpace cs; - if (ParseEnum(str, kJxlColorSpaceNames, &cs)) { - c->color_space = cs; - return true; - } - - return JXL_FAILURE("Unknown ColorSpace %s", str.c_str()); -} - -Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) { - if (c->color_space == JXL_COLOR_SPACE_XYB) { - // Implicit white point. - c->white_point = JXL_WHITE_POINT_D65; - return true; - } - - std::string str; - JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (ParseEnum(str, kJxlWhitePointNames, &c->white_point)) return true; - - Tokenizer xy_tokenizer(&str, ';'); - c->white_point = JXL_WHITE_POINT_CUSTOM; - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 0)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 1)); - return true; -} - -Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) { - if (c->color_space == JXL_COLOR_SPACE_GRAY || - c->color_space == JXL_COLOR_SPACE_XYB) { - // No primaries case. - return true; - } - - std::string str; - JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (ParseEnum(str, kJxlPrimariesNames, &c->primaries)) return true; - - Tokenizer xy_tokenizer(&str, ';'); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 1)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 0)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 1)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 0)); - JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 1)); - c->primaries = JXL_PRIMARIES_CUSTOM; - - return true; -} - -Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) { - std::string str; - JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (ParseEnum(str, kJxlRenderingIntentNames, &c->rendering_intent)) - return true; - - return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str()); -} - -Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) { - if (c->color_space == JXL_COLOR_SPACE_XYB) { - // Implicit TF. - c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - c->gamma = 1 / 3.; - return true; - } - - std::string str; - JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (ParseEnum(str, kJxlTransferFunctionNames, &c->transfer_function)) { - return true; - } - - if (str[0] == 'g') { - JXL_RETURN_IF_ERROR(ParseDouble(str.substr(1), &c->gamma)); - c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - return true; - } - - return JXL_FAILURE("Invalid gamma %s", str.c_str()); -} - -} // namespace - -Status ParseDescription(const std::string& description, JxlColorEncoding* c) { - *c = {}; - if (description == "sRGB") { - c->color_space = JXL_COLOR_SPACE_RGB; - c->white_point = JXL_WHITE_POINT_D65; - c->primaries = JXL_PRIMARIES_SRGB; - c->transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - c->rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; - } else if (description == "DisplayP3") { - c->color_space = JXL_COLOR_SPACE_RGB; - c->white_point = JXL_WHITE_POINT_D65; - c->primaries = JXL_PRIMARIES_P3; - c->transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - c->rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; - } else if (description == "Rec2100PQ") { - c->color_space = JXL_COLOR_SPACE_RGB; - c->white_point = JXL_WHITE_POINT_D65; - c->primaries = JXL_PRIMARIES_2100; - c->transfer_function = JXL_TRANSFER_FUNCTION_PQ; - c->rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - } else if (description == "Rec2100HLG") { - c->color_space = JXL_COLOR_SPACE_RGB; - c->white_point = JXL_WHITE_POINT_D65; - c->primaries = JXL_PRIMARIES_2100; - c->transfer_function = JXL_TRANSFER_FUNCTION_HLG; - c->rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - } else { - Tokenizer tokenizer(&description, '_'); - JXL_RETURN_IF_ERROR(ParseColorSpace(&tokenizer, c)); - JXL_RETURN_IF_ERROR(ParseWhitePoint(&tokenizer, c)); - JXL_RETURN_IF_ERROR(ParsePrimaries(&tokenizer, c)); - JXL_RETURN_IF_ERROR(ParseRenderingIntent(&tokenizer, c)); - JXL_RETURN_IF_ERROR(ParseTransferFunction(&tokenizer, c)); - } - return true; -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description.h b/third_party/jpeg-xl/lib/extras/dec/color_description.h deleted file mode 100644 index 23680ff7c6a24..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/color_description.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_COLOR_DESCRIPTION_H_ -#define LIB_EXTRAS_COLOR_DESCRIPTION_H_ - -#include - -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { - -// Parse the color description into a JxlColorEncoding "RGB_D65_SRG_Rel_Lin". -Status ParseDescription(const std::string& description, - JxlColorEncoding* JXL_RESTRICT c); - -} // namespace jxl - -#endif // LIB_EXTRAS_COLOR_DESCRIPTION_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description_test.cc b/third_party/jpeg-xl/lib/extras/dec/color_description_test.cc deleted file mode 100644 index e6e34f0edf061..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/color_description_test.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/color_description.h" - -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { - -// Verify ParseDescription(Description) yields the same ColorEncoding -TEST(ColorDescriptionTest, RoundTripAll) { - for (const auto& cdesc : test::AllEncodings()) { - const ColorEncoding c_original = test::ColorEncodingFromDescriptor(cdesc); - const std::string description = Description(c_original); - printf("%s\n", description.c_str()); - - JxlColorEncoding c_external = {}; - EXPECT_TRUE(ParseDescription(description, &c_external)); - ColorEncoding c_internal; - EXPECT_TRUE(c_internal.FromExternal(c_external)); - EXPECT_TRUE(c_original.SameColorEncoding(c_internal)) - << "Where c_original=" << c_original - << " and c_internal=" << c_internal; - } -} - -TEST(ColorDescriptionTest, NanGamma) { - const std::string description = "Gra_2_Per_gnan"; - JxlColorEncoding c; - EXPECT_FALSE(ParseDescription(description, &c)); -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc deleted file mode 100644 index b1edd9f6eabf4..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/color_hints.h" - -#include - -#include - -#include "lib/extras/dec/color_description.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -Status ApplyColorHints(const ColorHints& color_hints, - const bool color_already_set, const bool is_gray, - PackedPixelFile* ppf) { - bool got_color_space = color_already_set; - - JXL_RETURN_IF_ERROR(color_hints.Foreach( - [color_already_set, is_gray, ppf, &got_color_space]( - const std::string& key, const std::string& value) -> Status { - if (color_already_set && (key == "color_space" || key == "icc")) { - JXL_WARNING("Decoder ignoring %s hint", key.c_str()); - return true; - } - if (key == "color_space") { - JxlColorEncoding c_original_external; - if (!ParseDescription(value, &c_original_external)) { - return JXL_FAILURE("Failed to apply color_space"); - } - ppf->color_encoding = c_original_external; - - if (is_gray != - (ppf->color_encoding.color_space == JXL_COLOR_SPACE_GRAY)) { - return JXL_FAILURE("mismatch between file and color_space hint"); - } - - got_color_space = true; - } else if (key == "icc") { - const uint8_t* data = reinterpret_cast(value.data()); - std::vector icc(data, data + value.size()); - ppf->icc = std::move(icc); - ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - got_color_space = true; - } else if (key == "exif") { - const uint8_t* data = reinterpret_cast(value.data()); - std::vector blob(data, data + value.size()); - ppf->metadata.exif = std::move(blob); - } else if (key == "xmp") { - const uint8_t* data = reinterpret_cast(value.data()); - std::vector blob(data, data + value.size()); - ppf->metadata.xmp = std::move(blob); - } else if (key == "jumbf") { - const uint8_t* data = reinterpret_cast(value.data()); - std::vector blob(data, data + value.size()); - ppf->metadata.jumbf = std::move(blob); - } else { - JXL_WARNING("Ignoring %s hint", key.c_str()); - } - return true; - })); - - if (!got_color_space) { - ppf->color_encoding.color_space = - is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB; - ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; - ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; - ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - } - - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/color_hints.h b/third_party/jpeg-xl/lib/extras/dec/color_hints.h deleted file mode 100644 index 036f203e261b4..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/color_hints.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_COLOR_HINTS_H_ -#define LIB_EXTRAS_COLOR_HINTS_H_ - -// Not all the formats implemented in the extras lib support bundling color -// information into the file, and those that support it may not have it. -// To allow attaching color information to those file formats the caller can -// define these color hints. -// Besides color space information, 'ColorHints' may also include other -// additional information such as Exif, XMP and JUMBF metadata. - -#include -#include - -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -class ColorHints { - public: - // key=color_space, value=Description(c/pp): specify the ColorEncoding of - // the pixels for decoding. Otherwise, if the codec did not obtain an ICC - // profile from the image, assume sRGB. - // - // Strings are taken from the command line, so avoid spaces for convenience. - void Add(const std::string& key, const std::string& value) { - kv_.emplace_back(key, value); - } - - // Calls `func(key, value)` for each key/value in the order they were added, - // returning false immediately if `func` returns false. - template - Status Foreach(const Func& func) const { - for (const KeyValue& kv : kv_) { - Status ok = func(kv.key, kv.value); - if (!ok) { - return JXL_FAILURE("ColorHints::Foreach returned false"); - } - } - return true; - } - - private: - // Splitting into key/value avoids parsing in each codec. - struct KeyValue { - KeyValue(std::string key, std::string value) - : key(std::move(key)), value(std::move(value)) {} - - std::string key; - std::string value; - }; - - std::vector kv_; -}; - -// Apply the color hints to the decoded image in PackedPixelFile if any. -// color_already_set tells whether the color encoding was already set, in which -// case the hints are ignored if any hint is passed. -Status ApplyColorHints(const ColorHints& color_hints, bool color_already_set, - bool is_gray, PackedPixelFile* ppf); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_COLOR_HINTS_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.cc b/third_party/jpeg-xl/lib/extras/dec/decode.cc deleted file mode 100644 index 2581d53f633c0..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/decode.cc +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/decode.h" - -#include - -#include "lib/extras/dec/apng.h" -#include "lib/extras/dec/exr.h" -#include "lib/extras/dec/gif.h" -#include "lib/extras/dec/jpg.h" -#include "lib/extras/dec/jxl.h" -#include "lib/extras/dec/pgx.h" -#include "lib/extras/dec/pnm.h" - -namespace jxl { -namespace extras { -namespace { - -// Any valid encoding is larger (ensures codecs can read the first few bytes) -constexpr size_t kMinBytes = 9; - -std::string GetExtension(const std::string& path) { - // Pattern: "name.png" - size_t pos = path.find_last_of('.'); - if (pos != std::string::npos) { - return path.substr(pos); - } - - // Extension not found - return ""; -} - -} // namespace - -Codec CodecFromPath(const std::string& path, - size_t* JXL_RESTRICT bits_per_sample, - std::string* extension) { - std::string ext = GetExtension(path); - if (extension) { - if (extension->empty()) { - *extension = ext; - } else { - ext = *extension; - } - } - std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { - return std::tolower(c, std::locale::classic()); - }); - if (ext == ".png") return Codec::kPNG; - - if (ext == ".jpg") return Codec::kJPG; - if (ext == ".jpeg") return Codec::kJPG; - - if (ext == ".pgx") return Codec::kPGX; - - if (ext == ".pam") return Codec::kPNM; - if (ext == ".pnm") return Codec::kPNM; - if (ext == ".pgm") return Codec::kPNM; - if (ext == ".ppm") return Codec::kPNM; - if (ext == ".pfm") { - if (bits_per_sample != nullptr) *bits_per_sample = 32; - return Codec::kPNM; - } - - if (ext == ".gif") return Codec::kGIF; - - if (ext == ".exr") return Codec::kEXR; - - return Codec::kUnknown; -} - -bool CanDecode(Codec codec) { - switch (codec) { - case Codec::kEXR: - return CanDecodeEXR(); - case Codec::kGIF: - return CanDecodeGIF(); - case Codec::kJPG: - return CanDecodeJPG(); - case Codec::kPNG: - return CanDecodeAPNG(); - case Codec::kPNM: - case Codec::kPGX: - case Codec::kJXL: - return true; - default: - return false; - } -} - -std::string ListOfDecodeCodecs() { - std::string list_of_codecs("JXL, PPM, PNM, PFM, PAM, PGX"); - if (CanDecode(Codec::kPNG)) list_of_codecs.append(", PNG, APNG"); - if (CanDecode(Codec::kGIF)) list_of_codecs.append(", GIF"); - if (CanDecode(Codec::kJPG)) list_of_codecs.append(", JPEG"); - if (CanDecode(Codec::kEXR)) list_of_codecs.append(", EXR"); - return list_of_codecs; -} - -Status DecodeBytes(const Span bytes, - const ColorHints& color_hints, extras::PackedPixelFile* ppf, - const SizeConstraints* constraints, Codec* orig_codec) { - if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes"); - - *ppf = extras::PackedPixelFile(); - - // Default values when not set by decoders. - ppf->info.uses_original_profile = JXL_TRUE; - ppf->info.orientation = JXL_ORIENT_IDENTITY; - - const auto choose_codec = [&]() -> Codec { - if (DecodeImageAPNG(bytes, color_hints, ppf, constraints)) { - return Codec::kPNG; - } - if (DecodeImagePGX(bytes, color_hints, ppf, constraints)) { - return Codec::kPGX; - } - if (DecodeImagePNM(bytes, color_hints, ppf, constraints)) { - return Codec::kPNM; - } - JXLDecompressParams dparams = {}; - for (const uint32_t num_channels : {1, 2, 3, 4}) { - dparams.accepted_formats.push_back( - {num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0}); - } - dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; - size_t decoded_bytes; - if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes, - ppf) && - ApplyColorHints(color_hints, true, ppf->info.num_color_channels == 1, - ppf)) { - return Codec::kJXL; - } - if (DecodeImageGIF(bytes, color_hints, ppf, constraints)) { - return Codec::kGIF; - } - if (DecodeImageJPG(bytes, color_hints, ppf, constraints)) { - return Codec::kJPG; - } - if (DecodeImageEXR(bytes, color_hints, ppf, constraints)) { - return Codec::kEXR; - } - return Codec::kUnknown; - }; - - Codec codec = choose_codec(); - if (codec == Codec::kUnknown) { - return JXL_FAILURE("Codecs failed to decode"); - } - if (orig_codec) *orig_codec = codec; - - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.h b/third_party/jpeg-xl/lib/extras/dec/decode.h deleted file mode 100644 index bbffc7e0f0a33..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/decode.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_DECODE_H_ -#define LIB_EXTRAS_DEC_DECODE_H_ - -// Facade for image decoders (PNG, PNM, ...). - -#include -#include -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -// Codecs supported by DecodeBytes. -enum class Codec : uint32_t { - kUnknown, // for CodecFromPath - kPNG, - kPNM, - kPGX, - kJPG, - kGIF, - kEXR, - kJXL -}; - -bool CanDecode(Codec codec); - -std::string ListOfDecodeCodecs(); - -// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so -// that Encode() would encode to PFM instead of PPM. -Codec CodecFromPath(const std::string& path, - size_t* JXL_RESTRICT bits_per_sample = nullptr, - std::string* extension = nullptr); - -// Decodes "bytes" info *ppf. -// color_space_hint may specify the color space, otherwise, defaults to sRGB. -Status DecodeBytes(Span bytes, const ColorHints& color_hints, - extras::PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr, - Codec* orig_codec = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_DECODE_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/exr.cc b/third_party/jpeg-xl/lib/extras/dec/exr.cc deleted file mode 100644 index 59edd63eb863f..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/exr.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/exr.h" - -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -#if !JPEGXL_ENABLE_EXR - -namespace jxl { -namespace extras { -bool CanDecodeEXR() { return false; } - -Status DecodeImageEXR(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints) { - (void)bytes; - (void)color_hints; - (void)ppf; - (void)constraints; - return JXL_FAILURE("EXR is not supported"); -} -} // namespace extras -} // namespace jxl - -#else // JPEGXL_ENABLE_EXR - -#include -#include -#include -#include - -#include - -#ifdef __EXCEPTIONS -#include -#define JXL_EXR_THROW_LENGTH_ERROR() throw std::length_error(""); -#else // __EXCEPTIONS -#define JXL_EXR_THROW_LENGTH_ERROR() JXL_CRASH() -#endif // __EXCEPTIONS - -namespace jxl { -namespace extras { - -namespace { - -namespace OpenEXR = OPENEXR_IMF_NAMESPACE; - -// OpenEXR::Int64 is deprecated in favor of using uint64_t directly, but using -// uint64_t as recommended causes build failures with previous OpenEXR versions -// on macOS, where the definition for OpenEXR::Int64 was actually not equivalent -// to uint64_t. This alternative should work in all cases. -using ExrInt64 = decltype(std::declval().tellg()); - -constexpr int kExrBitsPerSample = 16; -constexpr int kExrAlphaBits = 16; - -class InMemoryIStream : public OpenEXR::IStream { - public: - // The data pointed to by `bytes` must outlive the InMemoryIStream. - explicit InMemoryIStream(const Span bytes) - : IStream(/*fileName=*/""), bytes_(bytes) {} - - bool isMemoryMapped() const override { return true; } - char* readMemoryMapped(const int n) override { - if (pos_ + n < pos_ || pos_ + n > bytes_.size()) { - JXL_EXR_THROW_LENGTH_ERROR(); - } - char* const result = - const_cast(reinterpret_cast(bytes_.data() + pos_)); - pos_ += n; - return result; - } - bool read(char c[], const int n) override { - std::copy_n(readMemoryMapped(n), n, c); - return pos_ < bytes_.size(); - } - - ExrInt64 tellg() override { return pos_; } - void seekg(const ExrInt64 pos) override { - if (pos >= bytes_.size()) { - JXL_EXR_THROW_LENGTH_ERROR(); - } - pos_ = pos; - } - - private: - const Span bytes_; - size_t pos_ = 0; -}; - -} // namespace - -bool CanDecodeEXR() { return true; } - -Status DecodeImageEXR(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints) { - InMemoryIStream is(bytes); - -#ifdef __EXCEPTIONS - std::unique_ptr input_ptr; - try { - input_ptr = jxl::make_unique(is); - } catch (...) { - // silently return false if it is not an EXR file - return false; - } - OpenEXR::RgbaInputFile& input = *input_ptr; -#else - OpenEXR::RgbaInputFile input(is); -#endif - - if ((input.channels() & OpenEXR::RgbaChannels::WRITE_RGB) != - OpenEXR::RgbaChannels::WRITE_RGB) { - return JXL_FAILURE("only RGB OpenEXR files are supported"); - } - const bool has_alpha = (input.channels() & OpenEXR::RgbaChannels::WRITE_A) == - OpenEXR::RgbaChannels::WRITE_A; - - const float intensity_target = OpenEXR::hasWhiteLuminance(input.header()) - ? OpenEXR::whiteLuminance(input.header()) - : 0; - - auto image_size = input.displayWindow().size(); - // Size is computed as max - min, but both bounds are inclusive. - ++image_size.x; - ++image_size.y; - - ppf->info.xsize = image_size.x; - ppf->info.ysize = image_size.y; - ppf->info.num_color_channels = 3; - - const JxlDataType data_type = - kExrBitsPerSample == 16 ? JXL_TYPE_FLOAT16 : JXL_TYPE_FLOAT; - const JxlPixelFormat format{ - /*num_channels=*/3u + (has_alpha ? 1u : 0u), - /*data_type=*/data_type, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0, - }; - ppf->frames.clear(); - // Allocates the frame buffer. - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(image_size.x, image_size.y, format)); - ppf->frames.emplace_back(std::move(frame)); - } - const auto& frame = ppf->frames.back(); - - const int row_size = input.dataWindow().size().x + 1; - // Number of rows to read at a time. - // https://www.openexr.com/documentation/ReadingAndWritingImageFiles.pdf - // recommends reading the whole file at once. - const int y_chunk_size = input.displayWindow().size().y + 1; - std::vector input_rows(row_size * y_chunk_size); - for (int start_y = - std::max(input.dataWindow().min.y, input.displayWindow().min.y); - start_y <= - std::min(input.dataWindow().max.y, input.displayWindow().max.y); - start_y += y_chunk_size) { - // Inclusive. - const int end_y = std::min( - start_y + y_chunk_size - 1, - std::min(input.dataWindow().max.y, input.displayWindow().max.y)); - input.setFrameBuffer( - input_rows.data() - input.dataWindow().min.x - start_y * row_size, - /*xStride=*/1, /*yStride=*/row_size); - input.readPixels(start_y, end_y); - for (int exr_y = start_y; exr_y <= end_y; ++exr_y) { - const int image_y = exr_y - input.displayWindow().min.y; - const OpenEXR::Rgba* const JXL_RESTRICT input_row = - &input_rows[(exr_y - start_y) * row_size]; - uint8_t* row = static_cast(frame.color.pixels()) + - frame.color.stride * image_y; - const uint32_t pixel_size = - (3 + (has_alpha ? 1 : 0)) * kExrBitsPerSample / 8; - for (int exr_x = - std::max(input.dataWindow().min.x, input.displayWindow().min.x); - exr_x <= - std::min(input.dataWindow().max.x, input.displayWindow().max.x); - ++exr_x) { - const int image_x = exr_x - input.displayWindow().min.x; - // TODO(eustas): UB: OpenEXR::Rgba is not TriviallyCopyable - memcpy(row + image_x * pixel_size, - input_row + (exr_x - input.dataWindow().min.x), pixel_size); - } - } - } - - ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; - ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; - ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; - if (OpenEXR::hasChromaticities(input.header())) { - ppf->color_encoding.primaries = JXL_PRIMARIES_CUSTOM; - ppf->color_encoding.white_point = JXL_WHITE_POINT_CUSTOM; - const auto& chromaticities = OpenEXR::chromaticities(input.header()); - ppf->color_encoding.primaries_red_xy[0] = chromaticities.red.x; - ppf->color_encoding.primaries_red_xy[1] = chromaticities.red.y; - ppf->color_encoding.primaries_green_xy[0] = chromaticities.green.x; - ppf->color_encoding.primaries_green_xy[1] = chromaticities.green.y; - ppf->color_encoding.primaries_blue_xy[0] = chromaticities.blue.x; - ppf->color_encoding.primaries_blue_xy[1] = chromaticities.blue.y; - ppf->color_encoding.white_point_xy[0] = chromaticities.white.x; - ppf->color_encoding.white_point_xy[1] = chromaticities.white.y; - } - - // EXR uses binary16 or binary32 floating point format. - ppf->info.bits_per_sample = kExrBitsPerSample; - ppf->info.exponent_bits_per_sample = kExrBitsPerSample == 16 ? 5 : 8; - if (has_alpha) { - ppf->info.alpha_bits = kExrAlphaBits; - ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = JXL_TRUE; - } - ppf->info.intensity_target = intensity_target; - return true; -} - -} // namespace extras -} // namespace jxl - -#endif // JPEGXL_ENABLE_EXR diff --git a/third_party/jpeg-xl/lib/extras/dec/exr.h b/third_party/jpeg-xl/lib/extras/dec/exr.h deleted file mode 100644 index 65078d030ff97..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/exr.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_EXR_H_ -#define LIB_EXTRAS_DEC_EXR_H_ - -// Decodes OpenEXR images in memory. - -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -bool CanDecodeEXR(); - -// Decodes `bytes` into `ppf`. color_hints are ignored. -Status DecodeImageEXR(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_EXR_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.cc b/third_party/jpeg-xl/lib/extras/dec/gif.cc deleted file mode 100644 index a87beb901acf7..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/gif.cc +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/gif.h" - -#include "lib/jxl/base/status.h" - -#if JPEGXL_ENABLE_GIF -#include -#endif -#include - -#include -#include -#include -#include - -#include "lib/extras/size_constraints.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/sanitizers.h" - -namespace jxl { -namespace extras { - -#if JPEGXL_ENABLE_GIF -namespace { - -struct ReadState { - Span bytes; -}; - -struct DGifCloser { - void operator()(GifFileType* const ptr) const { DGifCloseFile(ptr, nullptr); } -}; -using GifUniquePtr = std::unique_ptr; - -struct PackedRgba { - uint8_t r, g, b, a; -}; - -struct PackedRgb { - uint8_t r, g, b; -}; - -Status ensure_have_alpha(PackedFrame* frame) { - if (!frame->extra_channels.empty()) return true; - const JxlPixelFormat alpha_format{ - /*num_channels=*/1u, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0, - }; - JXL_ASSIGN_OR_RETURN(PackedImage image, - PackedImage::Create(frame->color.xsize, - frame->color.ysize, alpha_format)); - frame->extra_channels.emplace_back(std::move(image)); - // We need to set opaque-by-default. - std::fill_n(static_cast(frame->extra_channels[0].pixels()), - frame->color.xsize * frame->color.ysize, 255u); - return true; -} -} // namespace -#endif - -bool CanDecodeGIF() { -#if JPEGXL_ENABLE_GIF - return true; -#else - return false; -#endif -} - -Status DecodeImageGIF(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints) { -#if JPEGXL_ENABLE_GIF - int error = GIF_OK; - ReadState state = {bytes}; - const auto ReadFromSpan = [](GifFileType* const gif, GifByteType* const bytes, - int n) { - ReadState* const state = reinterpret_cast(gif->UserData); - // giflib API requires the input size `n` to be signed int. - if (static_cast(n) > state->bytes.size()) { - n = state->bytes.size(); - } - memcpy(bytes, state->bytes.data(), n); - if (!state->bytes.remove_prefix(n)) return 0; - return n; - }; - GifUniquePtr gif(DGifOpen(&state, ReadFromSpan, &error)); - if (gif == nullptr) { - if (error == D_GIF_ERR_NOT_GIF_FILE) { - // Not an error. - return false; - } else { - return JXL_FAILURE("Failed to read GIF: %s", GifErrorString(error)); - } - } - error = DGifSlurp(gif.get()); - if (error != GIF_OK) { - return JXL_FAILURE("Failed to read GIF: %s", GifErrorString(gif->Error)); - } - - msan::UnpoisonMemory(gif.get(), sizeof(*gif)); - if (gif->SColorMap) { - msan::UnpoisonMemory(gif->SColorMap, sizeof(*gif->SColorMap)); - msan::UnpoisonMemory( - gif->SColorMap->Colors, - sizeof(*gif->SColorMap->Colors) * gif->SColorMap->ColorCount); - } - msan::UnpoisonMemory(gif->SavedImages, - sizeof(*gif->SavedImages) * gif->ImageCount); - - JXL_RETURN_IF_ERROR( - VerifyDimensions(constraints, gif->SWidth, gif->SHeight)); - uint64_t total_pixel_count = - static_cast(gif->SWidth) * gif->SHeight; - for (int i = 0; i < gif->ImageCount; ++i) { - const SavedImage& image = gif->SavedImages[i]; - uint32_t w = image.ImageDesc.Width; - uint32_t h = image.ImageDesc.Height; - JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, w, h)); - uint64_t pixel_count = static_cast(w) * h; - if (total_pixel_count + pixel_count < total_pixel_count) { - return JXL_FAILURE("Image too big"); - } - total_pixel_count += pixel_count; - if (constraints && (total_pixel_count > constraints->dec_max_pixels)) { - return JXL_FAILURE("Image too big"); - } - } - - if (!gif->SColorMap) { - for (int i = 0; i < gif->ImageCount; ++i) { - if (!gif->SavedImages[i].ImageDesc.ColorMap) { - return JXL_FAILURE("Missing GIF color map"); - } - } - } - - if (gif->ImageCount > 1) { - ppf->info.have_animation = JXL_TRUE; - // Delays in GIF are specified in censiseconds. - ppf->info.animation.tps_numerator = 100; - ppf->info.animation.tps_denominator = 1; - } - - ppf->frames.clear(); - ppf->frames.reserve(gif->ImageCount); - - ppf->info.xsize = gif->SWidth; - ppf->info.ysize = gif->SHeight; - ppf->info.bits_per_sample = 8; - ppf->info.exponent_bits_per_sample = 0; - // alpha_bits is later set to 8 if we find a frame with transparent pixels. - ppf->info.alpha_bits = 0; - ppf->info.alpha_exponent_bits = 0; - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false, - /*is_gray=*/false, ppf)); - - ppf->info.num_color_channels = 3; - - // Pixel format for the 'canvas' onto which we paint - // the (potentially individually cropped) GIF frames - // of an animation. - const JxlPixelFormat canvas_format{ - /*num_channels=*/4u, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0, - }; - - // Pixel format for the JXL PackedFrame that goes into the - // PackedPixelFile. Here, we use 3 color channels, and provide - // the alpha channel as an extra_channel wherever it is used. - const JxlPixelFormat packed_frame_format{ - /*num_channels=*/3u, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0, - }; - - GifColorType background_color; - if (gif->SColorMap == nullptr || - gif->SBackGroundColor >= gif->SColorMap->ColorCount) { - background_color = {0, 0, 0}; - } else { - background_color = gif->SColorMap->Colors[gif->SBackGroundColor]; - } - const PackedRgba background_rgba{background_color.Red, background_color.Green, - background_color.Blue, 0}; - JXL_ASSIGN_OR_RETURN( - PackedFrame canvas, - PackedFrame::Create(gif->SWidth, gif->SHeight, canvas_format)); - std::fill_n(static_cast(canvas.color.pixels()), - canvas.color.xsize * canvas.color.ysize, background_rgba); - Rect canvas_rect{0, 0, canvas.color.xsize, canvas.color.ysize}; - - Rect previous_rect_if_restore_to_background; - - bool replace = true; - bool last_base_was_none = true; - for (int i = 0; i < gif->ImageCount; ++i) { - const SavedImage& image = gif->SavedImages[i]; - msan::UnpoisonMemory(image.RasterBits, sizeof(*image.RasterBits) * - image.ImageDesc.Width * - image.ImageDesc.Height); - const Rect image_rect(image.ImageDesc.Left, image.ImageDesc.Top, - image.ImageDesc.Width, image.ImageDesc.Height); - - Rect total_rect; - if (previous_rect_if_restore_to_background.xsize() != 0 || - previous_rect_if_restore_to_background.ysize() != 0) { - const size_t xbegin = std::min( - image_rect.x0(), previous_rect_if_restore_to_background.x0()); - const size_t ybegin = std::min( - image_rect.y0(), previous_rect_if_restore_to_background.y0()); - const size_t xend = - std::max(image_rect.x0() + image_rect.xsize(), - previous_rect_if_restore_to_background.x0() + - previous_rect_if_restore_to_background.xsize()); - const size_t yend = - std::max(image_rect.y0() + image_rect.ysize(), - previous_rect_if_restore_to_background.y0() + - previous_rect_if_restore_to_background.ysize()); - total_rect = Rect(xbegin, ybegin, xend - xbegin, yend - ybegin); - previous_rect_if_restore_to_background = Rect(); - replace = true; - } else { - total_rect = image_rect; - replace = false; - } - if (!image_rect.IsInside(canvas_rect)) { - return JXL_FAILURE("GIF frame extends outside of the canvas"); - } - - // Allocates the frame buffer. - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(total_rect.xsize(), total_rect.ysize(), - packed_frame_format)); - ppf->frames.emplace_back(std::move(frame)); - } - - PackedFrame* frame = &ppf->frames.back(); - - // We cannot tell right from the start whether there will be a - // need for an alpha channel. This is discovered only as soon as - // we see a transparent pixel. We hence initialize alpha lazily. - auto set_pixel_alpha = [&frame](size_t x, size_t y, uint8_t a) -> Status { - // If we do not have an alpha-channel and a==255 (fully opaque), - // we can skip setting this pixel-value and rely on - // "no alpha channel = no transparency". - if (a == 255 && !frame->extra_channels.empty()) return true; - JXL_RETURN_IF_ERROR(ensure_have_alpha(frame)); - static_cast( - frame->extra_channels[0].pixels())[y * frame->color.xsize + x] = a; - return true; - }; - - const ColorMapObject* const color_map = - image.ImageDesc.ColorMap ? image.ImageDesc.ColorMap : gif->SColorMap; - JXL_ENSURE(color_map); - msan::UnpoisonMemory(color_map, sizeof(*color_map)); - msan::UnpoisonMemory(color_map->Colors, - sizeof(*color_map->Colors) * color_map->ColorCount); - GraphicsControlBlock gcb; - DGifSavedExtensionToGCB(gif.get(), i, &gcb); - msan::UnpoisonMemory(&gcb, sizeof(gcb)); - bool is_full_size = total_rect.x0() == 0 && total_rect.y0() == 0 && - total_rect.xsize() == canvas.color.xsize && - total_rect.ysize() == canvas.color.ysize; - if (ppf->info.have_animation) { - frame->frame_info.duration = gcb.DelayTime; - frame->frame_info.layer_info.have_crop = static_cast(!is_full_size); - frame->frame_info.layer_info.crop_x0 = total_rect.x0(); - frame->frame_info.layer_info.crop_y0 = total_rect.y0(); - frame->frame_info.layer_info.xsize = frame->color.xsize; - frame->frame_info.layer_info.ysize = frame->color.ysize; - if (last_base_was_none) { - replace = true; - } - frame->frame_info.layer_info.blend_info.blendmode = - replace ? JXL_BLEND_REPLACE : JXL_BLEND_BLEND; - // We always only reference at most the last frame - frame->frame_info.layer_info.blend_info.source = - last_base_was_none ? 0u : 1u; - frame->frame_info.layer_info.blend_info.clamp = 1; - frame->frame_info.layer_info.blend_info.alpha = 0; - // TODO(veluca): this could in principle be implemented. - if (last_base_was_none && - (total_rect.x0() != 0 || total_rect.y0() != 0 || - total_rect.xsize() != canvas.color.xsize || - total_rect.ysize() != canvas.color.ysize || !replace)) { - return JXL_FAILURE( - "GIF with dispose-to-0 is not supported for non-full or " - "blended frames"); - } - switch (gcb.DisposalMode) { - case DISPOSE_DO_NOT: - case DISPOSE_BACKGROUND: - frame->frame_info.layer_info.save_as_reference = 1u; - last_base_was_none = false; - break; - case DISPOSE_PREVIOUS: - frame->frame_info.layer_info.save_as_reference = 0u; - break; - default: - frame->frame_info.layer_info.save_as_reference = 0u; - last_base_was_none = true; - } - } - - // Update the canvas by creating a copy first. - JXL_ASSIGN_OR_RETURN( - PackedImage new_canvas_image, - PackedImage::Create(canvas.color.xsize, canvas.color.ysize, - canvas.color.format)); - memcpy(new_canvas_image.pixels(), canvas.color.pixels(), - new_canvas_image.pixels_size); - for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) { - // Assumes format.align == 0. row points to the beginning of the y row in - // the image_rect. - PackedRgba* row = static_cast(new_canvas_image.pixels()) + - (y + image_rect.y0()) * new_canvas_image.xsize + - image_rect.x0(); - for (size_t x = 0; x < image_rect.xsize(); ++x, ++byte_index) { - const GifByteType byte = image.RasterBits[byte_index]; - if (byte >= color_map->ColorCount) { - return JXL_FAILURE("GIF color is out of bounds"); - } - - if (byte == gcb.TransparentColor) continue; - GifColorType color = color_map->Colors[byte]; - row[x].r = color.Red; - row[x].g = color.Green; - row[x].b = color.Blue; - row[x].a = 255; - } - } - const PackedImage& sub_frame_image = frame->color; - if (replace) { - // Copy from the new canvas image to the subframe - for (size_t y = 0; y < total_rect.ysize(); ++y) { - const PackedRgba* row_in = - static_cast(new_canvas_image.pixels()) + - (y + total_rect.y0()) * new_canvas_image.xsize + total_rect.x0(); - PackedRgb* row_out = static_cast(sub_frame_image.pixels()) + - y * sub_frame_image.xsize; - for (size_t x = 0; x < sub_frame_image.xsize; ++x) { - row_out[x].r = row_in[x].r; - row_out[x].g = row_in[x].g; - row_out[x].b = row_in[x].b; - JXL_RETURN_IF_ERROR(set_pixel_alpha(x, y, row_in[x].a)); - } - } - } else { - for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) { - // Assumes format.align == 0 - PackedRgb* row = static_cast(sub_frame_image.pixels()) + - y * sub_frame_image.xsize; - for (size_t x = 0; x < image_rect.xsize(); ++x, ++byte_index) { - const GifByteType byte = image.RasterBits[byte_index]; - if (byte > color_map->ColorCount) { - return JXL_FAILURE("GIF color is out of bounds"); - } - if (byte == gcb.TransparentColor) { - row[x].r = 0; - row[x].g = 0; - row[x].b = 0; - JXL_RETURN_IF_ERROR(set_pixel_alpha(x, y, 0)); - continue; - } - GifColorType color = color_map->Colors[byte]; - row[x].r = color.Red; - row[x].g = color.Green; - row[x].b = color.Blue; - JXL_RETURN_IF_ERROR(set_pixel_alpha(x, y, 255)); - } - } - } - - if (!frame->extra_channels.empty()) { - ppf->info.alpha_bits = 8; - } - - switch (gcb.DisposalMode) { - case DISPOSE_DO_NOT: - canvas.color = std::move(new_canvas_image); - break; - - case DISPOSE_BACKGROUND: - std::fill_n(static_cast(canvas.color.pixels()), - canvas.color.xsize * canvas.color.ysize, background_rgba); - previous_rect_if_restore_to_background = image_rect; - break; - - case DISPOSE_PREVIOUS: - break; - - case DISPOSAL_UNSPECIFIED: - default: - std::fill_n(static_cast(canvas.color.pixels()), - canvas.color.xsize * canvas.color.ysize, background_rgba); - } - } - // Finally, if any frame has an alpha-channel, every frame will need - // to have an alpha-channel. - bool seen_alpha = false; - for (const PackedFrame& frame : ppf->frames) { - if (!frame.extra_channels.empty()) { - seen_alpha = true; - break; - } - } - if (seen_alpha) { - for (PackedFrame& frame : ppf->frames) { - JXL_RETURN_IF_ERROR(ensure_have_alpha(&frame)); - } - } - return true; -#else - return false; -#endif -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.h b/third_party/jpeg-xl/lib/extras/dec/gif.h deleted file mode 100644 index 4d5be8664e720..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/gif.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_GIF_H_ -#define LIB_EXTRAS_DEC_GIF_H_ - -// Decodes GIF images in memory. - -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -bool CanDecodeGIF(); - -// Decodes `bytes` into `ppf`. color_hints are ignored. -Status DecodeImageGIF(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_GIF_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc deleted file mode 100644 index c307105139173..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/jpegli.h" - -#include - -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/sanitizers.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -namespace { - -constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, - 0x66, 0x00, 0x00}; -constexpr int kExifMarker = JPEG_APP0 + 1; -constexpr int kICCMarker = JPEG_APP0 + 2; - -inline bool IsJPG(const std::vector& bytes) { - if (bytes.size() < 2) return false; - if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; - return true; -} - -bool MarkerIsExif(const jpeg_saved_marker_ptr marker) { - return marker->marker == kExifMarker && - marker->data_length >= sizeof kExifSignature + 2 && - std::equal(std::begin(kExifSignature), std::end(kExifSignature), - marker->data); -} - -Status ReadICCProfile(jpeg_decompress_struct* const cinfo, - std::vector* const icc) { - uint8_t* icc_data_ptr; - unsigned int icc_data_len; - if (jpegli_read_icc_profile(cinfo, &icc_data_ptr, &icc_data_len)) { - icc->assign(icc_data_ptr, icc_data_ptr + icc_data_len); - free(icc_data_ptr); - return true; - } - return false; -} - -void ReadExif(jpeg_decompress_struct* const cinfo, - std::vector* const exif) { - constexpr size_t kExifSignatureSize = sizeof kExifSignature; - for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; - marker = marker->next) { - // marker is initialized by libjpeg, which we are not instrumenting with - // msan. - msan::UnpoisonMemory(marker, sizeof(*marker)); - msan::UnpoisonMemory(marker->data, marker->data_length); - if (!MarkerIsExif(marker)) continue; - size_t marker_length = marker->data_length - kExifSignatureSize; - exif->resize(marker_length); - std::copy_n(marker->data + kExifSignatureSize, marker_length, exif->data()); - return; - } -} - -JpegliDataType ConvertDataType(JxlDataType type) { - switch (type) { - case JXL_TYPE_UINT8: - return JPEGLI_TYPE_UINT8; - case JXL_TYPE_UINT16: - return JPEGLI_TYPE_UINT16; - case JXL_TYPE_FLOAT: - return JPEGLI_TYPE_FLOAT; - default: - return JPEGLI_TYPE_UINT8; - } -} - -JpegliEndianness ConvertEndianness(JxlEndianness type) { - switch (type) { - case JXL_NATIVE_ENDIAN: - return JPEGLI_NATIVE_ENDIAN; - case JXL_BIG_ENDIAN: - return JPEGLI_BIG_ENDIAN; - case JXL_LITTLE_ENDIAN: - return JPEGLI_LITTLE_ENDIAN; - default: - return JPEGLI_NATIVE_ENDIAN; - } -} - -JxlColorSpace ConvertColorSpace(J_COLOR_SPACE colorspace) { - switch (colorspace) { - case JCS_GRAYSCALE: - return JXL_COLOR_SPACE_GRAY; - case JCS_RGB: - return JXL_COLOR_SPACE_RGB; - default: - return JXL_COLOR_SPACE_UNKNOWN; - } -} - -void MyErrorExit(j_common_ptr cinfo) { - jmp_buf* env = static_cast(cinfo->client_data); - (*cinfo->err->output_message)(cinfo); - jpegli_destroy_decompress(reinterpret_cast(cinfo)); - longjmp(*env, 1); -} - -void MyOutputMessage(j_common_ptr cinfo) { - if (JXL_IS_DEBUG_BUILD) { - char buf[JMSG_LENGTH_MAX + 1]; - (*cinfo->err->format_message)(cinfo, buf); - buf[JMSG_LENGTH_MAX] = 0; - JXL_WARNING("%s", buf); - } -} - -Status UnmapColors(uint8_t* row, size_t xsize, int components, - JSAMPARRAY colormap, size_t num_colors) { - JXL_ENSURE(colormap != nullptr); - std::vector tmp(xsize * components); - for (size_t x = 0; x < xsize; ++x) { - JXL_ENSURE(row[x] < num_colors); - for (int c = 0; c < components; ++c) { - tmp[x * components + c] = colormap[c][row[x]]; - } - } - memcpy(row, tmp.data(), tmp.size()); - return true; -} - -} // namespace - -Status DecodeJpeg(const std::vector& compressed, - const JpegDecompressParams& dparams, ThreadPool* pool, - PackedPixelFile* ppf) { - // Don't do anything for non-JPEG files (no need to report an error) - if (!IsJPG(compressed)) return false; - - // TODO(veluca): use JPEGData also for pixels? - - // We need to declare all the non-trivial destructor local variables before - // the call to setjmp(). - std::unique_ptr row; - - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - // Setup error handling in jpeg library so we can deal with broken jpegs in - // the fuzzer. - jpeg_error_mgr jerr; - jmp_buf env; - cinfo.err = jpegli_std_error(&jerr); - jerr.error_exit = &MyErrorExit; - jerr.output_message = &MyOutputMessage; - if (setjmp(env)) { - return false; - } - cinfo.client_data = static_cast(&env); - - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, - reinterpret_cast(compressed.data()), - compressed.size()); - jpegli_save_markers(&cinfo, kICCMarker, 0xFFFF); - jpegli_save_markers(&cinfo, kExifMarker, 0xFFFF); - const auto failure = [&cinfo](const char* str) -> Status { - jpegli_abort_decompress(&cinfo); - jpegli_destroy_decompress(&cinfo); - return JXL_FAILURE("%s", str); - }; - jpegli_read_header(&cinfo, TRUE); - // Might cause CPU-zip bomb. - if (cinfo.arith_code) { - return failure("arithmetic code JPEGs are not supported"); - } - int nbcomp = cinfo.num_components; - if (nbcomp != 1 && nbcomp != 3) { - std::string msg = - "unsupported number of components in JPEG: " + std::to_string(nbcomp); - return failure(msg.c_str()); - } - if (dparams.force_rgb) { - cinfo.out_color_space = JCS_RGB; - } else if (dparams.force_grayscale) { - cinfo.out_color_space = JCS_GRAYSCALE; - } - if (ReadICCProfile(&cinfo, &ppf->icc)) { - ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - } else { - ppf->primary_color_representation = - PackedPixelFile::kColorEncodingIsPrimary; - ppf->icc.clear(); - // Default to SRGB - ppf->color_encoding.color_space = - ConvertColorSpace(cinfo.out_color_space); - ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; - ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; - ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; - } - ReadExif(&cinfo, &ppf->metadata.exif); - - ppf->info.xsize = cinfo.image_width; - ppf->info.ysize = cinfo.image_height; - if (dparams.output_data_type == JXL_TYPE_UINT8) { - ppf->info.bits_per_sample = 8; - ppf->info.exponent_bits_per_sample = 0; - } else if (dparams.output_data_type == JXL_TYPE_UINT16) { - ppf->info.bits_per_sample = 16; - ppf->info.exponent_bits_per_sample = 0; - } else if (dparams.output_data_type == JXL_TYPE_FLOAT) { - ppf->info.bits_per_sample = 32; - ppf->info.exponent_bits_per_sample = 8; - } else { - return failure("unsupported data type"); - } - ppf->info.uses_original_profile = JXL_TRUE; - - // No alpha in JPG - ppf->info.alpha_bits = 0; - ppf->info.alpha_exponent_bits = 0; - ppf->info.orientation = JXL_ORIENT_IDENTITY; - - jpegli_set_output_format(&cinfo, ConvertDataType(dparams.output_data_type), - ConvertEndianness(dparams.output_endianness)); - - if (dparams.num_colors > 0) { - cinfo.quantize_colors = TRUE; - cinfo.desired_number_of_colors = dparams.num_colors; - cinfo.two_pass_quantize = static_cast(dparams.two_pass_quant); - cinfo.dither_mode = static_cast(dparams.dither_mode); - } - - jpegli_start_decompress(&cinfo); - - ppf->info.num_color_channels = cinfo.out_color_components; - const JxlPixelFormat format{ - /*num_channels=*/static_cast(cinfo.out_color_components), - dparams.output_data_type, - dparams.output_endianness, - /*align=*/0, - }; - ppf->frames.clear(); - // Allocates the frame buffer. - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); - ppf->frames.emplace_back(std::move(frame)); - } - const auto& frame = ppf->frames.back(); - JXL_ENSURE(sizeof(JSAMPLE) * cinfo.out_color_components * - cinfo.image_width <= - frame.color.stride); - if (dparams.num_colors > 0) JXL_ENSURE(cinfo.colormap != nullptr); - - for (size_t y = 0; y < cinfo.image_height; ++y) { - JSAMPROW rows[] = {reinterpret_cast( - static_cast(frame.color.pixels()) + - frame.color.stride * y)}; - jpegli_read_scanlines(&cinfo, rows, 1); - if (dparams.num_colors > 0) { - JXL_RETURN_IF_ERROR( - UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components, - cinfo.colormap, cinfo.actual_number_of_colors)); - } - } - - jpegli_finish_decompress(&cinfo); - return true; - }; - bool success = try_catch_block(); - jpegli_destroy_decompress(&cinfo); - return success; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.h b/third_party/jpeg-xl/lib/extras/dec/jpegli.h deleted file mode 100644 index 574df54c8e453..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_JPEGLI_H_ -#define LIB_EXTRAS_DEC_JPEGLI_H_ - -// Decodes JPG pixels and metadata in memory using the libjpegli library. - -#include -#include - -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -struct JpegDecompressParams { - JxlDataType output_data_type = JXL_TYPE_UINT8; - JxlEndianness output_endianness = JXL_NATIVE_ENDIAN; - bool force_rgb = false; - bool force_grayscale = false; - int num_colors = 0; - bool two_pass_quant = true; - // 0 = none, 1 = ordered, 2 = Floyd-Steinberg - int dither_mode = 2; -}; - -Status DecodeJpeg(const std::vector& compressed, - const JpegDecompressParams& dparams, ThreadPool* pool, - PackedPixelFile* ppf); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_JPEGLI_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc deleted file mode 100644 index 35f7b51e0c68a..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/jpg.h" - -#if JPEGXL_ENABLE_JPEG -#include "lib/jxl/base/include_jpeglib.h" // NOLINT -#endif - -#include -#include -#include -#include -#include - -#include "lib/extras/size_constraints.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/sanitizers.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -#if JPEGXL_ENABLE_JPEG -namespace { - -constexpr unsigned char kICCSignature[12] = { - 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -constexpr int kICCMarker = JPEG_APP0 + 2; - -constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, - 0x66, 0x00, 0x00}; -constexpr int kExifMarker = JPEG_APP0 + 1; - -inline bool IsJPG(const Span bytes) { - if (bytes.size() < 2) return false; - if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; - return true; -} - -bool MarkerIsICC(const jpeg_saved_marker_ptr marker) { - return marker->marker == kICCMarker && - marker->data_length >= sizeof kICCSignature + 2 && - std::equal(std::begin(kICCSignature), std::end(kICCSignature), - marker->data); -} -bool MarkerIsExif(const jpeg_saved_marker_ptr marker) { - return marker->marker == kExifMarker && - marker->data_length >= sizeof kExifSignature + 2 && - std::equal(std::begin(kExifSignature), std::end(kExifSignature), - marker->data); -} - -Status ReadICCProfile(jpeg_decompress_struct* const cinfo, - std::vector* const icc) { - constexpr size_t kICCSignatureSize = sizeof kICCSignature; - // ICC signature + uint8_t index + uint8_t max_index. - constexpr size_t kICCHeadSize = kICCSignatureSize + 2; - // Markers are 1-indexed, and we keep them that way in this vector to get a - // convenient 0 at the front for when we compute the offsets later. - std::vector marker_lengths; - int num_markers = 0; - int seen_markers_count = 0; - bool has_num_markers = false; - for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; - marker = marker->next) { - // marker is initialized by libjpeg, which we are not instrumenting with - // msan. - msan::UnpoisonMemory(marker, sizeof(*marker)); - msan::UnpoisonMemory(marker->data, marker->data_length); - if (!MarkerIsICC(marker)) continue; - - const int current_marker = marker->data[kICCSignatureSize]; - if (current_marker == 0) { - return JXL_FAILURE("inconsistent JPEG ICC marker numbering"); - } - const int current_num_markers = marker->data[kICCSignatureSize + 1]; - if (current_marker > current_num_markers) { - return JXL_FAILURE("inconsistent JPEG ICC marker numbering"); - } - if (has_num_markers) { - if (current_num_markers != num_markers) { - return JXL_FAILURE("inconsistent numbers of JPEG ICC markers"); - } - } else { - num_markers = current_num_markers; - has_num_markers = true; - marker_lengths.resize(num_markers + 1); - } - - size_t marker_length = marker->data_length - kICCHeadSize; - - if (marker_length == 0) { - // NB: if we allow empty chunks, then the next check is incorrect. - return JXL_FAILURE("Empty ICC chunk"); - } - - if (marker_lengths[current_marker] != 0) { - return JXL_FAILURE("duplicate JPEG ICC marker number"); - } - marker_lengths[current_marker] = marker_length; - seen_markers_count++; - } - - if (marker_lengths.empty()) { - // Not an error. - return false; - } - - if (seen_markers_count != num_markers) { - JXL_ENSURE(has_num_markers); - return JXL_FAILURE("Incomplete set of ICC chunks"); - } - - std::vector offsets = std::move(marker_lengths); - std::partial_sum(offsets.begin(), offsets.end(), offsets.begin()); - icc->resize(offsets.back()); - - for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; - marker = marker->next) { - if (!MarkerIsICC(marker)) continue; - const uint8_t* first = marker->data + kICCHeadSize; - uint8_t current_marker = marker->data[kICCSignatureSize]; - size_t offset = offsets[current_marker - 1]; - size_t marker_length = offsets[current_marker] - offset; - std::copy_n(first, marker_length, icc->data() + offset); - } - - return true; -} - -void ReadExif(jpeg_decompress_struct* const cinfo, - std::vector* const exif) { - constexpr size_t kExifSignatureSize = sizeof kExifSignature; - for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; - marker = marker->next) { - // marker is initialized by libjpeg, which we are not instrumenting with - // msan. - msan::UnpoisonMemory(marker, sizeof(*marker)); - msan::UnpoisonMemory(marker->data, marker->data_length); - if (!MarkerIsExif(marker)) continue; - size_t marker_length = marker->data_length - kExifSignatureSize; - exif->resize(marker_length); - std::copy_n(marker->data + kExifSignatureSize, marker_length, exif->data()); - return; - } -} - -void MyErrorExit(j_common_ptr cinfo) { - jmp_buf* env = static_cast(cinfo->client_data); - (*cinfo->err->output_message)(cinfo); - jpeg_destroy_decompress(reinterpret_cast(cinfo)); - longjmp(*env, 1); -} - -void MyOutputMessage(j_common_ptr cinfo) { - if (JXL_IS_DEBUG_BUILD) { - char buf[JMSG_LENGTH_MAX + 1]; - (*cinfo->err->format_message)(cinfo, buf); - buf[JMSG_LENGTH_MAX] = 0; - JXL_WARNING("%s", buf); - } -} - -Status UnmapColors(uint8_t* row, size_t xsize, int components, - JSAMPARRAY colormap, size_t num_colors) { - JXL_ENSURE(colormap != nullptr); - std::vector tmp(xsize * components); - for (size_t x = 0; x < xsize; ++x) { - JXL_ENSURE(row[x] < num_colors); - for (int c = 0; c < components; ++c) { - tmp[x * components + c] = colormap[c][row[x]]; - } - } - memcpy(row, tmp.data(), tmp.size()); - return true; -} - -} // namespace -#endif - -bool CanDecodeJPG() { -#if JPEGXL_ENABLE_JPEG - return true; -#else - return false; -#endif -} - -Status DecodeImageJPG(const Span bytes, - const ColorHints& color_hints, PackedPixelFile* ppf, - const SizeConstraints* constraints, - const JPGDecompressParams* dparams) { -#if JPEGXL_ENABLE_JPEG - // Don't do anything for non-JPEG files (no need to report an error) - if (!IsJPG(bytes)) return false; - - // TODO(veluca): use JPEGData also for pixels? - - // We need to declare all the non-trivial destructor local variables before - // the call to setjmp(). - std::unique_ptr row; - - const auto try_catch_block = [&]() -> bool { - jpeg_decompress_struct cinfo = {}; - // Setup error handling in jpeg library so we can deal with broken jpegs in - // the fuzzer. - jpeg_error_mgr jerr; - jmp_buf env; - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = &MyErrorExit; - jerr.output_message = &MyOutputMessage; - if (setjmp(env)) { - return false; - } - cinfo.client_data = static_cast(&env); - - jpeg_create_decompress(&cinfo); - jpeg_mem_src(&cinfo, reinterpret_cast(bytes.data()), - bytes.size()); - jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF); - jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); - const auto failure = [&cinfo](const char* str) -> Status { - jpeg_abort_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - return JXL_FAILURE("%s", str); - }; - int read_header_result = jpeg_read_header(&cinfo, TRUE); - // TODO(eustas): what about JPEG_HEADER_TABLES_ONLY? - if (read_header_result == JPEG_SUSPENDED) { - return failure("truncated JPEG input"); - } - if (!VerifyDimensions(constraints, cinfo.image_width, cinfo.image_height)) { - return failure("image too big"); - } - // Might cause CPU-zip bomb. - if (cinfo.arith_code) { - return failure("arithmetic code JPEGs are not supported"); - } - int nbcomp = cinfo.num_components; - if (nbcomp != 1 && nbcomp != 3) { - return failure("unsupported number of components in JPEG"); - } - if (ReadICCProfile(&cinfo, &ppf->icc)) { - ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - } else { - ppf->primary_color_representation = - PackedPixelFile::kColorEncodingIsPrimary; - ppf->icc.clear(); - // Default to SRGB - // Actually, (cinfo.output_components == nbcomp) will be checked after - // `jpeg_start_decompress`. - ppf->color_encoding.color_space = - (nbcomp == 1) ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB; - ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; - ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; - ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; - ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; - } - ReadExif(&cinfo, &ppf->metadata.exif); - if (!ApplyColorHints(color_hints, /*color_already_set=*/true, - /*is_gray=*/false, ppf)) { - return failure("ApplyColorHints failed"); - } - - ppf->info.xsize = cinfo.image_width; - ppf->info.ysize = cinfo.image_height; - // Original data is uint, so exponent_bits_per_sample = 0. - ppf->info.bits_per_sample = BITS_IN_JSAMPLE; - static_assert(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16); - ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = JXL_TRUE; - - // No alpha in JPG - ppf->info.alpha_bits = 0; - ppf->info.alpha_exponent_bits = 0; - - ppf->info.num_color_channels = nbcomp; - ppf->info.orientation = JXL_ORIENT_IDENTITY; - - if (dparams && dparams->num_colors > 0) { - cinfo.quantize_colors = TRUE; - cinfo.desired_number_of_colors = dparams->num_colors; - cinfo.two_pass_quantize = static_cast(dparams->two_pass_quant); - cinfo.dither_mode = static_cast(dparams->dither_mode); - } - - jpeg_start_decompress(&cinfo); - JXL_ENSURE(cinfo.out_color_components == nbcomp); - JxlDataType data_type = - ppf->info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; - - const JxlPixelFormat format{ - /*num_channels=*/static_cast(nbcomp), - data_type, - /*endianness=*/JXL_NATIVE_ENDIAN, - /*align=*/0, - }; - ppf->frames.clear(); - // Allocates the frame buffer. - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); - ppf->frames.emplace_back(std::move(frame)); - } - const auto& frame = ppf->frames.back(); - JXL_ENSURE(sizeof(JSAMPLE) * cinfo.out_color_components * - cinfo.image_width <= - frame.color.stride); - - if (cinfo.quantize_colors) { - JSAMPLE** colormap = cinfo.colormap; - jxl::msan::UnpoisonMemory(reinterpret_cast(colormap), - cinfo.out_color_components * sizeof(JSAMPLE*)); - for (int c = 0; c < cinfo.out_color_components; ++c) { - jxl::msan::UnpoisonMemory( - reinterpret_cast(colormap[c]), - cinfo.actual_number_of_colors * sizeof(JSAMPLE)); - } - } - if (dparams && dparams->num_colors > 0) { - JXL_ENSURE(cinfo.colormap != nullptr); - } - for (size_t y = 0; y < cinfo.image_height; ++y) { - JSAMPROW rows[] = {reinterpret_cast( - static_cast(frame.color.pixels()) + - frame.color.stride * y)}; - jpeg_read_scanlines(&cinfo, rows, 1); - msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components * - cinfo.image_width); - if (dparams && dparams->num_colors > 0) { - JXL_RETURN_IF_ERROR( - UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components, - cinfo.colormap, cinfo.actual_number_of_colors)); - } - } - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - return true; - }; - - return try_catch_block(); -#else - return false; -#endif -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.h b/third_party/jpeg-xl/lib/extras/dec/jpg.h deleted file mode 100644 index 6e7b2f786b054..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_JPG_H_ -#define LIB_EXTRAS_DEC_JPG_H_ - -// Decodes JPG pixels and metadata in memory. - -#include - -#include "lib/extras/codec.h" -#include "lib/extras/dec/color_hints.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -bool CanDecodeJPG(); - -struct JPGDecompressParams { - int num_colors = 0; - bool two_pass_quant = false; - // 0 = none, 1 = ordered, 2 = Floyd-Steinberg - int dither_mode = 0; -}; - -// Decodes `bytes` into `ppf`. color_hints are ignored. -// `elapsed_deinterleave`, if non-null, will be set to the time (in seconds) -// that it took to deinterleave the raw JSAMPLEs to planar floats. -Status DecodeImageJPG(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr, - const JPGDecompressParams* dparams = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_JPG_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.cc b/third_party/jpeg-xl/lib/extras/dec/jxl.cc deleted file mode 100644 index 83170b2b9fcec..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.cc +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/jxl.h" - -#include -#include -#include -#include -#include - -#include // PRIu32 -#include -#include -#include -#include -#include - -#include "lib/extras/common.h" -#include "lib/extras/dec/color_description.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/exif.h" -#include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { -namespace { - -#define QUIT(M) \ - fprintf(stderr, "%s\n", M); \ - return false; - -struct BoxProcessor { - explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } - - bool InitializeOutput(std::vector* out) { - if (out == nullptr) { - fprintf(stderr, "internal: out == nullptr\n"); - return false; - } - box_data_ = out; - return AddMoreOutput(); - } - - bool AddMoreOutput() { - if (box_data_ == nullptr) { - fprintf(stderr, "internal: box_data_ == nullptr\n"); - return false; - } - Flush(); - static const size_t kBoxOutputChunkSize = 1 << 16; - box_data_->resize(box_data_->size() + kBoxOutputChunkSize); - next_out_ = box_data_->data() + total_size_; - avail_out_ = box_data_->size() - total_size_; - if (JXL_DEC_SUCCESS != - JxlDecoderSetBoxBuffer(dec_, next_out_, avail_out_)) { - fprintf(stderr, "JxlDecoderSetBoxBuffer failed\n"); - return false; - } - return true; - } - - void FinalizeOutput() { - if (box_data_ == nullptr) return; - Flush(); - box_data_->resize(total_size_); - Reset(); - } - - private: - JxlDecoder* dec_; - std::vector* box_data_; - uint8_t* next_out_; - size_t avail_out_; - size_t total_size_; - - void Reset() { - box_data_ = nullptr; - next_out_ = nullptr; - avail_out_ = 0; - total_size_ = 0; - } - void Flush() { - if (box_data_ == nullptr) return; - size_t remaining = JxlDecoderReleaseBoxBuffer(dec_); - size_t bytes_written = avail_out_ - remaining; - next_out_ += bytes_written; - avail_out_ -= bytes_written; - total_size_ += bytes_written; - } -}; - -void SetBitDepthFromDataType(JxlDataType data_type, uint32_t* bits_per_sample, - uint32_t* exponent_bits_per_sample) { - switch (data_type) { - case JXL_TYPE_UINT8: - *bits_per_sample = 8; - *exponent_bits_per_sample = 0; - break; - case JXL_TYPE_UINT16: - *bits_per_sample = 16; - *exponent_bits_per_sample = 0; - break; - case JXL_TYPE_FLOAT16: - *bits_per_sample = 16; - *exponent_bits_per_sample = 5; - break; - case JXL_TYPE_FLOAT: - *bits_per_sample = 32; - *exponent_bits_per_sample = 8; - break; - } -} - -template -void UpdateBitDepth(JxlBitDepth bit_depth, JxlDataType data_type, T* info) { - if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) { - SetBitDepthFromDataType(data_type, &info->bits_per_sample, - &info->exponent_bits_per_sample); - } else if (bit_depth.type == JXL_BIT_DEPTH_CUSTOM) { - info->bits_per_sample = bit_depth.bits_per_sample; - info->exponent_bits_per_sample = bit_depth.exponent_bits_per_sample; - } -} - -} // namespace - -bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, - const JXLDecompressParams& dparams, size_t* decoded_bytes, - PackedPixelFile* ppf, std::vector* jpeg_bytes) { - JxlSignature sig = JxlSignatureCheck(bytes, bytes_size); - // silently return false if this is not a JXL file - if (sig == JXL_SIG_INVALID) return false; - - auto decoder = JxlDecoderMake(dparams.memory_manager); - JxlDecoder* dec = decoder.get(); - ppf->frames.clear(); - - if (dparams.runner_opaque != nullptr && - JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec, dparams.runner, - dparams.runner_opaque)) { - fprintf(stderr, "JxlEncoderSetParallelRunner failed\n"); - return false; - } - - JxlPixelFormat format = {}; // Initialize to calm down clang-tidy. - std::vector accepted_formats = dparams.accepted_formats; - - JxlColorEncoding color_encoding; - size_t num_color_channels = 0; - if (!dparams.color_space.empty()) { - if (!jxl::ParseDescription(dparams.color_space, &color_encoding)) { - fprintf(stderr, "Failed to parse color space %s.\n", - dparams.color_space.c_str()); - return false; - } - num_color_channels = - color_encoding.color_space == JXL_COLOR_SPACE_GRAY ? 1 : 3; - } - - bool can_reconstruct_jpeg = false; - std::vector jpeg_data_chunk; - if (jpeg_bytes != nullptr) { - // This bound is very likely to be enough to hold the entire - // reconstructed JPEG, to avoid having to do expensive retries. - jpeg_data_chunk.resize(bytes_size * 3 / 2 + 1024); - jpeg_bytes->resize(0); - } - - int events = (JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE); - - bool max_passes_defined = - (dparams.max_passes < std::numeric_limits::max()); - if (max_passes_defined || dparams.max_downsampling > 1) { - events |= JXL_DEC_FRAME_PROGRESSION; - if (max_passes_defined) { - JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kPasses); - } else { - JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kLastPasses); - } - } - if (jpeg_bytes != nullptr) { - events |= JXL_DEC_JPEG_RECONSTRUCTION; - } else { - events |= (JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_PREVIEW_IMAGE | - JXL_DEC_BOX); - if (accepted_formats.empty()) { - // decoding just the metadata, not the pixel data - events ^= (JXL_DEC_FULL_IMAGE | JXL_DEC_PREVIEW_IMAGE); - } - } - if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec, events)) { - fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); - return false; - } - if (jpeg_bytes == nullptr) { - if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors( - dec, TO_JXL_BOOL(dparams.render_spotcolors))) { - fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n"); - return false; - } - if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation( - dec, TO_JXL_BOOL(dparams.keep_orientation))) { - fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n"); - return false; - } - if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha( - dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) { - fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n"); - return false; - } - if (dparams.display_nits > 0 && - JXL_DEC_SUCCESS != - JxlDecoderSetDesiredIntensityTarget(dec, dparams.display_nits)) { - fprintf(stderr, "Decoder failed to set desired intensity target\n"); - return false; - } - if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec, JXL_TRUE)) { - fprintf(stderr, "JxlDecoderSetDecompressBoxes failed\n"); - return false; - } - } - if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec, bytes, bytes_size)) { - fprintf(stderr, "Decoder failed to set input\n"); - return false; - } - uint32_t progression_index = 0; - bool codestream_done = jpeg_bytes == nullptr && accepted_formats.empty(); - BoxProcessor boxes(dec); - for (;;) { - JxlDecoderStatus status = JxlDecoderProcessInput(dec); - if (status == JXL_DEC_ERROR) { - fprintf(stderr, "Failed to decode image\n"); - return false; - } else if (status == JXL_DEC_NEED_MORE_INPUT) { - if (codestream_done) { - break; - } - if (dparams.allow_partial_input) { - if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) { - fprintf(stderr, - "Input file is truncated and there is no preview " - "available yet.\n"); - return false; - } - break; - } - size_t released_size = JxlDecoderReleaseInput(dec); - fprintf(stderr, - "Input file is truncated (total bytes: %" PRIuS - ", processed bytes: %" PRIuS - ") and --allow_partial_files is not present.\n", - bytes_size, bytes_size - released_size); - return false; - } else if (status == JXL_DEC_BOX) { - boxes.FinalizeOutput(); - JxlBoxType box_type; - if (JXL_DEC_SUCCESS != JxlDecoderGetBoxType(dec, box_type, JXL_TRUE)) { - fprintf(stderr, "JxlDecoderGetBoxType failed\n"); - return false; - } - std::vector* box_data = nullptr; - if (memcmp(box_type, "Exif", 4) == 0) { - box_data = &ppf->metadata.exif; - } else if (memcmp(box_type, "iptc", 4) == 0) { - box_data = &ppf->metadata.iptc; - } else if (memcmp(box_type, "jumb", 4) == 0) { - box_data = &ppf->metadata.jumbf; - } else if (memcmp(box_type, "jhgm", 4) == 0) { - box_data = &ppf->metadata.jhgm; - } else if (memcmp(box_type, "xml ", 4) == 0) { - box_data = &ppf->metadata.xmp; - } - if (box_data) { - if (!boxes.InitializeOutput(box_data)) { - return false; - } - } - } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { - if (!boxes.AddMoreOutput()) { - return false; - } - } else if (status == JXL_DEC_JPEG_RECONSTRUCTION) { - can_reconstruct_jpeg = true; - // Decoding to JPEG. - if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec, - jpeg_data_chunk.data(), - jpeg_data_chunk.size())) { - fprintf(stderr, "Decoder failed to set JPEG Buffer\n"); - return false; - } - } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { - if (jpeg_bytes == nullptr) { - fprintf(stderr, "internal: jpeg_bytes == nullptr\n"); - return false; - } - // Decoded a chunk to JPEG. - size_t used_jpeg_output = - jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); - jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(), - jpeg_data_chunk.data() + used_jpeg_output); - if (used_jpeg_output == 0) { - // Chunk is too small. - jpeg_data_chunk.resize(jpeg_data_chunk.size() * 2); - } - if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec, - jpeg_data_chunk.data(), - jpeg_data_chunk.size())) { - fprintf(stderr, "Decoder failed to set JPEG Buffer\n"); - return false; - } - } else if (status == JXL_DEC_BASIC_INFO) { - if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &ppf->info)) { - fprintf(stderr, "JxlDecoderGetBasicInfo failed\n"); - return false; - } - if (accepted_formats.empty()) continue; - if (num_color_channels != 0) { - // Mark the change in number of color channels due to the requested - // color space. - ppf->info.num_color_channels = num_color_channels; - } - if (dparams.output_bitdepth.type == JXL_BIT_DEPTH_CUSTOM) { - // Select format based on custom bits per sample. - ppf->info.bits_per_sample = dparams.output_bitdepth.bits_per_sample; - } - // Select format according to accepted formats. - if (!jxl::extras::SelectFormat(accepted_formats, ppf->info, &format)) { - fprintf(stderr, "SelectFormat failed\n"); - return false; - } - bool have_alpha = (format.num_channels == 2 || format.num_channels == 4); - if (!have_alpha) { - // Mark in the basic info that alpha channel was dropped. - ppf->info.alpha_bits = 0; - } else { - if (dparams.unpremultiply_alpha) { - // Mark in the basic info that alpha was unpremultiplied. - ppf->info.alpha_premultiplied = JXL_FALSE; - } - } - bool alpha_found = false; - for (uint32_t i = 0; i < ppf->info.num_extra_channels; ++i) { - JxlExtraChannelInfo eci; - if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &eci)) { - fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n"); - return false; - } - if (eci.type == JXL_CHANNEL_ALPHA && have_alpha && !alpha_found) { - // Skip the first alpha channels because it is already present in the - // interleaved image. - alpha_found = true; - continue; - } - std::string name(eci.name_length + 1, 0); - if (JXL_DEC_SUCCESS != - JxlDecoderGetExtraChannelName( - dec, i, const_cast(name.data()), name.size())) { - fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n"); - return false; - } - name.resize(eci.name_length); - ppf->extra_channels_info.push_back({eci, i, name}); - } - } else if (status == JXL_DEC_COLOR_ENCODING) { - if (!dparams.color_space.empty()) { - if (ppf->info.uses_original_profile) { - fprintf(stderr, - "Warning: --color_space ignored because the image is " - "not XYB encoded.\n"); - } else { - JxlDecoderSetCms(dec, *JxlGetDefaultCms()); - if (JXL_DEC_SUCCESS != - JxlDecoderSetPreferredColorProfile(dec, &color_encoding)) { - fprintf(stderr, "Failed to set color space.\n"); - return false; - } - } - } - size_t icc_size = 0; - JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA; - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - } - if (icc_size != 0) { - ppf->icc.resize(icc_size); - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( - dec, target, ppf->icc.data(), icc_size)) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } - } - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile( - dec, target, &ppf->color_encoding)) { - ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; - } - ppf->primary_color_representation = - PackedPixelFile::kColorEncodingIsPrimary; - icc_size = 0; - target = JXL_COLOR_PROFILE_TARGET_ORIGINAL; - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - } - if (icc_size != 0) { - ppf->orig_icc.resize(icc_size); - if (JXL_DEC_SUCCESS != - JxlDecoderGetColorAsICCProfile(dec, target, ppf->orig_icc.data(), - icc_size)) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } - ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - } - } else if (status == JXL_DEC_FRAME) { - auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize, - ppf->info.ysize, format); - JXL_ASSIGN_OR_QUIT(jxl::extras::PackedFrame frame, - jxl::extras::PackedFrame::Create( - ppf->info.xsize, ppf->info.ysize, format), - "Failed to create image frame."); - if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) { - fprintf(stderr, "JxlDecoderGetFrameHeader failed\n"); - return false; - } - frame.name.resize(frame.frame_info.name_length + 1, 0); - if (JXL_DEC_SUCCESS != - JxlDecoderGetFrameName(dec, const_cast(frame.name.data()), - frame.name.size())) { - fprintf(stderr, "JxlDecoderGetFrameName failed\n"); - return false; - } - frame.name.resize(frame.frame_info.name_length); - ppf->frames.emplace_back(std::move(frame)); - progression_index = 0; - } else if (status == JXL_DEC_FRAME_PROGRESSION) { - size_t downsampling = JxlDecoderGetIntendedDownsamplingRatio(dec); - if ((max_passes_defined && progression_index >= dparams.max_passes) || - (!max_passes_defined && downsampling <= dparams.max_downsampling)) { - if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) { - fprintf(stderr, "JxlDecoderFlushImage failed\n"); - return false; - } - if (ppf->frames.back().frame_info.is_last) { - break; - } - if (JXL_DEC_SUCCESS != JxlDecoderSkipCurrentFrame(dec)) { - fprintf(stderr, "JxlDecoderSkipCurrentFrame failed\n"); - return false; - } - } - ++progression_index; - } else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) { - size_t buffer_size; - if (JXL_DEC_SUCCESS != - JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)) { - fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n"); - return false; - } - JXL_ASSIGN_OR_QUIT( - jxl::extras::PackedImage preview_image, - jxl::extras::PackedImage::Create(ppf->info.preview.xsize, - ppf->info.preview.ysize, format), - "Failed to create preview image."); - ppf->preview_frame = - jxl::make_unique(std::move(preview_image)); - if (buffer_size != ppf->preview_frame->color.pixels_size) { - fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", - buffer_size, ppf->preview_frame->color.pixels_size); - return false; - } - if (JXL_DEC_SUCCESS != - JxlDecoderSetPreviewOutBuffer( - dec, &format, ppf->preview_frame->color.pixels(), buffer_size)) { - fprintf(stderr, "JxlDecoderSetPreviewOutBuffer failed\n"); - return false; - } - } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { - if (jpeg_bytes != nullptr) { - break; - } - size_t buffer_size; - if (JXL_DEC_SUCCESS != - JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)) { - fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n"); - return false; - } - jxl::extras::PackedFrame& frame = ppf->frames.back(); - if (buffer_size != frame.color.pixels_size) { - fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", - buffer_size, frame.color.pixels_size); - return false; - } - - if (dparams.use_image_callback) { - auto callback = [](void* opaque, size_t x, size_t y, size_t num_pixels, - const void* pixels) { - auto* ppf = reinterpret_cast(opaque); - jxl::extras::PackedImage& color = ppf->frames.back().color; - uint8_t* pixels_buffer = reinterpret_cast(color.pixels()); - size_t sample_size = color.pixel_stride(); - memcpy(pixels_buffer + (color.stride * y + sample_size * x), pixels, - num_pixels * sample_size); - }; - if (JXL_DEC_SUCCESS != - JxlDecoderSetImageOutCallback(dec, &format, callback, ppf)) { - fprintf(stderr, "JxlDecoderSetImageOutCallback failed\n"); - return false; - } - } else { - if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec, &format, - frame.color.pixels(), - buffer_size)) { - fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n"); - return false; - } - } - if (JXL_DEC_SUCCESS != - JxlDecoderSetImageOutBitDepth(dec, &dparams.output_bitdepth)) { - fprintf(stderr, "JxlDecoderSetImageOutBitDepth failed\n"); - return false; - } - UpdateBitDepth(dparams.output_bitdepth, format.data_type, &ppf->info); - bool have_alpha = (format.num_channels == 2 || format.num_channels == 4); - if (have_alpha) { - // Interleaved alpha channels has the same bit depth as color channels. - ppf->info.alpha_bits = ppf->info.bits_per_sample; - ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample; - } - JxlPixelFormat ec_format = format; - ec_format.num_channels = 1; - for (auto& eci : ppf->extra_channels_info) { - JXL_ASSIGN_OR_QUIT(jxl::extras::PackedImage image, - jxl::extras::PackedImage::Create( - ppf->info.xsize, ppf->info.ysize, ec_format), - "Failed to create extra channel image."); - frame.extra_channels.emplace_back(std::move(image)); - auto& ec = frame.extra_channels.back(); - size_t buffer_size; - if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize( - dec, &ec_format, &buffer_size, eci.index)) { - fprintf(stderr, "JxlDecoderExtraChannelBufferSize failed\n"); - return false; - } - if (buffer_size != ec.pixels_size) { - fprintf(stderr, - "Invalid extra channel buffer size" - " %" PRIuS " %" PRIuS "\n", - buffer_size, ec.pixels_size); - return false; - } - if (JXL_DEC_SUCCESS != - JxlDecoderSetExtraChannelBuffer(dec, &ec_format, ec.pixels(), - buffer_size, eci.index)) { - fprintf(stderr, "JxlDecoderSetExtraChannelBuffer failed\n"); - return false; - } - UpdateBitDepth(dparams.output_bitdepth, ec_format.data_type, - &eci.ec_info); - } - } else if (status == JXL_DEC_SUCCESS) { - // Decoding finished successfully. - break; - } else if (status == JXL_DEC_PREVIEW_IMAGE) { - // Nothing to do. - } else if (status == JXL_DEC_FULL_IMAGE) { - if (jpeg_bytes != nullptr || ppf->frames.back().frame_info.is_last) { - codestream_done = true; - } - } else { - fprintf(stderr, "Error: unexpected status: %d\n", - static_cast(status)); - return false; - } - } - boxes.FinalizeOutput(); - if (!ppf->metadata.exif.empty()) { - // Verify that Exif box has a valid TIFF header at the specified offset. - // Discard bytes preceding the header. - if (ppf->metadata.exif.size() >= 4) { - uint32_t offset = LoadBE32(ppf->metadata.exif.data()); - if (offset <= ppf->metadata.exif.size() - 8) { - std::vector exif(ppf->metadata.exif.begin() + 4 + offset, - ppf->metadata.exif.end()); - bool bigendian; - if (IsExif(exif, &bigendian)) { - ppf->metadata.exif = std::move(exif); - } else { - fprintf(stderr, "Warning: invalid TIFF header in Exif\n"); - } - } else { - fprintf(stderr, "Warning: invalid Exif offset: %" PRIu32 "\n", offset); - } - } else { - fprintf(stderr, "Warning: invalid Exif length: %" PRIuS "\n", - ppf->metadata.exif.size()); - } - } - if (jpeg_bytes != nullptr) { - if (!can_reconstruct_jpeg) return false; - size_t used_jpeg_output = - jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); - jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(), - jpeg_data_chunk.data() + used_jpeg_output); - } - if (decoded_bytes) { - *decoded_bytes = bytes_size - JxlDecoderReleaseInput(dec); - } - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.h b/third_party/jpeg-xl/lib/extras/dec/jxl.h deleted file mode 100644 index 7c80280c033ad..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_JXL_H_ -#define LIB_EXTRAS_DEC_JXL_H_ - -// Decodes JPEG XL images in memory. - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" - -namespace jxl { -namespace extras { - -struct JXLDecompressParams { - // If empty, little endian float formats will be accepted. - std::vector accepted_formats; - - // Requested output color space description. - std::string color_space; - // If set, performs tone mapping to this intensity target luminance. - float display_nits = 0.0; - // Whether spot colors are rendered on the image. - bool render_spotcolors = true; - // Whether to keep or undo the orientation given in the header. - bool keep_orientation = false; - - // If runner_opaque is set, the decoder uses this parallel runner. - JxlParallelRunner runner; - void* runner_opaque = nullptr; - - // If memory_manager is set, decoder uses it. - JxlMemoryManager* memory_manager = nullptr; - - // Whether truncated input should be treated as an error. - bool allow_partial_input = false; - - // How many passes to decode at most. By default, decode everything. - uint32_t max_passes = std::numeric_limits::max(); - - // Alternatively, one can specify the maximum tolerable downscaling factor - // with respect to the full size of the image. By default, nothing less than - // the full size is requested. - size_t max_downsampling = 1; - - // Whether to use the image callback or the image buffer to get the output. - bool use_image_callback = true; - // Whether to unpremultiply colors for associated alpha channels. - bool unpremultiply_alpha = false; - - // Controls the effective bit depth of the output pixels. - JxlBitDepth output_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; -}; - -bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, - const JXLDecompressParams& dparams, size_t* decoded_bytes, - PackedPixelFile* ppf, - std::vector* jpeg_bytes = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_JXL_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx.cc b/third_party/jpeg-xl/lib/extras/dec/pgx.cc deleted file mode 100644 index 4499069d7708b..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/pgx.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/pgx.h" - -#include - -#include "lib/extras/size_constraints.h" -#include "lib/jxl/base/bits.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jxl { -namespace extras { -namespace { - -struct HeaderPGX { - // NOTE: PGX is always grayscale - size_t xsize; - size_t ysize; - size_t bits_per_sample; - bool big_endian; - bool is_signed; -}; - -class Parser { - public: - explicit Parser(const Span bytes) - : pos_(bytes.data()), end_(pos_ + bytes.size()) {} - - // Sets "pos" to the first non-header byte/pixel on success. - Status ParseHeader(HeaderPGX* header, const uint8_t** pos) { - // codec.cc ensures we have at least two bytes => no range check here. - if (pos_[0] != 'P' || pos_[1] != 'G') return false; - pos_ += 2; - return ParseHeaderPGX(header, pos); - } - - // Exposed for testing - Status ParseUnsigned(size_t* number) { - if (pos_ == end_) return JXL_FAILURE("PGX: reached end before number"); - if (!IsDigit(*pos_)) return JXL_FAILURE("PGX: expected unsigned number"); - - *number = 0; - while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { - *number *= 10; - *number += *pos_ - '0'; - ++pos_; - } - - return true; - } - - private: - static bool IsDigit(const uint8_t c) { return '0' <= c && c <= '9'; } - static bool IsLineBreak(const uint8_t c) { return c == '\r' || c == '\n'; } - static bool IsWhitespace(const uint8_t c) { - return IsLineBreak(c) || c == '\t' || c == ' '; - } - - Status SkipSpace() { - if (pos_ == end_) return JXL_FAILURE("PGX: reached end before space"); - const uint8_t c = *pos_; - if (c != ' ') return JXL_FAILURE("PGX: expected space"); - ++pos_; - return true; - } - - Status SkipLineBreak() { - if (pos_ == end_) return JXL_FAILURE("PGX: reached end before line break"); - // Line break can be either "\n" (0a) or "\r\n" (0d 0a). - if (*pos_ == '\n') { - pos_++; - return true; - } else if (*pos_ == '\r' && pos_ + 1 != end_ && *(pos_ + 1) == '\n') { - pos_ += 2; - return true; - } - return JXL_FAILURE("PGX: expected line break"); - } - - Status SkipSingleWhitespace() { - if (pos_ == end_) return JXL_FAILURE("PGX: reached end before whitespace"); - if (!IsWhitespace(*pos_)) return JXL_FAILURE("PGX: expected whitespace"); - ++pos_; - return true; - } - - Status ParseHeaderPGX(HeaderPGX* header, const uint8_t** pos) { - JXL_RETURN_IF_ERROR(SkipSpace()); - if (pos_ + 2 > end_) return JXL_FAILURE("PGX: header too small"); - if (*pos_ == 'M' && *(pos_ + 1) == 'L') { - header->big_endian = true; - } else if (*pos_ == 'L' && *(pos_ + 1) == 'M') { - header->big_endian = false; - } else { - return JXL_FAILURE("PGX: invalid endianness"); - } - pos_ += 2; - JXL_RETURN_IF_ERROR(SkipSpace()); - if (pos_ == end_) return JXL_FAILURE("PGX: header too small"); - if (*pos_ == '+') { - header->is_signed = false; - } else if (*pos_ == '-') { - header->is_signed = true; - } else { - return JXL_FAILURE("PGX: invalid signedness"); - } - pos_++; - // Skip optional space - if (pos_ < end_ && *pos_ == ' ') pos_++; - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->bits_per_sample)); - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize)); - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize)); - // 0xa, or 0xd 0xa. - JXL_RETURN_IF_ERROR(SkipLineBreak()); - - // TODO(jon): could do up to 24-bit by converting the values to - // JXL_TYPE_FLOAT. - if (header->bits_per_sample > 16) { - return JXL_FAILURE("PGX: >16 bits not yet supported"); - } - // TODO(lode): support signed integers. This may require changing the way - // external_image works. - if (header->is_signed) { - return JXL_FAILURE("PGX: signed not yet supported"); - } - - size_t numpixels = header->xsize * header->ysize; - size_t bytes_per_pixel = header->bits_per_sample <= 8 ? 1 : 2; - if (pos_ + numpixels * bytes_per_pixel > end_) { - return JXL_FAILURE("PGX: data too small"); - } - - *pos = pos_; - return true; - } - - const uint8_t* pos_; - const uint8_t* const end_; -}; - -} // namespace - -Status DecodeImagePGX(const Span bytes, - const ColorHints& color_hints, PackedPixelFile* ppf, - const SizeConstraints* constraints) { - Parser parser(bytes); - HeaderPGX header = {}; - const uint8_t* pos = nullptr; - if (!parser.ParseHeader(&header, &pos)) return false; - JXL_RETURN_IF_ERROR( - VerifyDimensions(constraints, header.xsize, header.ysize)); - if (header.bits_per_sample == 0 || header.bits_per_sample > 32) { - return JXL_FAILURE("PGX: bits_per_sample invalid"); - } - - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false, - /*is_gray=*/true, ppf)); - ppf->info.xsize = header.xsize; - ppf->info.ysize = header.ysize; - // Original data is uint, so exponent_bits_per_sample = 0. - ppf->info.bits_per_sample = header.bits_per_sample; - ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = JXL_TRUE; - - // No alpha in PGX - ppf->info.alpha_bits = 0; - ppf->info.alpha_exponent_bits = 0; - ppf->info.num_color_channels = 1; // Always grayscale - ppf->info.orientation = JXL_ORIENT_IDENTITY; - - JxlDataType data_type; - if (header.bits_per_sample > 8) { - data_type = JXL_TYPE_UINT16; - } else { - data_type = JXL_TYPE_UINT8; - } - - const JxlPixelFormat format{ - /*num_channels=*/1, - /*data_type=*/data_type, - /*endianness=*/header.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, - /*align=*/0, - }; - ppf->frames.clear(); - // Allocates the frame buffer. - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(header.xsize, header.ysize, format)); - ppf->frames.emplace_back(std::move(frame)); - } - const auto& frame = ppf->frames.back(); - size_t pgx_remaining_size = bytes.data() + bytes.size() - pos; - if (pgx_remaining_size < frame.color.pixels_size) { - return JXL_FAILURE("PGX file too small"); - } - memcpy(frame.color.pixels(), pos, frame.color.pixels_size); - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx.h b/third_party/jpeg-xl/lib/extras/dec/pgx.h deleted file mode 100644 index ce852e6965537..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/pgx.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_PGX_H_ -#define LIB_EXTRAS_DEC_PGX_H_ - -// Decodes PGX pixels in memory. - -#include -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -// Decodes `bytes` into `ppf`. -Status DecodeImagePGX(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_PGX_H_ diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc b/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc deleted file mode 100644 index 9370e5d5ae2bf..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/pgx.h" - -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/extras/packed_image_convert.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/image_bundle.h" -#include "lib/jxl/image_ops.h" -#include "lib/jxl/test_memory_manager.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace extras { -namespace { - -Span MakeSpan(const char* str) { - return Bytes(reinterpret_cast(str), strlen(str)); -} - -TEST(CodecPGXTest, Test8bits) { - std::string pgx = "PG ML + 8 2 3\npixels"; - - PackedPixelFile ppf; - ThreadPool* pool = nullptr; - - EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf)); - CodecInOut io{jxl::test::MemoryManager()}; - EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io)); - - ScaleImage(255.f, io.Main().color()); - - EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample); - EXPECT_EQ(8u, io.metadata.m.bit_depth.bits_per_sample); - EXPECT_TRUE(io.metadata.m.color_encoding.IsGray()); - EXPECT_EQ(2u, io.xsize()); - EXPECT_EQ(3u, io.ysize()); - - float eps = 1e-5; - EXPECT_NEAR('p', io.Main().color()->Plane(0).Row(0)[0], eps); - EXPECT_NEAR('i', io.Main().color()->Plane(0).Row(0)[1], eps); - EXPECT_NEAR('x', io.Main().color()->Plane(0).Row(1)[0], eps); - EXPECT_NEAR('e', io.Main().color()->Plane(0).Row(1)[1], eps); - EXPECT_NEAR('l', io.Main().color()->Plane(0).Row(2)[0], eps); - EXPECT_NEAR('s', io.Main().color()->Plane(0).Row(2)[1], eps); -} - -TEST(CodecPGXTest, Test16bits) { - std::string pgx = "PG ML + 16 2 3\np_i_x_e_l_s_"; - - PackedPixelFile ppf; - ThreadPool* pool = nullptr; - - EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf)); - CodecInOut io{jxl::test::MemoryManager()}; - EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io)); - - ScaleImage(255.f, io.Main().color()); - - EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample); - EXPECT_EQ(16u, io.metadata.m.bit_depth.bits_per_sample); - EXPECT_TRUE(io.metadata.m.color_encoding.IsGray()); - EXPECT_EQ(2u, io.xsize()); - EXPECT_EQ(3u, io.ysize()); - - // Comparing ~16-bit numbers in floats, only ~7 bits left. - float eps = 1e-3; - const auto& plane = io.Main().color()->Plane(0); - EXPECT_NEAR(256.0f * 'p' + '_', plane.Row(0)[0] * 257, eps); - EXPECT_NEAR(256.0f * 'i' + '_', plane.Row(0)[1] * 257, eps); - EXPECT_NEAR(256.0f * 'x' + '_', plane.Row(1)[0] * 257, eps); - EXPECT_NEAR(256.0f * 'e' + '_', plane.Row(1)[1] * 257, eps); - EXPECT_NEAR(256.0f * 'l' + '_', plane.Row(2)[0] * 257, eps); - EXPECT_NEAR(256.0f * 's' + '_', plane.Row(2)[1] * 257, eps); -} - -} // namespace -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.cc b/third_party/jpeg-xl/lib/extras/dec/pnm.cc deleted file mode 100644 index 587cd189a9d04..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/pnm.cc +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/dec/pnm.h" - -#include - -#include -#include -#include -#include -#include - -#include "lib/extras/size_constraints.h" -#include "lib/jxl/base/bits.h" -#include "lib/jxl/base/c_callback_support.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { -namespace { - -class Parser { - public: - explicit Parser(const Span bytes) - : pos_(bytes.data()), end_(pos_ + bytes.size()) {} - - // Sets "pos" to the first non-header byte/pixel on success. - Status ParseHeader(HeaderPNM* header, const uint8_t** pos) { - // codec.cc ensures we have at least two bytes => no range check here. - if (pos_[0] != 'P') return false; - const uint8_t type = pos_[1]; - pos_ += 2; - - switch (type) { - case '4': - return JXL_FAILURE("pbm not supported"); - - case '5': - header->is_gray = true; - return ParseHeaderPNM(header, pos); - - case '6': - header->is_gray = false; - return ParseHeaderPNM(header, pos); - - case '7': - return ParseHeaderPAM(header, pos); - - case 'F': - header->is_gray = false; - return ParseHeaderPFM(header, pos); - - case 'f': - header->is_gray = true; - return ParseHeaderPFM(header, pos); - - default: - return false; - } - } - - // Exposed for testing - Status ParseUnsigned(size_t* number) { - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before number"); - if (!IsDigit(*pos_)) return JXL_FAILURE("PNM: expected unsigned number"); - - *number = 0; - while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { - *number *= 10; - *number += *pos_ - '0'; - ++pos_; - } - - return true; - } - - Status ParseSigned(double* number) { - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before signed"); - - if (*pos_ != '-' && *pos_ != '+' && !IsDigit(*pos_)) { - return JXL_FAILURE("PNM: expected signed number"); - } - - // Skip sign - const bool is_neg = *pos_ == '-'; - if (is_neg || *pos_ == '+') { - ++pos_; - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before digits"); - } - - // Leading digits - *number = 0.0; - while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { - *number *= 10; - *number += *pos_ - '0'; - ++pos_; - } - - // Decimal places? - if (pos_ < end_ && *pos_ == '.') { - ++pos_; - double place = 0.1; - while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { - *number += (*pos_ - '0') * place; - place *= 0.1; - ++pos_; - } - } - - if (is_neg) *number = -*number; - return true; - } - - private: - static bool IsDigit(const uint8_t c) { return '0' <= c && c <= '9'; } - static bool IsLineBreak(const uint8_t c) { return c == '\r' || c == '\n'; } - static bool IsWhitespace(const uint8_t c) { - return IsLineBreak(c) || c == '\t' || c == ' '; - } - - Status SkipBlank() { - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before blank"); - const uint8_t c = *pos_; - if (c != ' ' && c != '\n') return JXL_FAILURE("PNM: expected blank"); - ++pos_; - return true; - } - - Status SkipSingleWhitespace() { - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before whitespace"); - if (!IsWhitespace(*pos_)) return JXL_FAILURE("PNM: expected whitespace"); - ++pos_; - return true; - } - - Status SkipWhitespace() { - if (pos_ == end_) return JXL_FAILURE("PNM: reached end before whitespace"); - if (!IsWhitespace(*pos_) && *pos_ != '#') { - return JXL_FAILURE("PNM: expected whitespace/comment"); - } - - while (pos_ < end_ && IsWhitespace(*pos_)) { - ++pos_; - } - - // Comment(s) - while (pos_ != end_ && *pos_ == '#') { - while (pos_ != end_ && !IsLineBreak(*pos_)) { - ++pos_; - } - // Newline(s) - while (pos_ != end_ && IsLineBreak(*pos_)) pos_++; - } - - while (pos_ < end_ && IsWhitespace(*pos_)) { - ++pos_; - } - return true; - } - - Status MatchString(const char* keyword, bool skipws = true) { - const uint8_t* ppos = pos_; - const uint8_t* kw = reinterpret_cast(keyword); - while (*kw) { - if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input"); - if (*kw != *ppos) return false; - ppos++; - kw++; - } - pos_ = ppos; - if (skipws) { - JXL_RETURN_IF_ERROR(SkipWhitespace()); - } else { - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - } - return true; - } - - Status ParseHeaderPAM(HeaderPNM* header, const uint8_t** pos) { - size_t depth = 3; - size_t max_val = 255; - JXL_RETURN_IF_ERROR(SkipWhitespace()); - while (!MatchString("ENDHDR", /*skipws=*/false)) { - if (MatchString("WIDTH")) { - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize)); - JXL_RETURN_IF_ERROR(SkipWhitespace()); - } else if (MatchString("HEIGHT")) { - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize)); - JXL_RETURN_IF_ERROR(SkipWhitespace()); - } else if (MatchString("DEPTH")) { - JXL_RETURN_IF_ERROR(ParseUnsigned(&depth)); - JXL_RETURN_IF_ERROR(SkipWhitespace()); - } else if (MatchString("MAXVAL")) { - JXL_RETURN_IF_ERROR(ParseUnsigned(&max_val)); - JXL_RETURN_IF_ERROR(SkipWhitespace()); - } else if (MatchString("TUPLTYPE")) { - if (MatchString("RGB_ALPHA")) { - header->has_alpha = true; - } else if (MatchString("RGB")) { - } else if (MatchString("GRAYSCALE_ALPHA")) { - header->has_alpha = true; - header->is_gray = true; - } else if (MatchString("GRAYSCALE")) { - header->is_gray = true; - } else if (MatchString("BLACKANDWHITE_ALPHA")) { - header->has_alpha = true; - header->is_gray = true; - max_val = 1; - } else if (MatchString("BLACKANDWHITE")) { - header->is_gray = true; - max_val = 1; - } else if (MatchString("Alpha")) { - header->ec_types.push_back(JXL_CHANNEL_ALPHA); - } else if (MatchString("Depth")) { - header->ec_types.push_back(JXL_CHANNEL_DEPTH); - } else if (MatchString("SpotColor")) { - header->ec_types.push_back(JXL_CHANNEL_SPOT_COLOR); - } else if (MatchString("SelectionMask")) { - header->ec_types.push_back(JXL_CHANNEL_SELECTION_MASK); - } else if (MatchString("Black")) { - header->ec_types.push_back(JXL_CHANNEL_BLACK); - } else if (MatchString("CFA")) { - header->ec_types.push_back(JXL_CHANNEL_CFA); - } else if (MatchString("Thermal")) { - header->ec_types.push_back(JXL_CHANNEL_THERMAL); - } else if (MatchString("Unknown")) { - header->ec_types.push_back(JXL_CHANNEL_UNKNOWN); - } else if (MatchString("Optional")) { - header->ec_types.push_back(JXL_CHANNEL_OPTIONAL); - } else { - return JXL_FAILURE("PAM: unknown TUPLTYPE"); - } - } else { - constexpr size_t kMaxHeaderLength = 20; - char unknown_header[kMaxHeaderLength + 1]; - size_t len = std::min(kMaxHeaderLength, end_ - pos_); - strncpy(unknown_header, reinterpret_cast(pos_), len); - unknown_header[len] = 0; - return JXL_FAILURE("PAM: unknown header keyword: %s", unknown_header); - } - } - size_t num_channels = header->is_gray ? 1 : 3; - if (header->has_alpha) num_channels++; - if (num_channels + header->ec_types.size() != depth) { - return JXL_FAILURE("PAM: bad DEPTH"); - } - if (max_val == 0 || max_val >= 65536) { - return JXL_FAILURE("PAM: bad MAXVAL"); - } - // e.g. When `max_val` is 1 , we want 1 bit: - header->bits_per_sample = FloorLog2Nonzero(max_val) + 1; - if ((1u << header->bits_per_sample) - 1 != max_val) - return JXL_FAILURE("PNM: unsupported MaxVal (expected 2^n - 1)"); - // PAM does not pack bits as in PBM. - - header->floating_point = false; - header->big_endian = true; - *pos = pos_; - return true; - } - - Status ParseHeaderPNM(HeaderPNM* header, const uint8_t** pos) { - JXL_RETURN_IF_ERROR(SkipWhitespace()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize)); - - JXL_RETURN_IF_ERROR(SkipWhitespace()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize)); - - JXL_RETURN_IF_ERROR(SkipWhitespace()); - size_t max_val; - JXL_RETURN_IF_ERROR(ParseUnsigned(&max_val)); - if (max_val == 0 || max_val >= 65536) { - return JXL_FAILURE("PNM: bad MaxVal"); - } - header->bits_per_sample = FloorLog2Nonzero(max_val) + 1; - if ((1u << header->bits_per_sample) - 1 != max_val) - return JXL_FAILURE("PNM: unsupported MaxVal (expected 2^n - 1)"); - header->floating_point = false; - header->big_endian = true; - - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - - *pos = pos_; - return true; - } - - Status ParseHeaderPFM(HeaderPNM* header, const uint8_t** pos) { - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize)); - - JXL_RETURN_IF_ERROR(SkipBlank()); - JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize)); - - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - // The scale has no meaning as multiplier, only its sign is used to - // indicate endianness. All software expects nominal range 0..1. - double scale; - JXL_RETURN_IF_ERROR(ParseSigned(&scale)); - if (scale == 0.0) { - return JXL_FAILURE("PFM: bad scale factor value."); - } else if (std::abs(scale) != 1.0) { - JXL_WARNING("PFM: Discarding non-unit scale factor"); - } - header->big_endian = scale > 0.0; - header->bits_per_sample = 32; - header->floating_point = true; - - JXL_RETURN_IF_ERROR(SkipSingleWhitespace()); - - *pos = pos_; - return true; - } - - const uint8_t* pos_; - const uint8_t* const end_; -}; - -} // namespace - -struct PNMChunkedInputFrame { - JxlChunkedFrameInputSource operator()() { - return JxlChunkedFrameInputSource{ - this, - METHOD_TO_C_CALLBACK( - &PNMChunkedInputFrame::GetColorChannelsPixelFormat), - METHOD_TO_C_CALLBACK(&PNMChunkedInputFrame::GetColorChannelDataAt), - METHOD_TO_C_CALLBACK(&PNMChunkedInputFrame::GetExtraChannelPixelFormat), - METHOD_TO_C_CALLBACK(&PNMChunkedInputFrame::GetExtraChannelDataAt), - METHOD_TO_C_CALLBACK(&PNMChunkedInputFrame::ReleaseCurrentData)}; - } - - void /* NOLINT */ GetColorChannelsPixelFormat(JxlPixelFormat* pixel_format) { - *pixel_format = format; - } - - const void* GetColorChannelDataAt(size_t xpos, size_t ypos, size_t xsize, - size_t ysize, size_t* row_offset) { - const size_t bytes_per_channel = - DivCeil(dec->header_.bits_per_sample, jxl::kBitsPerByte); - const size_t num_channels = dec->header_.is_gray ? 1 : 3; - const size_t bytes_per_pixel = num_channels * bytes_per_channel; - *row_offset = dec->header_.xsize * bytes_per_pixel; - const size_t offset = ypos * *row_offset + xpos * bytes_per_pixel; - return dec->pnm_.data() + offset + dec->data_start_; - } - - void GetExtraChannelPixelFormat(size_t ec_index, - JxlPixelFormat* pixel_format) { - (void)this; - *pixel_format = {}; - JXL_DEBUG_ABORT("Not implemented"); - } - - const void* GetExtraChannelDataAt(size_t ec_index, size_t xpos, size_t ypos, - size_t xsize, size_t ysize, - size_t* row_offset) { - (void)this; - *row_offset = 0; - JXL_DEBUG_ABORT("Not implemented"); - return nullptr; - } - - void ReleaseCurrentData(const void* buffer) {} - - JxlPixelFormat format; - const ChunkedPNMDecoder* dec; -}; - -StatusOr ChunkedPNMDecoder::Init(const char* path) { - ChunkedPNMDecoder dec; - JXL_ASSIGN_OR_RETURN(dec.pnm_, MemoryMappedFile::Init(path)); - size_t size = dec.pnm_.size(); - if (size < 2) return JXL_FAILURE("Invalid ppm"); - size_t hdr_buf = std::min(size, 10 * 1024); - Span span(dec.pnm_.data(), hdr_buf); - Parser parser(span); - HeaderPNM& header = dec.header_; - const uint8_t* pos = nullptr; - if (!parser.ParseHeader(&header, &pos)) { - return StatusCode::kGenericError; - } - dec.data_start_ = pos - span.data(); - - if (header.bits_per_sample == 0 || header.bits_per_sample > 16) { - return JXL_FAILURE("Invalid bits_per_sample"); - } - if (header.has_alpha || !header.ec_types.empty() || header.floating_point) { - return JXL_FAILURE("Only PGM and PPM inputs are supported"); - } - - const size_t bytes_per_channel = - DivCeil(dec.header_.bits_per_sample, jxl::kBitsPerByte); - const size_t num_channels = dec.header_.is_gray ? 1 : 3; - const size_t bytes_per_pixel = num_channels * bytes_per_channel; - size_t row_size = dec.header_.xsize * bytes_per_pixel; - if (size < header.ysize * row_size + dec.data_start_) { - return JXL_FAILURE("PNM file too small"); - } - return dec; -} - -jxl::Status ChunkedPNMDecoder::InitializePPF(const ColorHints& color_hints, - PackedPixelFile* ppf) { - // PPM specifies that in the raster, the sample values are "nonlinear" - // (BP.709, with gamma number of 2.2). Deviate from the specification and - // assume `sRGB` in our implementation. - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false, - header_.is_gray, ppf)); - - ppf->info.xsize = header_.xsize; - ppf->info.ysize = header_.ysize; - ppf->info.bits_per_sample = header_.bits_per_sample; - ppf->info.exponent_bits_per_sample = 0; - ppf->info.orientation = JXL_ORIENT_IDENTITY; - ppf->info.alpha_bits = 0; - ppf->info.alpha_exponent_bits = 0; - ppf->info.num_color_channels = (header_.is_gray ? 1 : 3); - ppf->info.num_extra_channels = 0; - - const JxlDataType data_type = - header_.bits_per_sample > 8 ? JXL_TYPE_UINT16 : JXL_TYPE_UINT8; - const JxlPixelFormat format{ - /*num_channels=*/ppf->info.num_color_channels, - /*data_type=*/data_type, - /*endianness=*/header_.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, - /*align=*/0, - }; - - PNMChunkedInputFrame frame; - frame.format = format; - frame.dec = this; - ppf->chunked_frames.emplace_back(header_.xsize, header_.ysize, frame); - return true; -} - -Status DecodeImagePNM(const Span bytes, - const ColorHints& color_hints, PackedPixelFile* ppf, - const SizeConstraints* constraints) { - Parser parser(bytes); - HeaderPNM header = {}; - const uint8_t* pos = nullptr; - if (!parser.ParseHeader(&header, &pos)) return false; - JXL_RETURN_IF_ERROR( - VerifyDimensions(constraints, header.xsize, header.ysize)); - - if (header.bits_per_sample == 0 || header.bits_per_sample > 32) { - return JXL_FAILURE("PNM: bits_per_sample invalid"); - } - - // PPM specifies that in the raster, the sample values are "nonlinear" - // (BP.709, with gamma number of 2.2). Deviate from the specification and - // assume `sRGB` in our implementation. - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false, - header.is_gray, ppf)); - - ppf->info.xsize = header.xsize; - ppf->info.ysize = header.ysize; - if (header.floating_point) { - ppf->info.bits_per_sample = 32; - ppf->info.exponent_bits_per_sample = 8; - } else { - ppf->info.bits_per_sample = header.bits_per_sample; - ppf->info.exponent_bits_per_sample = 0; - } - - ppf->info.orientation = JXL_ORIENT_IDENTITY; - - // No alpha in PNM and PFM - ppf->info.alpha_bits = (header.has_alpha ? ppf->info.bits_per_sample : 0); - ppf->info.alpha_exponent_bits = 0; - ppf->info.num_color_channels = (header.is_gray ? 1 : 3); - uint32_t num_alpha_channels = (header.has_alpha ? 1 : 0); - uint32_t num_interleaved_channels = - ppf->info.num_color_channels + num_alpha_channels; - ppf->info.num_extra_channels = num_alpha_channels + header.ec_types.size(); - - for (auto type : header.ec_types) { - PackedExtraChannel pec = {}; - pec.ec_info.bits_per_sample = ppf->info.bits_per_sample; - pec.ec_info.type = type; - ppf->extra_channels_info.emplace_back(std::move(pec)); - } - - JxlDataType data_type; - if (header.floating_point) { - // There's no float16 pnm version. - data_type = JXL_TYPE_FLOAT; - } else { - if (header.bits_per_sample > 8) { - data_type = JXL_TYPE_UINT16; - } else { - data_type = JXL_TYPE_UINT8; - } - } - - const JxlPixelFormat format{ - /*num_channels=*/num_interleaved_channels, - /*data_type=*/data_type, - /*endianness=*/header.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, - /*align=*/0, - }; - const JxlPixelFormat ec_format{1, format.data_type, format.endianness, 0}; - ppf->frames.clear(); - { - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(header.xsize, header.ysize, format)); - ppf->frames.emplace_back(std::move(frame)); - } - auto* frame = &ppf->frames.back(); - for (size_t i = 0; i < header.ec_types.size(); ++i) { - JXL_ASSIGN_OR_RETURN( - PackedImage ec, - PackedImage::Create(header.xsize, header.ysize, ec_format)); - frame->extra_channels.emplace_back(std::move(ec)); - } - size_t pnm_remaining_size = bytes.data() + bytes.size() - pos; - if (pnm_remaining_size < frame->color.pixels_size) { - return JXL_FAILURE("PNM file too small"); - } - - uint8_t* out = reinterpret_cast(frame->color.pixels()); - std::vector ec_out(header.ec_types.size()); - for (size_t i = 0; i < ec_out.size(); ++i) { - ec_out[i] = reinterpret_cast(frame->extra_channels[i].pixels()); - } - if (ec_out.empty()) { - const bool flipped_y = header.bits_per_sample == 32; // PFMs are flipped - for (size_t y = 0; y < header.ysize; ++y) { - size_t y_in = flipped_y ? header.ysize - 1 - y : y; - const uint8_t* row_in = &pos[y_in * frame->color.stride]; - uint8_t* row_out = &out[y * frame->color.stride]; - memcpy(row_out, row_in, frame->color.stride); - } - } else { - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(data_type)); - size_t pwidth = PackedImage::BitsPerChannel(data_type) / 8; - for (size_t y = 0; y < header.ysize; ++y) { - for (size_t x = 0; x < header.xsize; ++x) { - memcpy(out, pos, frame->color.pixel_stride()); - out += frame->color.pixel_stride(); - pos += frame->color.pixel_stride(); - for (auto& p : ec_out) { - memcpy(p, pos, pwidth); - pos += pwidth; - p += pwidth; - } - } - } - } - if (ppf->info.exponent_bits_per_sample == 0) { - ppf->input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; - } - return true; -} - -// Exposed for testing. -Status PnmParseSigned(Bytes str, double* v) { - return Parser(str).ParseSigned(v); -} - -Status PnmParseUnsigned(Bytes str, size_t* v) { - return Parser(str).ParseUnsigned(v); -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.h b/third_party/jpeg-xl/lib/extras/dec/pnm.h deleted file mode 100644 index a6ad14fdc8f6b..0000000000000 --- a/third_party/jpeg-xl/lib/extras/dec/pnm.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_DEC_PNM_H_ -#define LIB_EXTRAS_DEC_PNM_H_ - -// Decodes PBM/PGM/PPM/PFM pixels in memory. - -#include -#include - -// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown) -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/mmap.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints; - -namespace extras { - -// Decodes `bytes` into `ppf`. color_hints may specify "color_space", which -// defaults to sRGB. -Status DecodeImagePNM(Span bytes, const ColorHints& color_hints, - PackedPixelFile* ppf, - const SizeConstraints* constraints = nullptr); - -struct HeaderPNM { - size_t xsize; - size_t ysize; - bool is_gray; // PGM - bool has_alpha; // PAM - size_t bits_per_sample; - bool floating_point; - bool big_endian; - std::vector ec_types; // PAM -}; - -class ChunkedPNMDecoder { - public: - static StatusOr Init(const char* file_path); - // Initializes `ppf` with a pointer to this `ChunkedPNMDecoder`. - jxl::Status InitializePPF(const ColorHints& color_hints, - PackedPixelFile* ppf); - - private: - HeaderPNM header_ = {}; - size_t data_start_ = 0; - MemoryMappedFile pnm_; - - friend struct PNMChunkedInputFrame; -}; - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_DEC_PNM_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.cc b/third_party/jpeg-xl/lib/extras/enc/apng.cc deleted file mode 100644 index 4d3ce8c17ab5c..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/apng.cc +++ /dev/null @@ -1,510 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/apng.h" - -// Parts of this code are taken from apngdis, which has the following license: -/* APNG Disassembler 2.8 - * - * Deconstructs APNG files into individual frames. - * - * http://apngdis.sourceforge.net - * - * Copyright (c) 2010-2015 Max Stepin - * maxst at users.sourceforge.net - * - * zlib license - * ------------ - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - */ - -#include -#include -#include - -#include "lib/extras/exif.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/printf_macros.h" -#if JPEGXL_ENABLE_APNG -#include "png.h" /* original (unpatched) libpng is ok */ -#endif - -namespace jxl { -namespace extras { - -#if JPEGXL_ENABLE_APNG -namespace { - -constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, - 0x66, 0x00, 0x00}; - -class APNGEncoder : public Encoder { - public: - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 2, 3, 4}) { - for (const JxlDataType data_type : - {JXL_TYPE_UINT8, JXL_TYPE_UINT16, JXL_TYPE_FLOAT}) { - for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { - formats.push_back( - JxlPixelFormat{num_channels, data_type, endianness, /*align=*/0}); - } - } - } - return formats; - } - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - // Encode main image frames - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - encoded_image->icc.clear(); - encoded_image->bitstreams.resize(1); - JXL_RETURN_IF_ERROR(EncodePackedPixelFileToAPNG( - ppf, pool, &encoded_image->bitstreams.front())); - - // Encode extra channels - for (size_t i = 0; i < ppf.extra_channels_info.size(); ++i) { - encoded_image->extra_channel_bitstreams.emplace_back(); - auto& ec_bitstreams = encoded_image->extra_channel_bitstreams.back(); - ec_bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR(EncodePackedPixelFileToAPNG( - ppf, pool, &ec_bitstreams.back(), true, i)); - } - return true; - } - - private: - Status EncodePackedPixelFileToAPNG(const PackedPixelFile& ppf, - ThreadPool* pool, - std::vector* bytes, - bool encode_extra_channels = false, - size_t extra_channel_index = 0) const; -}; - -void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) { - std::vector* bytes = - static_cast*>(png_get_io_ptr(png_ptr)); - bytes->insert(bytes->end(), data, data + length); -} - -// Stores XMP and EXIF/IPTC into key/value strings for PNG -class BlobsWriterPNG { - public: - static Status Encode(const PackedMetadata& blobs, - std::vector* strings) { - if (!blobs.exif.empty()) { - // PNG viewers typically ignore Exif orientation but not all of them do - // (and e.g. cjxl doesn't), so we overwrite the Exif orientation to the - // identity to avoid repeated orientation. - std::vector exif = blobs.exif; - ResetExifOrientation(exif); - // By convention, the data is prefixed with "Exif\0\0" when stored in - // the legacy (and non-standard) "Raw profile type exif" text chunk - // currently used here. - // TODO(user): Store Exif data in an eXIf chunk instead, which always - // begins with the TIFF header. - if (exif.size() >= sizeof kExifSignature && - memcmp(exif.data(), kExifSignature, sizeof kExifSignature) != 0) { - exif.insert(exif.begin(), kExifSignature, - kExifSignature + sizeof kExifSignature); - } - JXL_RETURN_IF_ERROR(EncodeBase16("exif", exif, strings)); - } - if (!blobs.iptc.empty()) { - JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings)); - } - if (!blobs.xmp.empty()) { - // TODO(user): Store XMP data in an "XML:com.adobe.xmp" text chunk - // instead. - JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, strings)); - } - return true; - } - - private: - // TODO(eustas): use array - static JXL_INLINE char EncodeNibble(const uint8_t nibble) { - if (nibble < 16) { - return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10; - } else { - JXL_DEBUG_ABORT("Internal logic error"); - return 0; - } - } - - static Status EncodeBase16(const std::string& type, - const std::vector& bytes, - std::vector* strings) { - // Encoding: base16 with newline after 72 chars. - const size_t base16_size = - 2 * bytes.size() + DivCeil(bytes.size(), static_cast(36)) + 1; - std::string base16; - base16.reserve(base16_size); - for (size_t i = 0; i < bytes.size(); ++i) { - if (i % 36 == 0) base16.push_back('\n'); - base16.push_back(EncodeNibble(bytes[i] >> 4)); - base16.push_back(EncodeNibble(bytes[i] & 0x0F)); - } - base16.push_back('\n'); - JXL_ENSURE(base16.length() == base16_size); - - char key[30]; - snprintf(key, sizeof(key), "Raw profile type %s", type.c_str()); - - char header[30]; - snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(), - bytes.size()); - - strings->emplace_back(key); - strings->push_back(std::string(header) + base16); - return true; - } -}; - -void MaybeAddCICP(const JxlColorEncoding& c_enc, png_structp png_ptr, - png_infop info_ptr) { - png_byte cicp_data[4] = {}; - png_unknown_chunk cicp_chunk; - if (c_enc.color_space != JXL_COLOR_SPACE_RGB) { - return; - } - if (c_enc.primaries == JXL_PRIMARIES_P3) { - if (c_enc.white_point == JXL_WHITE_POINT_D65) { - cicp_data[0] = 12; - } else if (c_enc.white_point == JXL_WHITE_POINT_DCI) { - cicp_data[0] = 11; - } else { - return; - } - } else if (c_enc.primaries != JXL_PRIMARIES_CUSTOM && - c_enc.white_point == JXL_WHITE_POINT_D65) { - cicp_data[0] = static_cast(c_enc.primaries); - } else { - return; - } - if (c_enc.transfer_function == JXL_TRANSFER_FUNCTION_UNKNOWN || - c_enc.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) { - return; - } - cicp_data[1] = static_cast(c_enc.transfer_function); - cicp_data[2] = 0; - cicp_data[3] = 1; - cicp_chunk.data = cicp_data; - cicp_chunk.size = sizeof(cicp_data); - cicp_chunk.location = PNG_HAVE_IHDR; - memcpy(cicp_chunk.name, "cICP", 5); - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, - reinterpret_cast("cICP"), 1); - png_set_unknown_chunks(png_ptr, info_ptr, &cicp_chunk, 1); -} - -bool MaybeAddSRGB(const JxlColorEncoding& c_enc, png_structp png_ptr, - png_infop info_ptr) { - if (c_enc.transfer_function == JXL_TRANSFER_FUNCTION_SRGB && - (c_enc.color_space == JXL_COLOR_SPACE_GRAY || - (c_enc.color_space == JXL_COLOR_SPACE_RGB && - c_enc.primaries == JXL_PRIMARIES_SRGB && - c_enc.white_point == JXL_WHITE_POINT_D65))) { - png_set_sRGB(png_ptr, info_ptr, c_enc.rendering_intent); - png_set_cHRM_fixed(png_ptr, info_ptr, 31270, 32900, 64000, 33000, 30000, - 60000, 15000, 6000); - png_set_gAMA_fixed(png_ptr, info_ptr, 45455); - return true; - } - return false; -} - -void MaybeAddCHRM(const JxlColorEncoding& c_enc, png_structp png_ptr, - png_infop info_ptr) { - if (c_enc.color_space != JXL_COLOR_SPACE_RGB) return; - if (c_enc.primaries == 0) return; - png_set_cHRM(png_ptr, info_ptr, c_enc.white_point_xy[0], - c_enc.white_point_xy[1], c_enc.primaries_red_xy[0], - c_enc.primaries_red_xy[1], c_enc.primaries_green_xy[0], - c_enc.primaries_green_xy[1], c_enc.primaries_blue_xy[0], - c_enc.primaries_blue_xy[1]); -} - -void MaybeAddGAMA(const JxlColorEncoding& c_enc, png_structp png_ptr, - png_infop info_ptr) { - switch (c_enc.transfer_function) { - case JXL_TRANSFER_FUNCTION_LINEAR: - png_set_gAMA_fixed(png_ptr, info_ptr, PNG_FP_1); - break; - case JXL_TRANSFER_FUNCTION_SRGB: - png_set_gAMA_fixed(png_ptr, info_ptr, 45455); - break; - case JXL_TRANSFER_FUNCTION_GAMMA: - png_set_gAMA(png_ptr, info_ptr, c_enc.gamma); - break; - - default:; - // No gAMA chunk. - } -} - -void MaybeAddCLLi(const JxlColorEncoding& c_enc, const float intensity_target, - png_structp png_ptr, png_infop info_ptr) { - if (c_enc.transfer_function != JXL_TRANSFER_FUNCTION_PQ) return; - if (intensity_target == 10'000) return; - - const uint32_t max_content_light_level = - static_cast(10'000.f * Clamp1(intensity_target, 0.f, 10'000.f)); - png_byte chunk_data[8] = {}; - png_save_uint_32(chunk_data, max_content_light_level); - // Leave MaxFALL set to 0. - png_unknown_chunk chunk; - memcpy(chunk.name, "cLLi", 5); - chunk.data = chunk_data; - chunk.size = sizeof chunk_data; - chunk.location = PNG_HAVE_IHDR; - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, - reinterpret_cast("cLLi"), 1); - png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); -} - -Status APNGEncoder::EncodePackedPixelFileToAPNG( - const PackedPixelFile& ppf, ThreadPool* pool, std::vector* bytes, - bool encode_extra_channels, size_t extra_channel_index) const { - JxlExtraChannelInfo ec_info{}; - if (encode_extra_channels) { - if (ppf.extra_channels_info.size() <= extra_channel_index) { - return JXL_FAILURE("Invalid index for extra channel"); - } - ec_info = ppf.extra_channels_info[extra_channel_index].ec_info; - } - - bool has_alpha = !encode_extra_channels && (ppf.info.alpha_bits != 0); - bool is_gray = encode_extra_channels || (ppf.info.num_color_channels == 1); - size_t color_channels = - encode_extra_channels ? 1 : ppf.info.num_color_channels; - size_t num_channels = color_channels + (has_alpha ? 1 : 0); - - if (!ppf.info.have_animation && ppf.frames.size() != 1) { - return JXL_FAILURE("Invalid number of frames"); - } - - size_t count = 0; - size_t anim_chunks = 0; - - for (const auto& frame : ppf.frames) { - const PackedImage& color = encode_extra_channels - ? frame.extra_channels[extra_channel_index] - : frame.color; - - size_t xsize = color.xsize; - size_t ysize = color.ysize; - size_t num_samples = num_channels * xsize * ysize; - - uint32_t bits_per_sample = encode_extra_channels ? ec_info.bits_per_sample - : ppf.info.bits_per_sample; - if (!encode_extra_channels) { - JXL_RETURN_IF_ERROR(VerifyPackedImage(color, ppf.info)); - } else { - JXL_RETURN_IF_ERROR(VerifyFormat(color.format)); - JXL_RETURN_IF_ERROR(VerifyBitDepth(color.format.data_type, - bits_per_sample, - ec_info.exponent_bits_per_sample)); - } - const JxlPixelFormat format = color.format; - const uint8_t* in = reinterpret_cast(color.pixels()); - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(format.data_type)); - size_t data_bits_per_sample = PackedImage::BitsPerChannel(format.data_type); - size_t bytes_per_sample = data_bits_per_sample / 8; - size_t out_bytes_per_sample = bytes_per_sample > 1 ? 2 : 1; - size_t out_stride = xsize * num_channels * out_bytes_per_sample; - size_t out_size = ysize * out_stride; - std::vector out(out_size); - - if (format.data_type == JXL_TYPE_UINT8) { - if (bits_per_sample < 8) { - float mul = 255.0 / ((1u << bits_per_sample) - 1); - for (size_t i = 0; i < num_samples; ++i) { - out[i] = static_cast(std::lroundf(in[i] * mul)); - } - } else { - memcpy(out.data(), in, out_size); - } - } else if (format.data_type == JXL_TYPE_UINT16) { - if (bits_per_sample < 16 || format.endianness != JXL_BIG_ENDIAN) { - float mul = 65535.0 / ((1u << bits_per_sample) - 1); - const uint8_t* p_in = in; - uint8_t* p_out = out.data(); - for (size_t i = 0; i < num_samples; ++i, p_in += 2, p_out += 2) { - uint32_t val = (format.endianness == JXL_BIG_ENDIAN ? LoadBE16(p_in) - : LoadLE16(p_in)); - StoreBE16(static_cast(std::lroundf(val * mul)), p_out); - } - } else { - memcpy(out.data(), in, out_size); - } - } else if (format.data_type == JXL_TYPE_FLOAT) { - constexpr float kMul = 65535.0; - const uint8_t* p_in = in; - uint8_t* p_out = out.data(); - for (size_t i = 0; i < num_samples; - ++i, p_in += sizeof(float), p_out += 2) { - float val = - Clamp1(format.endianness == JXL_BIG_ENDIAN ? LoadBEFloat(p_in) - : format.endianness == JXL_LITTLE_ENDIAN - ? LoadLEFloat(p_in) - : *reinterpret_cast(p_in), - 0.f, 1.f); - StoreBE16(static_cast(std::lroundf(val * kMul)), p_out); - } - } - png_structp png_ptr; - png_infop info_ptr; - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, - nullptr); - - if (!png_ptr) return JXL_FAILURE("Could not init png encoder"); - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) return JXL_FAILURE("Could not init png info struct"); - png_set_compression_level(png_ptr, 1); - - png_set_write_fn(png_ptr, bytes, PngWrite, nullptr); - png_set_flush(png_ptr, 0); - - int width = xsize; - int height = ysize; - - png_byte color_type = (is_gray ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB); - if (has_alpha) color_type |= PNG_COLOR_MASK_ALPHA; - png_byte bit_depth = out_bytes_per_sample * 8; - - png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - if (count == 0 && !encode_extra_channels) { - if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) { - if (ppf.primary_color_representation != - PackedPixelFile::kIccIsPrimary) { - MaybeAddCICP(ppf.color_encoding, png_ptr, info_ptr); - } - if (!ppf.icc.empty()) { - png_set_benign_errors(png_ptr, 1); - png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), - ppf.icc.size()); - } - MaybeAddCHRM(ppf.color_encoding, png_ptr, info_ptr); - MaybeAddGAMA(ppf.color_encoding, png_ptr, info_ptr); - } - MaybeAddCLLi(ppf.color_encoding, ppf.info.intensity_target, png_ptr, - info_ptr); - - std::vector textstrings; - JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(ppf.metadata, &textstrings)); - for (size_t kk = 0; kk + 1 < textstrings.size(); kk += 2) { - png_text text; - text.key = const_cast(textstrings[kk].c_str()); - text.text = const_cast(textstrings[kk + 1].c_str()); - text.compression = PNG_TEXT_COMPRESSION_zTXt; - png_set_text(png_ptr, info_ptr, &text, 1); - } - - png_write_info(png_ptr, info_ptr); - } else { - // fake writing a header, otherwise libpng gets confused - size_t pos = bytes->size(); - png_write_info(png_ptr, info_ptr); - bytes->resize(pos); - } - - if (ppf.info.have_animation) { - if (count == 0) { - png_byte adata[8]; - png_save_uint_32(adata, ppf.frames.size()); - png_save_uint_32(adata + 4, ppf.info.animation.num_loops); - png_byte actl[5] = "acTL"; - png_write_chunk(png_ptr, actl, adata, 8); - } - png_byte fdata[26]; - // TODO(jon): also make this work for the non-coalesced case - png_save_uint_32(fdata, anim_chunks++); - png_save_uint_32(fdata + 4, width); - png_save_uint_32(fdata + 8, height); - png_save_uint_32(fdata + 12, 0); - png_save_uint_32(fdata + 16, 0); - png_save_uint_16(fdata + 20, frame.frame_info.duration * - ppf.info.animation.tps_denominator); - png_save_uint_16(fdata + 22, ppf.info.animation.tps_numerator); - fdata[24] = 1; - fdata[25] = 0; - png_byte fctl[5] = "fcTL"; - png_write_chunk(png_ptr, fctl, fdata, 26); - } - - std::vector rows(height); - for (int y = 0; y < height; ++y) { - rows[y] = out.data() + y * out_stride; - } - - png_write_flush(png_ptr); - const size_t pos = bytes->size(); - png_write_image(png_ptr, rows.data()); - png_write_flush(png_ptr); - if (count > 0) { - std::vector fdata(4); - png_save_uint_32(fdata.data(), anim_chunks++); - size_t p = pos; - while (p + 8 < bytes->size()) { - size_t len = png_get_uint_32(bytes->data() + p); - JXL_ENSURE(bytes->operator[](p + 4) == 'I'); - JXL_ENSURE(bytes->operator[](p + 5) == 'D'); - JXL_ENSURE(bytes->operator[](p + 6) == 'A'); - JXL_ENSURE(bytes->operator[](p + 7) == 'T'); - fdata.insert(fdata.end(), bytes->data() + p + 8, - bytes->data() + p + 8 + len); - p += len + 12; - } - bytes->resize(pos); - - png_byte fdat[5] = "fdAT"; - png_write_chunk(png_ptr, fdat, fdata.data(), fdata.size()); - } - - count++; - if (count == ppf.frames.size() || !ppf.info.have_animation) { - png_write_end(png_ptr, nullptr); - } - - png_destroy_write_struct(&png_ptr, &info_ptr); - } - - return true; -} - -} // namespace -#endif - -std::unique_ptr GetAPNGEncoder() { -#if JPEGXL_ENABLE_APNG - return jxl::make_unique(); -#else - return nullptr; -#endif -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.h b/third_party/jpeg-xl/lib/extras/enc/apng.h deleted file mode 100644 index 2a2139c8fac18..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/apng.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_APNG_H_ -#define LIB_EXTRAS_ENC_APNG_H_ - -// Encodes APNG images in memory. - -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetAPNGEncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_APNG_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.cc b/third_party/jpeg-xl/lib/extras/enc/encode.cc deleted file mode 100644 index 71be78e36cc22..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/encode.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/encode.h" - -#include - -#include "lib/extras/enc/apng.h" -#include "lib/extras/enc/exr.h" -#include "lib/extras/enc/jpg.h" -#include "lib/extras/enc/npy.h" -#include "lib/extras/enc/pgx.h" -#include "lib/extras/enc/pnm.h" - -namespace jxl { -namespace extras { - -Status Encoder::VerifyBasicInfo(const JxlBasicInfo& info) { - if (info.xsize == 0 || info.ysize == 0) { - return JXL_FAILURE("Empty image"); - } - if (info.num_color_channels != 1 && info.num_color_channels != 3) { - return JXL_FAILURE("Invalid number of color channels"); - } - if (info.alpha_bits > 0 && info.alpha_bits != info.bits_per_sample) { - return JXL_FAILURE("Alpha bit depth does not match image bit depth"); - } - if (info.orientation != JXL_ORIENT_IDENTITY) { - return JXL_FAILURE("Orientation must be identity"); - } - return true; -} - -Status Encoder::VerifyFormat(const JxlPixelFormat& format) const { - for (auto f : AcceptedFormats()) { - if (f.num_channels != format.num_channels) continue; - if (f.data_type != format.data_type) continue; - if (f.data_type == JXL_TYPE_UINT8 || f.endianness == format.endianness) { - return true; - } - } - return JXL_FAILURE("Format is not in the list of accepted formats."); -} - -Status Encoder::VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample, - uint32_t exponent_bits) { - if ((data_type == JXL_TYPE_UINT8 && - (bits_per_sample == 0 || bits_per_sample > 8 || exponent_bits != 0)) || - (data_type == JXL_TYPE_UINT16 && - (bits_per_sample <= 8 || bits_per_sample > 16 || exponent_bits != 0)) || - (data_type == JXL_TYPE_FLOAT16 && - (bits_per_sample > 16 || exponent_bits > 5))) { - return JXL_FAILURE( - "Incompatible data_type %d and bit depth %u with exponent bits %u", - static_cast(data_type), bits_per_sample, exponent_bits); - } - return true; -} - -Status Encoder::VerifyImageSize(const PackedImage& image, - const JxlBasicInfo& info) { - if (image.pixels() == nullptr) { - return JXL_FAILURE("Invalid image."); - } - if (image.stride != image.xsize * image.pixel_stride()) { - return JXL_FAILURE("Invalid image stride."); - } - if (image.pixels_size != image.ysize * image.stride) { - return JXL_FAILURE("Invalid image size."); - } - size_t info_num_channels = - (info.num_color_channels + (info.alpha_bits > 0 ? 1 : 0)); - if (image.xsize != info.xsize || image.ysize != info.ysize || - image.format.num_channels != info_num_channels) { - return JXL_FAILURE("Frame size does not match image size"); - } - return true; -} - -Status Encoder::VerifyPackedImage(const PackedImage& image, - const JxlBasicInfo& info) const { - JXL_RETURN_IF_ERROR(VerifyImageSize(image, info)); - JXL_RETURN_IF_ERROR(VerifyFormat(image.format)); - JXL_RETURN_IF_ERROR(VerifyBitDepth(image.format.data_type, - info.bits_per_sample, - info.exponent_bits_per_sample)); - return true; -} - -template -class MetadataEncoder : public Encoder { - public: - std::vector AcceptedFormats() const override { - std::vector formats; - // empty, i.e. no need for actual pixel data - return formats; - } - - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - encoded->icc.clear(); - encoded->bitstreams.resize(1); - if (metadata == 0) encoded->bitstreams.front() = ppf.metadata.exif; - if (metadata == 1) encoded->bitstreams.front() = ppf.metadata.xmp; - if (metadata == 2) encoded->bitstreams.front() = ppf.metadata.jumbf; - return true; - } -}; - -std::unique_ptr Encoder::FromExtension(std::string extension) { - std::transform( - extension.begin(), extension.end(), extension.begin(), - [](char c) { return std::tolower(c, std::locale::classic()); }); - if (extension == ".png" || extension == ".apng") return GetAPNGEncoder(); - if (extension == ".jpg") return GetJPEGEncoder(); - if (extension == ".jpeg") return GetJPEGEncoder(); - if (extension == ".npy") return GetNumPyEncoder(); - if (extension == ".pgx") return GetPGXEncoder(); - if (extension == ".pam") return GetPAMEncoder(); - if (extension == ".pgm") return GetPGMEncoder(); - if (extension == ".ppm") return GetPPMEncoder(); - if (extension == ".pnm") return GetPNMEncoder(); - if (extension == ".pfm") return GetPFMEncoder(); - if (extension == ".exr") return GetEXREncoder(); - if (extension == ".exif") return jxl::make_unique>(); - if (extension == ".xmp") return jxl::make_unique>(); - if (extension == ".xml") return jxl::make_unique>(); - if (extension == ".jumbf") return jxl::make_unique>(); - if (extension == ".jumb") return jxl::make_unique>(); - - return nullptr; -} - -std::string ListOfEncodeCodecs() { - std::string list_of_codecs("PPM, PNM, PFM, PAM, PGX"); - if (GetAPNGEncoder()) list_of_codecs.append(", PNG, APNG"); - if (GetJPEGEncoder()) list_of_codecs.append(", JPEG"); - if (GetEXREncoder()) list_of_codecs.append(", EXR"); - return list_of_codecs; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.h b/third_party/jpeg-xl/lib/extras/enc/encode.h deleted file mode 100644 index a71f3b220fc7b..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/encode.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_ENCODE_H_ -#define LIB_EXTRAS_ENC_ENCODE_H_ - -// Facade for image encoders. - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -struct EncodedImage { - // One (if the format supports animations or the image has only one frame) or - // more 1quential bitstreams. - std::vector> bitstreams; - - // For each extra channel one or more sequential bitstreams. - std::vector>> extra_channel_bitstreams; - - std::vector preview_bitstream; - - // If the format does not support embedding color profiles into the bitstreams - // above, it will be present here, to be written as a separate file. If it - // does support them, this field will be empty. - std::vector icc; - - // Additional output for conformance testing, only filled in by NumPyEncoder. - std::vector metadata; -}; - -class Encoder { - public: - static std::unique_ptr FromExtension(std::string extension); - - virtual ~Encoder() = default; - - // Set of pixel formats that this encoder takes as input. - // If empty, the 'encoder' does not need any pixels (it's metadata-only). - virtual std::vector AcceptedFormats() const = 0; - - // Any existing data in encoded_image is discarded. - virtual Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const = 0; - - void SetOption(std::string name, std::string value) { - options_[std::move(name)] = std::move(value); - } - - static Status VerifyBasicInfo(const JxlBasicInfo& info); - static Status VerifyImageSize(const PackedImage& image, - const JxlBasicInfo& info); - static Status VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample, - uint32_t exponent_bits); - - protected: - const std::unordered_map& options() const { - return options_; - } - - Status VerifyFormat(const JxlPixelFormat& format) const; - - Status VerifyPackedImage(const PackedImage& image, - const JxlBasicInfo& info) const; - - private: - std::unordered_map options_; -}; - -std::string ListOfEncodeCodecs(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_ENCODE_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/exr.cc b/third_party/jpeg-xl/lib/extras/enc/exr.cc deleted file mode 100644 index 5a4f7d768c3d9..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/exr.cc +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/exr.h" - -#if JPEGXL_ENABLE_EXR -#include -#include -#include -#include -#endif -#include - -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/byte_order.h" - -namespace jxl { -namespace extras { - -#if JPEGXL_ENABLE_EXR -namespace { - -namespace OpenEXR = OPENEXR_IMF_NAMESPACE; -namespace Imath = IMATH_NAMESPACE; - -// OpenEXR::Int64 is deprecated in favor of using uint64_t directly, but using -// uint64_t as recommended causes build failures with previous OpenEXR versions -// on macOS, where the definition for OpenEXR::Int64 was actually not equivalent -// to uint64_t. This alternative should work in all cases. -using ExrInt64 = decltype(std::declval().tellg()); - -class InMemoryOStream : public OpenEXR::OStream { - public: - // `bytes` must outlive the InMemoryOStream. - explicit InMemoryOStream(std::vector* const bytes) - : OStream(/*fileName=*/""), bytes_(*bytes) {} - - void write(const char c[], const int n) override { - if (bytes_.size() < pos_ + n) { - bytes_.resize(pos_ + n); - } - std::copy_n(c, n, bytes_.begin() + pos_); - pos_ += n; - } - - ExrInt64 tellp() override { return pos_; } - void seekp(const ExrInt64 pos) override { - if (bytes_.size() + 1 < pos) { - bytes_.resize(pos - 1); - } - pos_ = pos; - } - - private: - std::vector& bytes_; - size_t pos_ = 0; -}; - -// Loads a Big-Endian float -float LoadBEFloat(const uint8_t* p) { - uint32_t u = LoadBE32(p); - float result; - memcpy(&result, &u, 4); - return result; -} - -// Loads a Little-Endian float -float LoadLEFloat(const uint8_t* p) { - uint32_t u = LoadLE32(p); - float result; - memcpy(&result, &u, 4); - return result; -} - -Status EncodeImageEXR(const PackedImage& image, const JxlBasicInfo& info, - const JxlColorEncoding& c_enc, ThreadPool* pool, - std::vector* bytes) { - OpenEXR::setGlobalThreadCount(0); - - const size_t xsize = info.xsize; - const size_t ysize = info.ysize; - const bool has_alpha = info.alpha_bits > 0; - const bool alpha_is_premultiplied = FROM_JXL_BOOL(info.alpha_premultiplied); - - if (info.num_color_channels != 3 || - c_enc.color_space != JXL_COLOR_SPACE_RGB || - c_enc.transfer_function != JXL_TRANSFER_FUNCTION_LINEAR) { - return JXL_FAILURE("Unsupported color encoding for OpenEXR output."); - } - - const size_t num_channels = 3 + (has_alpha ? 1 : 0); - const JxlPixelFormat format = image.format; - - if (format.data_type != JXL_TYPE_FLOAT) { - return JXL_FAILURE("Unsupported pixel format for OpenEXR output"); - } - - const uint8_t* in = reinterpret_cast(image.pixels()); - size_t in_stride = num_channels * 4 * xsize; - - OpenEXR::Header header(xsize, ysize); - OpenEXR::Chromaticities chromaticities; - chromaticities.red = - Imath::V2f(c_enc.primaries_red_xy[0], c_enc.primaries_red_xy[1]); - chromaticities.green = - Imath::V2f(c_enc.primaries_green_xy[0], c_enc.primaries_green_xy[1]); - chromaticities.blue = - Imath::V2f(c_enc.primaries_blue_xy[0], c_enc.primaries_blue_xy[1]); - chromaticities.white = - Imath::V2f(c_enc.white_point_xy[0], c_enc.white_point_xy[1]); - OpenEXR::addChromaticities(header, chromaticities); - OpenEXR::addWhiteLuminance(header, info.intensity_target); - - auto loadFloat = - format.endianness == JXL_BIG_ENDIAN ? LoadBEFloat : LoadLEFloat; - auto loadAlpha = - has_alpha ? loadFloat : [](const uint8_t* p) -> float { return 1.0f; }; - - // Ensure that the destructor of RgbaOutputFile has run before we look at the - // size of `bytes`. - { - InMemoryOStream os(bytes); - OpenEXR::RgbaOutputFile output( - os, header, has_alpha ? OpenEXR::WRITE_RGBA : OpenEXR::WRITE_RGB); - // How many rows to write at once. Again, the OpenEXR documentation - // recommends writing the whole image in one call. - const int y_chunk_size = ysize; - std::vector output_rows(xsize * y_chunk_size); - - for (size_t start_y = 0; start_y < ysize; start_y += y_chunk_size) { - // Inclusive. - const size_t end_y = std::min(start_y + y_chunk_size - 1, ysize - 1); - output.setFrameBuffer(output_rows.data() - start_y * xsize, - /*xStride=*/1, /*yStride=*/xsize); - for (size_t y = start_y; y <= end_y; ++y) { - const uint8_t* in_row = &in[(y - start_y) * in_stride]; - OpenEXR::Rgba* const JXL_RESTRICT row_data = - &output_rows[(y - start_y) * xsize]; - for (size_t x = 0; x < xsize; ++x) { - const uint8_t* in_pixel = &in_row[4 * num_channels * x]; - float r = loadFloat(&in_pixel[0]); - float g = loadFloat(&in_pixel[4]); - float b = loadFloat(&in_pixel[8]); - const float alpha = loadAlpha(&in_pixel[12]); - if (!alpha_is_premultiplied) { - r *= alpha; - g *= alpha; - b *= alpha; - } - row_data[x] = OpenEXR::Rgba(r, g, b, alpha); - } - } - output.writePixels(/*numScanLines=*/end_y - start_y + 1); - } - } - - return true; -} - -class EXREncoder : public Encoder { - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 2, 3, 4}) { - for (const JxlDataType data_type : {JXL_TYPE_FLOAT}) { - for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { - formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, - /*data_type=*/data_type, - /*endianness=*/endianness, - /*align=*/0}); - } - } - } - return formats; - } - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - encoded_image->icc.clear(); - encoded_image->bitstreams.clear(); - encoded_image->bitstreams.reserve(ppf.frames.size()); - for (const auto& frame : ppf.frames) { - JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); - encoded_image->bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR(EncodeImageEXR(frame.color, ppf.info, - ppf.color_encoding, pool, - &encoded_image->bitstreams.back())); - } - return true; - } -}; - -} // namespace -#endif - -std::unique_ptr GetEXREncoder() { -#if JPEGXL_ENABLE_EXR - return jxl::make_unique(); -#else - return nullptr; -#endif -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/exr.h b/third_party/jpeg-xl/lib/extras/enc/exr.h deleted file mode 100644 index 1baaa0272f5eb..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/exr.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_EXR_H_ -#define LIB_EXTRAS_ENC_EXR_H_ - -// Encodes OpenEXR images in memory. - -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetEXREncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_EXR_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc deleted file mode 100644 index 52de317399a6e..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/jpegli.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/extras/enc/encode.h" -#include "lib/extras/packed_image.h" -#include "lib/jpegli/common.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/types.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/enc_xyb.h" -#include "lib/jxl/simd_util.h" - -namespace jxl { -namespace extras { - -namespace { - -void MyErrorExit(j_common_ptr cinfo) { - jmp_buf* env = static_cast(cinfo->client_data); - (*cinfo->err->output_message)(cinfo); - jpegli_destroy_compress(reinterpret_cast(cinfo)); - longjmp(*env, 1); -} - -Status VerifyInput(const PackedPixelFile& ppf) { - const JxlBasicInfo& info = ppf.info; - JXL_RETURN_IF_ERROR(Encoder::VerifyBasicInfo(info)); - if (ppf.frames.size() != 1) { - return JXL_FAILURE("JPEG input must have exactly one frame."); - } - if (info.num_color_channels != 1 && info.num_color_channels != 3) { - return JXL_FAILURE("Invalid number of color channels %d", - info.num_color_channels); - } - const PackedImage& image = ppf.frames[0].color; - JXL_RETURN_IF_ERROR(Encoder::VerifyImageSize(image, info)); - if (image.format.data_type == JXL_TYPE_FLOAT16) { - return JXL_FAILURE("FLOAT16 input is not supported."); - } - JXL_RETURN_IF_ERROR(Encoder::VerifyBitDepth(image.format.data_type, - info.bits_per_sample, - info.exponent_bits_per_sample)); - if ((image.format.data_type == JXL_TYPE_UINT8 && info.bits_per_sample != 8) || - (image.format.data_type == JXL_TYPE_UINT16 && - info.bits_per_sample != 16)) { - return JXL_FAILURE("Only full bit depth unsigned types are supported."); - } - return true; -} - -Status GetColorEncoding(const PackedPixelFile& ppf, - ColorEncoding* color_encoding) { - if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { - IccBytes icc = ppf.icc; - JXL_RETURN_IF_ERROR( - color_encoding->SetICC(std::move(icc), JxlGetDefaultCms())); - } else { - JXL_RETURN_IF_ERROR(color_encoding->FromExternal(ppf.color_encoding)); - } - if (color_encoding->ICC().empty()) { - return JXL_FAILURE("Invalid color encoding."); - } - return true; -} - -bool HasICCProfile(const std::vector& app_data) { - size_t pos = 0; - while (pos < app_data.size()) { - if (pos + 16 > app_data.size()) return false; - uint8_t marker = app_data[pos + 1]; - size_t marker_len = (app_data[pos + 2] << 8) + app_data[pos + 3] + 2; - if (marker == 0xe2 && memcmp(&app_data[pos + 4], "ICC_PROFILE", 12) == 0) { - return true; - } - pos += marker_len; - } - return false; -} - -Status WriteAppData(j_compress_ptr cinfo, - const std::vector& app_data) { - size_t pos = 0; - while (pos < app_data.size()) { - if (pos + 4 > app_data.size()) { - return JXL_FAILURE("Incomplete APP header."); - } - uint8_t marker = app_data[pos + 1]; - size_t marker_len = (app_data[pos + 2] << 8) + app_data[pos + 3] + 2; - if (app_data[pos] != 0xff || marker < 0xe0 || marker > 0xef) { - return JXL_FAILURE("Invalid APP marker %02x %02x", app_data[pos], marker); - } - if (marker_len <= 4) { - return JXL_FAILURE("Invalid APP marker length."); - } - if (pos + marker_len > app_data.size()) { - return JXL_FAILURE("Incomplete APP data"); - } - jpegli_write_marker(cinfo, marker, &app_data[pos + 4], marker_len - 4); - pos += marker_len; - } - return true; -} - -constexpr int kICCMarker = 0xe2; -constexpr unsigned char kICCSignature[12] = { - 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -constexpr uint8_t kUnknownTf = 2; -constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70}; -constexpr size_t kCICPTagSize = 12; - -bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk, - size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag, - size_t* cicp_pos) { - if (is_first_chunk) { - // Look up the offset of the CICP tag from the first chunk of ICC data. - if (len < 132) { - return false; - } - uint32_t tag_count = LoadBE32(&icc_data[128]); - if (len < 132 + 12 * tag_count) { - return false; - } - for (uint32_t i = 0; i < tag_count; ++i) { - if (memcmp(&icc_data[132 + 12 * i], kCICPTagSignature, 4) == 0) { - *cicp_offset = LoadBE32(&icc_data[136 + 12 * i]); - *cicp_length = LoadBE32(&icc_data[140 + 12 * i]); - } - } - if (*cicp_length < kCICPTagSize) { - return false; - } - } - if (*cicp_offset < len) { - size_t n_bytes = std::min(len - *cicp_offset, kCICPTagSize - *cicp_pos); - memcpy(&cicp_tag[*cicp_pos], &icc_data[*cicp_offset], n_bytes); - *cicp_pos += n_bytes; - *cicp_offset = 0; - } else { - *cicp_offset -= len; - } - return true; -} - -uint8_t LookupCICPTransferFunctionFromAppData(const uint8_t* app_data, - size_t len) { - size_t last_index = 0; - size_t cicp_offset = 0; - size_t cicp_length = 0; - uint8_t cicp_tag[kCICPTagSize] = {}; - size_t cicp_pos = 0; - size_t pos = 0; - while (pos < len) { - const uint8_t* marker = &app_data[pos]; - if (pos + 4 > len) { - return kUnknownTf; - } - size_t marker_size = (marker[2] << 8) + marker[3] + 2; - if (pos + marker_size > len) { - return kUnknownTf; - } - if (marker_size < 18 || marker[0] != 0xff || marker[1] != kICCMarker || - memcmp(&marker[4], kICCSignature, 12) != 0) { - pos += marker_size; - continue; - } - uint8_t index = marker[16]; - uint8_t total = marker[17]; - const uint8_t* payload = marker + 18; - const size_t payload_size = marker_size - 18; - if (index != last_index + 1 || index > total) { - return kUnknownTf; - } - if (!FindCICPTag(payload, payload_size, last_index == 0, &cicp_offset, - &cicp_length, &cicp_tag[0], &cicp_pos)) { - return kUnknownTf; - } - if (cicp_pos == kCICPTagSize) { - break; - } - ++last_index; - } - if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) { - return cicp_tag[9]; - } - return kUnknownTf; -} - -uint8_t LookupCICPTransferFunctionFromICCProfile(const uint8_t* icc_data, - size_t len) { - size_t cicp_offset = 0; - size_t cicp_length = 0; - uint8_t cicp_tag[kCICPTagSize] = {}; - size_t cicp_pos = 0; - if (!FindCICPTag(icc_data, len, true, &cicp_offset, &cicp_length, - &cicp_tag[0], &cicp_pos)) { - return kUnknownTf; - } - if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) { - return cicp_tag[9]; - } - return kUnknownTf; -} - -JpegliDataType ConvertDataType(JxlDataType type) { - switch (type) { - case JXL_TYPE_UINT8: - return JPEGLI_TYPE_UINT8; - case JXL_TYPE_UINT16: - return JPEGLI_TYPE_UINT16; - case JXL_TYPE_FLOAT: - return JPEGLI_TYPE_FLOAT; - default: - return JPEGLI_TYPE_UINT8; - } -} - -JpegliEndianness ConvertEndianness(JxlEndianness endianness) { - switch (endianness) { - case JXL_NATIVE_ENDIAN: - return JPEGLI_NATIVE_ENDIAN; - case JXL_LITTLE_ENDIAN: - return JPEGLI_LITTLE_ENDIAN; - case JXL_BIG_ENDIAN: - return JPEGLI_BIG_ENDIAN; - default: - return JPEGLI_NATIVE_ENDIAN; - } -} - -void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t xsize, - size_t c_out, float* row_out) { - bool is_little_endian = - (format.endianness == JXL_LITTLE_ENDIAN || - (format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian())); - static constexpr double kMul8 = 1.0 / 255.0; - static constexpr double kMul16 = 1.0 / 65535.0; - const size_t c_in = format.num_channels; - if (format.data_type == JXL_TYPE_UINT8) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < c_out; ++c) { - const size_t ix = c_in * x + c; - row_out[c_out * x + c] = row_in[ix] * kMul8; - } - } - } else if (format.data_type == JXL_TYPE_UINT16 && is_little_endian) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < c_out; ++c) { - const size_t ix = c_in * x + c; - row_out[c_out * x + c] = LoadLE16(&row_in[2 * ix]) * kMul16; - } - } - } else if (format.data_type == JXL_TYPE_UINT16 && !is_little_endian) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < c_out; ++c) { - const size_t ix = c_in * x + c; - row_out[c_out * x + c] = LoadBE16(&row_in[2 * ix]) * kMul16; - } - } - } else if (format.data_type == JXL_TYPE_FLOAT && is_little_endian) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < c_out; ++c) { - const size_t ix = c_in * x + c; - row_out[c_out * x + c] = LoadLEFloat(&row_in[4 * ix]); - } - } - } else if (format.data_type == JXL_TYPE_FLOAT && !is_little_endian) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < c_out; ++c) { - const size_t ix = c_in * x + c; - row_out[c_out * x + c] = LoadBEFloat(&row_in[4 * ix]); - } - } - } -} - -Status EncodeJpegToTargetSize(const PackedPixelFile& ppf, - const JpegSettings& jpeg_settings, - size_t target_size, ThreadPool* pool, - std::vector* output) { - output->clear(); - size_t best_error = std::numeric_limits::max(); - float distance0 = -1.0f; - float distance1 = -1.0f; - float distance = 1.0f; - for (int step = 0; step < 15; ++step) { - JpegSettings settings = jpeg_settings; - settings.libjpeg_quality = 0; - settings.distance = distance; - settings.target_size = 0; - std::vector compressed; - JXL_RETURN_IF_ERROR(EncodeJpeg(ppf, settings, pool, &compressed)); - size_t size = compressed.size(); - // prefer being under the target size to being over it - size_t error = size < target_size - ? target_size - size - : static_cast(1.2f * (size - target_size)); - if (error < best_error) { - best_error = error; - std::swap(*output, compressed); - } - float rel_error = size * 1.0f / target_size; - if (std::abs(rel_error - 1.0f) < 0.002f) { - break; - } - if (size < target_size) { - distance1 = distance; - } else { - distance0 = distance; - } - if (distance1 == -1) { - distance *= std::pow(rel_error, 1.5) * 1.05; - } else if (distance0 == -1) { - distance *= std::pow(rel_error, 1.5) * 0.95; - } else { - distance = 0.5 * (distance0 + distance1); - } - } - return true; -} - -} // namespace - -Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, - ThreadPool* pool, std::vector* compressed) { - if (jpeg_settings.libjpeg_quality > 0) { - auto encoder = Encoder::FromExtension(".jpg"); - encoder->SetOption("q", std::to_string(jpeg_settings.libjpeg_quality)); - if (!jpeg_settings.libjpeg_chroma_subsampling.empty()) { - encoder->SetOption("chroma_subsampling", - jpeg_settings.libjpeg_chroma_subsampling); - } - EncodedImage encoded; - JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, pool)); - size_t target_size = encoded.bitstreams[0].size(); - return EncodeJpegToTargetSize(ppf, jpeg_settings, target_size, pool, - compressed); - } - if (jpeg_settings.target_size > 0) { - return EncodeJpegToTargetSize(ppf, jpeg_settings, jpeg_settings.target_size, - pool, compressed); - } - JXL_RETURN_IF_ERROR(VerifyInput(ppf)); - - ColorEncoding color_encoding; - JXL_RETURN_IF_ERROR(GetColorEncoding(ppf, &color_encoding)); - - ColorSpaceTransform c_transform(*JxlGetDefaultCms()); - ColorEncoding xyb_encoding; - if (jpeg_settings.xyb) { - if (HasICCProfile(jpeg_settings.app_data)) { - return JXL_FAILURE("APP data ICC profile is not supported in XYB mode."); - } - const ColorEncoding& c_desired = ColorEncoding::LinearSRGB(false); - JXL_RETURN_IF_ERROR( - c_transform.Init(color_encoding, c_desired, 255.0f, ppf.info.xsize, 1)); - xyb_encoding.SetColorSpace(jxl::ColorSpace::kXYB); - xyb_encoding.SetRenderingIntent(jxl::RenderingIntent::kPerceptual); - JXL_RETURN_IF_ERROR(xyb_encoding.CreateICC()); - } - const ColorEncoding& output_encoding = - jpeg_settings.xyb ? xyb_encoding : color_encoding; - - // We need to declare all the non-trivial destructor local variables - // before the call to setjmp(). - std::vector pixels; - unsigned char* output_buffer = nullptr; - unsigned long output_size = 0; // NOLINT - std::vector row_bytes; - const size_t max_vector_size = MaxVectorSize(); - size_t rowlen = RoundUpTo(ppf.info.xsize, max_vector_size); - hwy::AlignedFreeUniquePtr xyb_tmp = - hwy::AllocateAligned(6 * rowlen); - hwy::AlignedFreeUniquePtr premul_absorb = - hwy::AllocateAligned(max_vector_size * 12); - ComputePremulAbsorb(255.0f, premul_absorb.get()); - - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - jpeg_error_mgr jerr; - jmp_buf env; - cinfo.err = jpegli_std_error(&jerr); - jerr.error_exit = &MyErrorExit; - if (setjmp(env)) { - return false; - } - cinfo.client_data = static_cast(&env); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &output_buffer, &output_size); - const JxlBasicInfo& info = ppf.info; - cinfo.image_width = info.xsize; - cinfo.image_height = info.ysize; - cinfo.input_components = info.num_color_channels; - cinfo.in_color_space = - cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB; - if (jpeg_settings.xyb) { - jpegli_set_xyb_mode(&cinfo); - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - } else if (jpeg_settings.use_std_quant_tables) { - jpegli_use_standard_quant_tables(&cinfo); - } - uint8_t cicp_tf = kUnknownTf; - if (!jpeg_settings.app_data.empty()) { - cicp_tf = LookupCICPTransferFunctionFromAppData( - jpeg_settings.app_data.data(), jpeg_settings.app_data.size()); - } else if (!output_encoding.IsSRGB()) { - cicp_tf = LookupCICPTransferFunctionFromICCProfile( - output_encoding.ICC().data(), output_encoding.ICC().size()); - } - jpegli_set_cicp_transfer_function(&cinfo, cicp_tf); - jpegli_set_defaults(&cinfo); - if (!jpeg_settings.chroma_subsampling.empty()) { - if (jpeg_settings.chroma_subsampling == "444") { - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - } else if (jpeg_settings.chroma_subsampling == "440") { - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 2; - } else if (jpeg_settings.chroma_subsampling == "422") { - cinfo.comp_info[0].h_samp_factor = 2; - cinfo.comp_info[0].v_samp_factor = 1; - } else if (jpeg_settings.chroma_subsampling == "420") { - cinfo.comp_info[0].h_samp_factor = 2; - cinfo.comp_info[0].v_samp_factor = 2; - } else { - return false; - } - for (int i = 1; i < cinfo.num_components; ++i) { - cinfo.comp_info[i].h_samp_factor = 1; - cinfo.comp_info[i].v_samp_factor = 1; - } - } else if (!jpeg_settings.xyb) { - // Default is no chroma subsampling. - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - } - jpegli_enable_adaptive_quantization( - &cinfo, TO_JXL_BOOL(jpeg_settings.use_adaptive_quantization)); - if (jpeg_settings.psnr_target > 0.0) { - jpegli_set_psnr(&cinfo, jpeg_settings.psnr_target, - jpeg_settings.search_tolerance, - jpeg_settings.min_distance, jpeg_settings.max_distance); - } else if (jpeg_settings.quality > 0.0) { - float distance = jpegli_quality_to_distance(jpeg_settings.quality); - jpegli_set_distance(&cinfo, distance, TRUE); - } else { - jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE); - } - jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level); - cinfo.optimize_coding = TO_JXL_BOOL(jpeg_settings.optimize_coding); - if (!jpeg_settings.app_data.empty()) { - // Make sure jpegli_start_compress() does not write any APP markers. - cinfo.write_JFIF_header = JXL_FALSE; - cinfo.write_Adobe_marker = JXL_FALSE; - } - const PackedImage& image = ppf.frames[0].color; - if (jpeg_settings.xyb) { - jpegli_set_input_format(&cinfo, JPEGLI_TYPE_FLOAT, JPEGLI_NATIVE_ENDIAN); - } else { - jpegli_set_input_format(&cinfo, ConvertDataType(image.format.data_type), - ConvertEndianness(image.format.endianness)); - } - jpegli_start_compress(&cinfo, TRUE); - if (!jpeg_settings.app_data.empty()) { - JXL_RETURN_IF_ERROR(WriteAppData(&cinfo, jpeg_settings.app_data)); - } - if ((jpeg_settings.app_data.empty() && !output_encoding.IsSRGB()) || - jpeg_settings.xyb) { - jpegli_write_icc_profile(&cinfo, output_encoding.ICC().data(), - output_encoding.ICC().size()); - } - const uint8_t* pixels = reinterpret_cast(image.pixels()); - if (jpeg_settings.xyb) { - float* src_buf = c_transform.BufSrc(0); - float* dst_buf = c_transform.BufDst(0); - for (size_t y = 0; y < image.ysize; ++y) { - // convert to float - ToFloatRow(&pixels[y * image.stride], image.format, image.xsize, - info.num_color_channels, src_buf); - // convert to linear srgb - if (!c_transform.Run(0, src_buf, dst_buf, image.xsize)) { - return false; - } - // deinterleave channels - float* row0 = &xyb_tmp[0]; - float* row1 = &xyb_tmp[rowlen]; - float* row2 = &xyb_tmp[2 * rowlen]; - for (size_t x = 0; x < image.xsize; ++x) { - row0[x] = dst_buf[3 * x + 0]; - row1[x] = dst_buf[3 * x + 1]; - row2[x] = dst_buf[3 * x + 2]; - } - // convert to xyb - LinearRGBRowToXYB(row0, row1, row2, premul_absorb.get(), image.xsize); - // scale xyb - ScaleXYBRow(row0, row1, row2, image.xsize); - // interleave channels - float* row_out = &xyb_tmp[3 * rowlen]; - for (size_t x = 0; x < image.xsize; ++x) { - row_out[3 * x + 0] = row0[x]; - row_out[3 * x + 1] = row1[x]; - row_out[3 * x + 2] = row2[x]; - } - // feed to jpegli as native endian floats - JSAMPROW row[] = {reinterpret_cast(row_out)}; - jpegli_write_scanlines(&cinfo, row, 1); - } - } else { - row_bytes.resize(image.stride); - if (cinfo.num_components == static_cast(image.format.num_channels)) { - for (size_t y = 0; y < info.ysize; ++y) { - memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); - JSAMPROW row[] = {row_bytes.data()}; - jpegli_write_scanlines(&cinfo, row, 1); - } - } else { - for (size_t y = 0; y < info.ysize; ++y) { - JXL_RETURN_IF_ERROR( - PackedImage::ValidateDataType(image.format.data_type)); - int bytes_per_channel = - PackedImage::BitsPerChannel(image.format.data_type) / 8; - int bytes_per_pixel = cinfo.num_components * bytes_per_channel; - for (size_t x = 0; x < info.xsize; ++x) { - memcpy(&row_bytes[x * bytes_per_pixel], - &pixels[y * image.stride + x * image.pixel_stride()], - bytes_per_pixel); - } - JSAMPROW row[] = {row_bytes.data()}; - jpegli_write_scanlines(&cinfo, row, 1); - } - } - } - jpegli_finish_compress(&cinfo); - compressed->resize(output_size); - std::copy_n(output_buffer, output_size, compressed->data()); - return true; - }; - bool success = try_catch_block(); - jpegli_destroy_compress(&cinfo); - if (output_buffer) free(output_buffer); - return success; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.h b/third_party/jpeg-xl/lib/extras/enc/jpegli.h deleted file mode 100644 index 9538b2e3fc92e..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jpegli.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_JPEGLI_H_ -#define LIB_EXTRAS_ENC_JPEGLI_H_ - -// Encodes JPG pixels and metadata in memory using the libjpegli library. - -#include - -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -struct JpegSettings { - bool xyb = false; - size_t target_size = 0; - float quality = 0.0f; - float distance = 1.f; - bool use_adaptive_quantization = true; - bool use_std_quant_tables = false; - int progressive_level = 2; - bool optimize_coding = true; - std::string chroma_subsampling; - int libjpeg_quality = 0; - std::string libjpeg_chroma_subsampling; - // Parameters for selecting distance based on PSNR target. - float psnr_target = 0.0f; - float search_tolerance = 0.01; - float min_distance = 0.1f; - float max_distance = 25.0f; - // If not empty, must contain concatenated APP marker segments. In this case, - // these and only these APP marker segments will be written to the JPEG - // output. In xyb mode app_data must not contain an ICC profile, in this - // case an additional APP2 ICC profile for the XYB colorspace will be emitted. - std::vector app_data; -}; - -Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, - ThreadPool* pool, std::vector* compressed); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_JPEGLI_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc deleted file mode 100644 index a9209ffce0eec..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc +++ /dev/null @@ -1,618 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/jpg.h" - -#if JPEGXL_ENABLE_JPEG -#include "lib/jxl/base/include_jpeglib.h" // NOLINT -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/extras/exif.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/sanitizers.h" -#include "lib/jxl/base/status.h" -#if JPEGXL_ENABLE_SJPEG -#include "sjpeg.h" -#include "sjpegi.h" -#endif - -namespace jxl { -namespace extras { - -#if JPEGXL_ENABLE_JPEG -namespace { - -constexpr unsigned char kICCSignature[12] = { - 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -constexpr int kICCMarker = JPEG_APP0 + 2; -constexpr size_t kMaxBytesInMarker = 65533; - -constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, - 0x66, 0x00, 0x00}; -constexpr int kExifMarker = JPEG_APP0 + 1; - -enum class JpegEncoder { - kLibJpeg, - kSJpeg, -}; - -// Popular jpeg scan scripts -// The fields of the individual scans are: -// comps_in_scan, component_index[], Ss, Se, Ah, Al -constexpr auto kScanScript1 = to_array({ - {1, {0}, 0, 0, 0, 0}, // - {1, {1}, 0, 0, 0, 0}, // - {1, {2}, 0, 0, 0, 0}, // - {1, {0}, 1, 8, 0, 0}, // - {1, {0}, 9, 63, 0, 0}, // - {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0} // -}); -constexpr size_t kNumScans1 = kScanScript1.size(); - -constexpr auto kScanScript2 = to_array({ - {1, {0}, 0, 0, 0, 0}, // - {1, {1}, 0, 0, 0, 0}, // - {1, {2}, 0, 0, 0, 0}, // - {1, {0}, 1, 2, 0, 1}, // - {1, {0}, 3, 63, 0, 1}, // - {1, {0}, 1, 63, 1, 0}, // - {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0} // -}); -constexpr size_t kNumScans2 = kScanScript2.size(); - -constexpr auto kScanScript3 = to_array({ - {1, {0}, 0, 0, 0, 0}, // - {1, {1}, 0, 0, 0, 0}, // - {1, {2}, 0, 0, 0, 0}, // - {1, {0}, 1, 63, 0, 2}, // - {1, {0}, 1, 63, 2, 1}, // - {1, {0}, 1, 63, 1, 0}, // - {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0} // -}); -constexpr size_t kNumScans3 = kScanScript3.size(); - -constexpr auto kScanScript4 = to_array({ - {3, {0, 1, 2}, 0, 0, 0, 1}, // - {1, {0}, 1, 5, 0, 2}, // - {1, {2}, 1, 63, 0, 1}, // - {1, {1}, 1, 63, 0, 1}, // - {1, {0}, 6, 63, 0, 2}, // - {1, {0}, 1, 63, 2, 1}, // - {3, {0, 1, 2}, 0, 0, 1, 0}, // - {1, {2}, 1, 63, 1, 0}, // - {1, {1}, 1, 63, 1, 0}, // - {1, {0}, 1, 63, 1, 0} // -}); -constexpr size_t kNumScans4 = kScanScript4.size(); - -constexpr auto kScanScript5 = to_array({ - {3, {0, 1, 2}, 0, 0, 0, 1}, // - {1, {0}, 1, 5, 0, 2}, // - {1, {1}, 1, 5, 0, 2}, // - {1, {2}, 1, 5, 0, 2}, // - {1, {1}, 6, 63, 0, 2}, // - {1, {2}, 6, 63, 0, 2}, // - {1, {0}, 6, 63, 0, 2}, // - {1, {0}, 1, 63, 2, 1}, // - {1, {1}, 1, 63, 2, 1}, // - {1, {2}, 1, 63, 2, 1}, // - {3, {0, 1, 2}, 0, 0, 1, 0}, // - {1, {0}, 1, 63, 1, 0}, // - {1, {1}, 1, 63, 1, 0}, // - {1, {2}, 1, 63, 1, 0} // -}); -constexpr size_t kNumScans5 = kScanScript5.size(); - -// default progressive mode of jpegli -constexpr auto kScanScript6 = to_array({ - {3, {0, 1, 2}, 0, 0, 0, 0}, // - {1, {0}, 1, 2, 0, 0}, // - {1, {1}, 1, 2, 0, 0}, // - {1, {2}, 1, 2, 0, 0}, // - {1, {0}, 3, 63, 0, 2}, // - {1, {1}, 3, 63, 0, 2}, // - {1, {2}, 3, 63, 0, 2}, // - {1, {0}, 3, 63, 2, 1}, // - {1, {1}, 3, 63, 2, 1}, // - {1, {2}, 3, 63, 2, 1}, // - {1, {0}, 3, 63, 1, 0}, // - {1, {1}, 3, 63, 1, 0}, // - {1, {2}, 3, 63, 1, 0}, // -}); -constexpr size_t kNumScans6 = kScanScript6.size(); - -// Adapt RGB scan info to grayscale jpegs. -void FilterScanComponents(const jpeg_compress_struct* cinfo, - jpeg_scan_info* si) { - const int all_comps_in_scan = si->comps_in_scan; - si->comps_in_scan = 0; - for (int j = 0; j < all_comps_in_scan; ++j) { - const int component = si->component_index[j]; - if (component < cinfo->input_components) { - si->component_index[si->comps_in_scan++] = component; - } - } -} - -Status SetJpegProgression(int progressive_id, - std::vector* scan_infos, - jpeg_compress_struct* cinfo) { - if (progressive_id < 0) { - return true; - } - if (progressive_id == 0) { - jpeg_simple_progression(cinfo); - return true; - } - const jpeg_scan_info* kScanScripts[] = { - kScanScript1.data(), kScanScript2.data(), kScanScript3.data(), - kScanScript4.data(), kScanScript5.data(), kScanScript6.data()}; - constexpr auto kNumScans = to_array( - {kNumScans1, kNumScans2, kNumScans3, kNumScans4, kNumScans5, kNumScans6}); - if (progressive_id > static_cast(kNumScans.size())) { - return JXL_FAILURE("Unknown jpeg scan script id %d", progressive_id); - } - const jpeg_scan_info* scan_script = kScanScripts[progressive_id - 1]; - const size_t num_scans = kNumScans[progressive_id - 1]; - // filter scan script for number of components - for (size_t i = 0; i < num_scans; ++i) { - jpeg_scan_info scan_info = scan_script[i]; - FilterScanComponents(cinfo, &scan_info); - if (scan_info.comps_in_scan > 0) { - scan_infos->emplace_back(scan_info); - } - } - cinfo->scan_info = scan_infos->data(); - cinfo->num_scans = scan_infos->size(); - return true; -} - -void WriteICCProfile(jpeg_compress_struct* const cinfo, - const std::vector& icc) { - constexpr size_t kMaxIccBytesInMarker = - kMaxBytesInMarker - sizeof kICCSignature - 2; - const int num_markers = - static_cast(DivCeil(icc.size(), kMaxIccBytesInMarker)); - size_t begin = 0; - for (int current_marker = 0; current_marker < num_markers; ++current_marker) { - const size_t length = std::min(kMaxIccBytesInMarker, icc.size() - begin); - jpeg_write_m_header( - cinfo, kICCMarker, - static_cast(length + sizeof kICCSignature + 2)); - for (const unsigned char c : kICCSignature) { - jpeg_write_m_byte(cinfo, c); - } - jpeg_write_m_byte(cinfo, current_marker + 1); - jpeg_write_m_byte(cinfo, num_markers); - for (size_t i = 0; i < length; ++i) { - jpeg_write_m_byte(cinfo, icc[begin]); - ++begin; - } - } -} -void WriteExif(jpeg_compress_struct* const cinfo, - const std::vector& exif) { - jpeg_write_m_header( - cinfo, kExifMarker, - static_cast(exif.size() + sizeof kExifSignature)); - for (const unsigned char c : kExifSignature) { - jpeg_write_m_byte(cinfo, c); - } - for (uint8_t c : exif) { - jpeg_write_m_byte(cinfo, c); - } -} - -Status SetChromaSubsampling(const std::string& subsampling, - jpeg_compress_struct* const cinfo) { - const std::pair, std::array>> - options[] = {{"444", {{{1, 1, 1}}, {{1, 1, 1}}}}, - {"420", {{{2, 1, 1}}, {{2, 1, 1}}}}, - {"422", {{{2, 1, 1}}, {{1, 1, 1}}}}, - {"440", {{{1, 1, 1}}, {{2, 1, 1}}}}}; - for (const auto& option : options) { - if (subsampling == option.first) { - for (size_t i = 0; i < 3; i++) { - cinfo->comp_info[i].h_samp_factor = option.second.first[i]; - cinfo->comp_info[i].v_samp_factor = option.second.second[i]; - } - return true; - } - } - return false; -} - -struct JpegParams { - // Common between sjpeg and libjpeg - int quality = 100; - std::string chroma_subsampling = "444"; - // Libjpeg parameters - int progressive_id = -1; - bool optimize_coding = true; - bool is_xyb = false; - // Sjpeg parameters - int libjpeg_quality = 0; - std::string libjpeg_chroma_subsampling = "444"; - float psnr_target = 0; - std::string custom_base_quant_fn; - float search_q_start = 65.0f; - float search_q_min = 1.0f; - float search_q_max = 100.0f; - int search_max_iters = 20; - float search_tolerance = 0.1f; - float search_q_precision = 0.01f; - float search_first_iter_slope = 3.0f; - bool enable_adaptive_quant = true; -}; - -Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info, - const std::vector& icc, - std::vector exif, const JpegParams& params, - std::vector* bytes) { - if (BITS_IN_JSAMPLE != 8 || sizeof(JSAMPLE) != 1) { - return JXL_FAILURE("Only 8 bit JSAMPLE is supported."); - } - jpeg_compress_struct cinfo = {}; - jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - unsigned char* buffer = nullptr; -#ifdef LIBJPEG_TURBO_VERSION - unsigned long size = 0; // NOLINT -#else - size_t size = 0; // NOLINT -#endif - jpeg_mem_dest(&cinfo, &buffer, &size); - cinfo.image_width = image.xsize; - cinfo.image_height = image.ysize; - cinfo.input_components = info.num_color_channels; - cinfo.in_color_space = info.num_color_channels == 1 ? JCS_GRAYSCALE : JCS_RGB; - jpeg_set_defaults(&cinfo); - cinfo.optimize_coding = static_cast(params.optimize_coding); - if (cinfo.input_components == 3) { - JXL_RETURN_IF_ERROR( - SetChromaSubsampling(params.chroma_subsampling, &cinfo)); - } - if (params.is_xyb) { - // Tell libjpeg not to convert XYB data to YCbCr. - jpeg_set_colorspace(&cinfo, JCS_RGB); - } - jpeg_set_quality(&cinfo, params.quality, TRUE); - std::vector scan_infos; - JXL_RETURN_IF_ERROR( - SetJpegProgression(params.progressive_id, &scan_infos, &cinfo)); - jpeg_start_compress(&cinfo, TRUE); - if (!icc.empty()) { - WriteICCProfile(&cinfo, icc); - } - if (!exif.empty()) { - ResetExifOrientation(exif); - WriteExif(&cinfo, exif); - } - if (cinfo.input_components > 3 || cinfo.input_components < 0) - return JXL_FAILURE("invalid numbers of components"); - - std::vector row_bytes(image.stride); - const uint8_t* pixels = reinterpret_cast(image.pixels()); - if (cinfo.num_components == static_cast(image.format.num_channels) && - image.format.data_type == JXL_TYPE_UINT8) { - for (size_t y = 0; y < info.ysize; ++y) { - memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); - JSAMPROW row[] = {row_bytes.data()}; - jpeg_write_scanlines(&cinfo, row, 1); - } - } else if (image.format.data_type == JXL_TYPE_UINT8) { - for (size_t y = 0; y < info.ysize; ++y) { - const uint8_t* image_row = pixels + y * image.stride; - for (size_t x = 0; x < info.xsize; ++x) { - const uint8_t* image_pixel = image_row + x * image.pixel_stride(); - memcpy(&row_bytes[x * cinfo.num_components], image_pixel, - cinfo.num_components); - } - JSAMPROW row[] = {row_bytes.data()}; - jpeg_write_scanlines(&cinfo, row, 1); - } - } else { - for (size_t y = 0; y < info.ysize; ++y) { - const uint8_t* image_row = pixels + y * image.stride; - for (size_t x = 0; x < info.xsize; ++x) { - const uint8_t* image_pixel = image_row + x * image.pixel_stride(); - for (int c = 0; c < cinfo.num_components; ++c) { - uint32_t val16 = (image_pixel[2 * c] << 8) + image_pixel[2 * c + 1]; - row_bytes[x * cinfo.num_components + c] = (val16 + 128) / 257; - } - } - JSAMPROW row[] = {row_bytes.data()}; - jpeg_write_scanlines(&cinfo, row, 1); - } - } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - bytes->resize(size); - // Compressed image data is initialized by libjpeg, which we are not - // instrumenting with msan. - msan::UnpoisonMemory(buffer, size); - std::copy_n(buffer, size, bytes->data()); - std::free(buffer); - return true; -} - -#if JPEGXL_ENABLE_SJPEG -struct MySearchHook : public sjpeg::SearchHook { - uint8_t base_tables[2][64]; - float q_start; - float q_precision; - float first_iter_slope; - void ReadBaseTables(const std::string& fn) { - const uint8_t kJPEGAnnexKMatrices[2][64] = { - {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, - {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}; - memcpy(base_tables[0], kJPEGAnnexKMatrices[0], sizeof(base_tables[0])); - memcpy(base_tables[1], kJPEGAnnexKMatrices[1], sizeof(base_tables[1])); - if (!fn.empty()) { - std::ifstream f(fn); - std::string line; - int idx = 0; - while (idx < 128 && std::getline(f, line)) { - if (line.empty() || line[0] == '#') continue; - std::istringstream line_stream(line); - std::string token; - while (idx < 128 && std::getline(line_stream, token, ',')) { - uint8_t val = std::stoi(token); - base_tables[idx / 64][idx % 64] = val; - idx++; - } - } - } - } - bool Setup(const sjpeg::EncoderParam& param) override { - sjpeg::SearchHook::Setup(param); - q = q_start; - return true; - } - void NextMatrix(int idx, uint8_t dst[64]) override { - float factor = (q <= 0) ? 5000.0f - : (q < 50.0f) ? 5000.0f / q - : (q < 100.0f) ? 2 * (100.0f - q) - : 0.0f; - sjpeg::SetQuantMatrix(base_tables[idx], factor, dst); - } - bool Update(float result) override { - value = result; - if (std::fabs(value - target) < tolerance * target) { - return true; - } - if (value > target) { - qmax = q; - } else { - qmin = q; - } - if (qmin == qmax) { - return true; - } - const float last_q = q; - if (pass == 0) { - q += first_iter_slope * - (for_size ? 0.1 * std::log(target / value) : (target - value)); - q = std::max(qmin, std::min(qmax, q)); - } else { - q = (qmin + qmax) / 2.; - } - return (pass > 0 && std::fabs(q - last_q) < q_precision); - } - ~MySearchHook() override = default; -}; -#endif - -Status EncodeWithSJpeg(const PackedImage& image, const JxlBasicInfo& info, - const std::vector& icc, - std::vector exif, const JpegParams& params, - std::vector* bytes) { -#if !JPEGXL_ENABLE_SJPEG - return JXL_FAILURE("JPEG XL was built without sjpeg support"); -#else - if (image.format.data_type != JXL_TYPE_UINT8) { - return JXL_FAILURE("Unsupported pixel data type"); - } - if (info.alpha_bits > 0) { - return JXL_FAILURE("alpha is not supported"); - } - sjpeg::EncoderParam param(params.quality); - if (!icc.empty()) { - param.iccp.assign(icc.begin(), icc.end()); - } - if (!exif.empty()) { - ResetExifOrientation(exif); - param.exif.assign(exif.begin(), exif.end()); - } - if (params.chroma_subsampling == "444") { - param.yuv_mode = SJPEG_YUV_444; - } else if (params.chroma_subsampling == "420") { - param.yuv_mode = SJPEG_YUV_420; - } else if (params.chroma_subsampling == "420sharp") { - param.yuv_mode = SJPEG_YUV_SHARP; - } else { - return JXL_FAILURE("sjpeg does not support this chroma subsampling mode"); - } - param.adaptive_quantization = params.enable_adaptive_quant; - std::unique_ptr hook; - if (params.libjpeg_quality > 0) { - JpegParams libjpeg_params; - libjpeg_params.quality = params.libjpeg_quality; - libjpeg_params.chroma_subsampling = params.libjpeg_chroma_subsampling; - std::vector libjpeg_bytes; - JXL_RETURN_IF_ERROR(EncodeWithLibJpeg(image, info, icc, exif, - libjpeg_params, &libjpeg_bytes)); - param.target_mode = sjpeg::EncoderParam::TARGET_SIZE; - param.target_value = libjpeg_bytes.size(); - } - if (params.psnr_target > 0) { - param.target_mode = sjpeg::EncoderParam::TARGET_PSNR; - param.target_value = params.psnr_target; - } - if (param.target_mode != sjpeg::EncoderParam::TARGET_NONE) { - param.passes = params.search_max_iters; - param.tolerance = params.search_tolerance; - param.qmin = params.search_q_min; - param.qmax = params.search_q_max; - hook = jxl::make_unique(); - hook->ReadBaseTables(params.custom_base_quant_fn); - hook->q_start = params.search_q_start; - hook->q_precision = params.search_q_precision; - hook->first_iter_slope = params.search_first_iter_slope; - param.search_hook = hook.get(); - } - size_t stride = info.xsize * 3; - const uint8_t* pixels = reinterpret_cast(image.pixels()); - std::string output; - JXL_RETURN_IF_ERROR( - sjpeg::Encode(pixels, image.xsize, image.ysize, stride, param, &output)); - bytes->assign( - reinterpret_cast(output.data()), - reinterpret_cast(output.data() + output.size())); - return true; -#endif -} - -Status EncodeImageJPG(const PackedImage& image, const JxlBasicInfo& info, - const std::vector& icc, - std::vector exif, JpegEncoder encoder, - const JpegParams& params, ThreadPool* pool, - std::vector* bytes) { - if (params.quality > 100) { - return JXL_FAILURE("please specify a 0-100 JPEG quality"); - } - - switch (encoder) { - case JpegEncoder::kLibJpeg: - JXL_RETURN_IF_ERROR( - EncodeWithLibJpeg(image, info, icc, std::move(exif), params, bytes)); - break; - case JpegEncoder::kSJpeg: - JXL_RETURN_IF_ERROR( - EncodeWithSJpeg(image, info, icc, std::move(exif), params, bytes)); - break; - default: - return JXL_FAILURE("tried to use an unknown JPEG encoder"); - } - - return true; -} - -class JPEGEncoder : public Encoder { - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 2, 3, 4}) { - for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { - formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, - /*data_type=*/JXL_TYPE_UINT8, - /*endianness=*/endianness, - /*align=*/0}); - } - formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, - /*data_type=*/JXL_TYPE_UINT16, - /*endianness=*/JXL_BIG_ENDIAN, - /*align=*/0}); - } - return formats; - } - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg; - JpegParams params; - for (const auto& it : options()) { - if (it.first == "q") { - std::istringstream is(it.second); - JXL_RETURN_IF_ERROR(static_cast(is >> params.quality)); - } else if (it.first == "libjpeg_quality") { - std::istringstream is(it.second); - JXL_RETURN_IF_ERROR(static_cast(is >> params.libjpeg_quality)); - } else if (it.first == "chroma_subsampling") { - params.chroma_subsampling = it.second; - } else if (it.first == "libjpeg_chroma_subsampling") { - params.libjpeg_chroma_subsampling = it.second; - } else if (it.first == "jpeg_encoder") { - if (it.second == "libjpeg") { - jpeg_encoder = JpegEncoder::kLibJpeg; - } else if (it.second == "sjpeg") { - jpeg_encoder = JpegEncoder::kSJpeg; - } else { - return JXL_FAILURE("unknown jpeg encoder \"%s\"", it.second.c_str()); - } - } else if (it.first == "progressive") { - std::istringstream is(it.second); - JXL_RETURN_IF_ERROR(static_cast(is >> params.progressive_id)); - } else if (it.first == "optimize" && it.second == "OFF") { - params.optimize_coding = false; - } else if (it.first == "adaptive_q" && it.second == "OFF") { - params.enable_adaptive_quant = false; - } else if (it.first == "psnr") { - params.psnr_target = std::stof(it.second); - } else if (it.first == "base_quant_fn") { - params.custom_base_quant_fn = it.second; - } else if (it.first == "search_q_start") { - params.search_q_start = std::stof(it.second); - } else if (it.first == "search_q_min") { - params.search_q_min = std::stof(it.second); - } else if (it.first == "search_q_max") { - params.search_q_max = std::stof(it.second); - } else if (it.first == "search_max_iters") { - params.search_max_iters = std::stoi(it.second); - } else if (it.first == "search_tolerance") { - params.search_tolerance = std::stof(it.second); - } else if (it.first == "search_q_precision") { - params.search_q_precision = std::stof(it.second); - } else if (it.first == "search_first_iter_slope") { - params.search_first_iter_slope = std::stof(it.second); - } - } - params.is_xyb = (ppf.color_encoding.color_space == JXL_COLOR_SPACE_XYB); - encoded_image->bitstreams.clear(); - encoded_image->bitstreams.reserve(ppf.frames.size()); - for (const auto& frame : ppf.frames) { - JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); - encoded_image->bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR(EncodeImageJPG( - frame.color, ppf.info, ppf.icc, ppf.metadata.exif, jpeg_encoder, - params, pool, &encoded_image->bitstreams.back())); - } - return true; - } -}; - -} // namespace -#endif - -std::unique_ptr GetJPEGEncoder() { -#if JPEGXL_ENABLE_JPEG - return jxl::make_unique(); -#else - return nullptr; -#endif -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.h b/third_party/jpeg-xl/lib/extras/enc/jpg.h deleted file mode 100644 index 20b37cd168446..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_JPG_H_ -#define LIB_EXTRAS_ENC_JPG_H_ - -// Encodes JPG pixels and metadata in memory. - -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetJPEGEncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_JPG_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc deleted file mode 100644 index 15f62673161da..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/jxl.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/exif.h" - -namespace jxl { -namespace extras { - -JxlEncoderStatus SetOption(const JXLOption& opt, - JxlEncoderFrameSettings* settings) { - return opt.is_float - ? JxlEncoderFrameSettingsSetFloatOption(settings, opt.id, opt.fval) - : JxlEncoderFrameSettingsSetOption(settings, opt.id, opt.ival); -} - -bool SetFrameOptions(const std::vector& options, size_t frame_index, - size_t* option_idx, JxlEncoderFrameSettings* settings) { - while (*option_idx < options.size()) { - const auto& opt = options[*option_idx]; - if (opt.frame_index > frame_index) { - break; - } - if (JXL_ENC_SUCCESS != SetOption(opt, settings)) { - fprintf(stderr, "Setting option id %d failed.\n", opt.id); - return false; - } - (*option_idx)++; - } - return true; -} - -bool SetupFrame(JxlEncoder* enc, JxlEncoderFrameSettings* settings, - const JxlFrameHeader& frame_header, - const JXLCompressParams& params, const PackedPixelFile& ppf, - size_t frame_index, size_t num_alpha_channels, - size_t num_interleaved_alpha, size_t& option_idx) { - if (JXL_ENC_SUCCESS != JxlEncoderSetFrameHeader(settings, &frame_header)) { - fprintf(stderr, "JxlEncoderSetFrameHeader() failed.\n"); - return false; - } - if (!SetFrameOptions(params.options, frame_index, &option_idx, settings)) { - return false; - } - if (num_alpha_channels > 0) { - JxlExtraChannelInfo extra_channel_info; - JxlEncoderInitExtraChannelInfo(JXL_CHANNEL_ALPHA, &extra_channel_info); - extra_channel_info.bits_per_sample = ppf.info.alpha_bits; - extra_channel_info.exponent_bits_per_sample = ppf.info.alpha_exponent_bits; - if (params.premultiply != -1) { - if (params.premultiply != 0 && params.premultiply != 1) { - fprintf(stderr, "premultiply must be one of: -1, 0, 1.\n"); - return false; - } - extra_channel_info.alpha_premultiplied = params.premultiply; - } - if (JXL_ENC_SUCCESS != - JxlEncoderSetExtraChannelInfo(enc, 0, &extra_channel_info)) { - fprintf(stderr, "JxlEncoderSetExtraChannelInfo() failed.\n"); - return false; - } - // We take the extra channel blend info frame_info, but don't do - // clamping. - JxlBlendInfo extra_channel_blend_info = frame_header.layer_info.blend_info; - extra_channel_blend_info.clamp = JXL_FALSE; - JxlEncoderSetExtraChannelBlendInfo(settings, 0, &extra_channel_blend_info); - } - // Add extra channel info for the rest of the extra channels. - for (size_t i = 0; i < ppf.info.num_extra_channels; ++i) { - if (i < ppf.extra_channels_info.size()) { - const auto& ec_info = ppf.extra_channels_info[i].ec_info; - if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelInfo( - enc, num_interleaved_alpha + i, &ec_info)) { - fprintf(stderr, "JxlEncoderSetExtraChannelInfo() failed.\n"); - return false; - } - } - } - return true; -} - -bool ReadCompressedOutput(JxlEncoder* enc, std::vector* compressed) { - compressed->clear(); - compressed->resize(4096); - uint8_t* next_out = compressed->data(); - size_t avail_out = compressed->size() - (next_out - compressed->data()); - JxlEncoderStatus result = JXL_ENC_NEED_MORE_OUTPUT; - while (result == JXL_ENC_NEED_MORE_OUTPUT) { - result = JxlEncoderProcessOutput(enc, &next_out, &avail_out); - if (result == JXL_ENC_NEED_MORE_OUTPUT) { - size_t offset = next_out - compressed->data(); - compressed->resize(compressed->size() * 2); - next_out = compressed->data() + offset; - avail_out = compressed->size() - offset; - } - } - compressed->resize(next_out - compressed->data()); - if (result != JXL_ENC_SUCCESS) { - fprintf(stderr, "JxlEncoderProcessOutput failed.\n"); - return false; - } - return true; -} - -bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, - const std::vector* jpeg_bytes, - std::vector* compressed) { - auto encoder = JxlEncoderMake(params.memory_manager); - JxlEncoder* enc = encoder.get(); - - if (params.allow_expert_options) { - JxlEncoderAllowExpertOptions(enc); - } - - if (params.runner_opaque != nullptr && - JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc, params.runner, - params.runner_opaque)) { - fprintf(stderr, "JxlEncoderSetParallelRunner failed\n"); - return false; - } - - if (params.HasOutputProcessor() && - JXL_ENC_SUCCESS != - JxlEncoderSetOutputProcessor(enc, params.output_processor)) { - fprintf(stderr, "JxlEncoderSetOutputProcessorfailed\n"); - return false; - } - - auto* settings = JxlEncoderFrameSettingsCreate(enc, nullptr); - size_t option_idx = 0; - if (!SetFrameOptions(params.options, 0, &option_idx, settings)) { - return false; - } - if (JXL_ENC_SUCCESS != - JxlEncoderSetFrameDistance(settings, params.distance)) { - fprintf(stderr, "Setting frame distance failed.\n"); - return false; - } - if (params.debug_image) { - JxlEncoderSetDebugImageCallback(settings, params.debug_image, - params.debug_image_opaque); - } - if (params.stats) { - JxlEncoderCollectStats(settings, params.stats); - } - - bool has_jpeg_bytes = (jpeg_bytes != nullptr); - bool use_boxes = !ppf.metadata.exif.empty() || !ppf.metadata.xmp.empty() || - !ppf.metadata.jhgm.empty() || !ppf.metadata.jumbf.empty() || - !ppf.metadata.iptc.empty(); - bool use_container = params.use_container || use_boxes || - (has_jpeg_bytes && params.jpeg_store_metadata); - - if (JXL_ENC_SUCCESS != - JxlEncoderUseContainer(enc, static_cast(use_container))) { - fprintf(stderr, "JxlEncoderUseContainer failed.\n"); - return false; - } - - if (has_jpeg_bytes) { - if (params.jpeg_store_metadata && - JXL_ENC_SUCCESS != JxlEncoderStoreJPEGMetadata(enc, JXL_TRUE)) { - fprintf(stderr, "Storing JPEG metadata failed.\n"); - return false; - } - if (params.jpeg_store_metadata && params.jpeg_strip_exif) { - fprintf(stderr, - "Cannot store metadata and strip exif at the same time.\n"); - return false; - } - if (params.jpeg_store_metadata && params.jpeg_strip_xmp) { - fprintf(stderr, - "Cannot store metadata and strip xmp at the same time.\n"); - return false; - } - if (!params.jpeg_store_metadata && params.jpeg_strip_exif) { - JxlEncoderFrameSettingsSetOption(settings, - JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF, 0); - } - if (!params.jpeg_store_metadata && params.jpeg_strip_xmp) { - JxlEncoderFrameSettingsSetOption(settings, - JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP, 0); - } - if (params.jpeg_strip_jumbf) { - JxlEncoderFrameSettingsSetOption( - settings, JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF, 0); - } - if (JXL_ENC_SUCCESS != JxlEncoderAddJPEGFrame(settings, jpeg_bytes->data(), - jpeg_bytes->size())) { - JxlEncoderError error = JxlEncoderGetError(enc); - if (error == JXL_ENC_ERR_BAD_INPUT) { - fprintf(stderr, - "Error while decoding the JPEG image. It may be corrupt (e.g. " - "truncated) or of an unsupported type (e.g. CMYK).\n"); - } else if (error == JXL_ENC_ERR_JBRD) { - fprintf(stderr, - "JPEG bitstream reconstruction data could not be created. " - "Possibly there is too much tail data.\n" - "Try using --allow_jpeg_reconstruction 0, to losslessly " - "recompress the JPEG image data without bitstream " - "reconstruction data.\n"); - } else { - fprintf(stderr, "JxlEncoderAddJPEGFrame() failed.\n"); - } - return false; - } - } else { - size_t num_alpha_channels = 0; // Adjusted below. - JxlBasicInfo basic_info = ppf.info; - basic_info.xsize *= params.already_downsampled; - basic_info.ysize *= params.already_downsampled; - if (basic_info.alpha_bits > 0) num_alpha_channels = 1; - if (params.intensity_target > 0) { - basic_info.intensity_target = params.intensity_target; - } - basic_info.num_extra_channels = - std::max(num_alpha_channels, ppf.info.num_extra_channels); - basic_info.num_color_channels = ppf.info.num_color_channels; - const bool lossless = (params.distance == 0); - auto non_perceptual_option = std::find_if( - params.options.begin(), params.options.end(), [](JXLOption option) { - return option.id == - JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS; - }); - const bool non_perceptual = non_perceptual_option != params.options.end() && - non_perceptual_option->ival == 1; - basic_info.uses_original_profile = TO_JXL_BOOL(lossless || non_perceptual); - if (params.override_bitdepth != 0) { - basic_info.bits_per_sample = params.override_bitdepth; - basic_info.exponent_bits_per_sample = - params.override_bitdepth == 32 ? 8 : 0; - } - if (JXL_ENC_SUCCESS != - JxlEncoderSetCodestreamLevel(enc, params.codestream_level)) { - fprintf(stderr, "Setting --codestream_level failed.\n"); - return false; - } - if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc, &basic_info)) { - fprintf(stderr, "JxlEncoderSetBasicInfo() failed.\n"); - return false; - } - if (JXL_ENC_SUCCESS != - JxlEncoderSetUpsamplingMode(enc, params.already_downsampled, - params.upsampling_mode)) { - fprintf(stderr, "JxlEncoderSetUpsamplingMode() failed.\n"); - return false; - } - if (JXL_ENC_SUCCESS != - JxlEncoderSetFrameBitDepth(settings, &ppf.input_bitdepth)) { - fprintf(stderr, "JxlEncoderSetFrameBitDepth() failed.\n"); - return false; - } - if (num_alpha_channels != 0 && - JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance( - settings, 0, params.alpha_distance)) { - fprintf(stderr, "Setting alpha distance failed.\n"); - return false; - } - if (lossless && - JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(settings, JXL_TRUE)) { - fprintf(stderr, "JxlEncoderSetFrameLossless() failed.\n"); - return false; - } - if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { - if (JXL_ENC_SUCCESS != - JxlEncoderSetICCProfile(enc, ppf.icc.data(), ppf.icc.size())) { - fprintf(stderr, "JxlEncoderSetICCProfile() failed.\n"); - return false; - } - } else { - if (JXL_ENC_SUCCESS != - JxlEncoderSetColorEncoding(enc, &ppf.color_encoding)) { - fprintf(stderr, "JxlEncoderSetColorEncoding() failed.\n"); - return false; - } - } - - if (use_boxes) { - if (JXL_ENC_SUCCESS != JxlEncoderUseBoxes(enc)) { - fprintf(stderr, "JxlEncoderUseBoxes() failed.\n"); - return false; - } - // Prepend 4 zero bytes to exif for tiff header offset - std::vector exif_with_offset; - bool bigendian; - if (IsExif(ppf.metadata.exif, &bigendian)) { - exif_with_offset.resize(ppf.metadata.exif.size() + 4); - memcpy(exif_with_offset.data() + 4, ppf.metadata.exif.data(), - ppf.metadata.exif.size()); - } - const struct BoxInfo { - const char* type; - const std::vector& bytes; - } boxes[] = { - {"Exif", exif_with_offset}, {"xml ", ppf.metadata.xmp}, - {"jumb", ppf.metadata.jumbf}, {"xml ", ppf.metadata.iptc}, - {"jhgm", ppf.metadata.jhgm}, - }; - for (auto box : boxes) { - if (!box.bytes.empty()) { - if (JXL_ENC_SUCCESS != - JxlEncoderAddBox(enc, box.type, box.bytes.data(), - box.bytes.size(), - TO_JXL_BOOL(params.compress_boxes))) { - fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type); - return false; - } - } - } - JxlEncoderCloseBoxes(enc); - } - - for (size_t num_frame = 0; num_frame < ppf.frames.size(); ++num_frame) { - const jxl::extras::PackedFrame& pframe = ppf.frames[num_frame]; - const jxl::extras::PackedImage& pimage = pframe.color; - JxlPixelFormat ppixelformat = pimage.format; - size_t num_interleaved_alpha = - (ppixelformat.num_channels - ppf.info.num_color_channels); - if (!SetupFrame(enc, settings, pframe.frame_info, params, ppf, num_frame, - num_alpha_channels, num_interleaved_alpha, option_idx)) { - return false; - } - if (JXL_ENC_SUCCESS != JxlEncoderAddImageFrame(settings, &ppixelformat, - pimage.pixels(), - pimage.pixels_size)) { - fprintf(stderr, "JxlEncoderAddImageFrame() failed.\n"); - return false; - } - // Only set extra channel buffer if it is provided non-interleaved. - for (size_t i = 0; i < pframe.extra_channels.size(); ++i) { - if (JXL_ENC_SUCCESS != - JxlEncoderSetExtraChannelBuffer(settings, &ppixelformat, - pframe.extra_channels[i].pixels(), - pframe.extra_channels[i].stride * - pframe.extra_channels[i].ysize, - num_interleaved_alpha + i)) { - fprintf(stderr, "JxlEncoderSetExtraChannelBuffer() failed.\n"); - return false; - } - } - } - for (size_t fi = 0; fi < ppf.chunked_frames.size(); ++fi) { - ChunkedPackedFrame& chunked_frame = ppf.chunked_frames[fi]; - size_t num_interleaved_alpha = - (chunked_frame.format.num_channels - ppf.info.num_color_channels); - if (!SetupFrame(enc, settings, chunked_frame.frame_info, params, ppf, fi, - num_alpha_channels, num_interleaved_alpha, option_idx)) { - return false; - } - const bool last_frame = fi + 1 == ppf.chunked_frames.size(); - if (JXL_ENC_SUCCESS != - JxlEncoderAddChunkedFrame(settings, TO_JXL_BOOL(last_frame), - chunked_frame.GetInputSource())) { - fprintf(stderr, "JxlEncoderAddChunkedFrame() failed.\n"); - return false; - } - } - } - JxlEncoderCloseInput(enc); - if (params.HasOutputProcessor()) { - if (JXL_ENC_SUCCESS != JxlEncoderFlushInput(enc)) { - fprintf(stderr, "JxlEncoderAddChunkedFrame() failed.\n"); - return false; - } - } else if (!ReadCompressedOutput(enc, compressed)) { - return false; - } - return true; -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.h b/third_party/jpeg-xl/lib/extras/enc/jxl.h deleted file mode 100644 index a6573eb129ffa..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_JXL_H_ -#define LIB_EXTRAS_ENC_JXL_H_ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "lib/extras/packed_image.h" - -namespace jxl { -namespace extras { - -struct JXLOption { - JXLOption(JxlEncoderFrameSettingId id, int64_t val, size_t frame_index) - : id(id), is_float(false), ival(val), frame_index(frame_index) {} - JXLOption(JxlEncoderFrameSettingId id, float val, size_t frame_index) - : id(id), is_float(true), fval(val), frame_index(frame_index) {} - - JxlEncoderFrameSettingId id; - bool is_float; - union { - int64_t ival; - float fval; - }; - size_t frame_index; -}; - -struct JXLCompressParams { - std::vector options; - - // Target butteraugli distance, 0.0 means lossless. - float distance = 1.0f; - float alpha_distance = 0.0f; - - // If set to true, forces container mode. - bool use_container = false; - - // Whether to enable/disable byte-exact jpeg reconstruction for jpeg inputs. - bool jpeg_store_metadata = true; - bool jpeg_strip_exif = false; - bool jpeg_strip_xmp = false; - bool jpeg_strip_jumbf = false; - - // Whether to create brob boxes. - bool compress_boxes = true; - - // Upper bound on the intensity level present in the image in nits (zero means - // that the library chooses a default). - float intensity_target = 0; - int already_downsampled = 1; - int upsampling_mode = -1; - - // Overrides for bitdepth, codestream level and alpha premultiply. - size_t override_bitdepth = 0; - int32_t codestream_level = -1; - int32_t premultiply = -1; - - // If runner_opaque is set, the encoder uses this parallel runner. - JxlParallelRunner runner = JxlThreadParallelRunner; - void* runner_opaque = nullptr; - - // If memory_manager is set, encoder uses it. - JxlMemoryManager* memory_manager = nullptr; - - JxlEncoderOutputProcessor output_processor = {}; - JxlDebugImageCallback debug_image = nullptr; - void* debug_image_opaque = nullptr; - JxlEncoderStats* stats = nullptr; - bool allow_expert_options = false; - - void AddOption(JxlEncoderFrameSettingId id, int64_t val) { - options.emplace_back(id, val, 0); - } - void AddFloatOption(JxlEncoderFrameSettingId id, float val) { - options.emplace_back(id, val, 0); - } - bool HasOutputProcessor() const { - return (output_processor.get_buffer != nullptr && - output_processor.release_buffer != nullptr && - output_processor.set_finalized_position != nullptr); - } -}; - -bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, - const std::vector* jpeg_bytes, - std::vector* compressed); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_JXL_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/npy.cc b/third_party/jpeg-xl/lib/extras/enc/npy.cc deleted file mode 100644 index 38777a3535412..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/npy.cc +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/npy.h" - -#include - -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/common.h" - -namespace jxl { -namespace extras { -namespace { - -// JSON value writing - -class JSONField { - public: - virtual ~JSONField() = default; - virtual void Write(std::ostream& o, uint32_t indent) const = 0; - - protected: - JSONField() = default; -}; - -class JSONValue : public JSONField { - public: - template - explicit JSONValue(const T& value) : value_(std::to_string(value)) {} - - explicit JSONValue(const std::string& value) : value_("\"" + value + "\"") {} - - explicit JSONValue(bool value) : value_(value ? "true" : "false") {} - - void Write(std::ostream& o, uint32_t indent) const override { o << value_; } - - private: - std::string value_; -}; - -class JSONDict : public JSONField { - public: - JSONDict() = default; - - template - T* AddEmpty(const std::string& key) { - static_assert(std::is_convertible::value, - "T must be a JSONField"); - T* ret = new T(); - JSONField* field = static_cast(ret); - auto handle = std::unique_ptr(field); - values_.emplace_back(key, std::move(handle)); - return ret; - } - - template - void Add(const std::string& key, const T& value) { - JSONField* field = static_cast(new JSONValue(value)); - auto handle = std::unique_ptr(field); - values_.emplace_back(key, std::move(handle)); - } - - void Write(std::ostream& o, uint32_t indent) const override { - std::string indent_str(indent, ' '); - o << "{"; - bool is_first = true; - for (const auto& key_value : values_) { - if (!is_first) { - o << ","; - } - is_first = false; - o << "\n" << indent_str << " \"" << key_value.first << "\": "; - key_value.second->Write(o, indent + 2); - } - if (!values_.empty()) { - o << "\n" << indent_str; - } - o << "}"; - } - - private: - // Dictionary with order. - std::vector>> values_; -}; - -class JSONArray : public JSONField { - public: - JSONArray() = default; - - template - T* AddEmpty() { - static_assert(std::is_convertible::value, - "T must be a JSONField"); - T* ret = new T(); - values_.emplace_back(ret); - return ret; - } - - template - void Add(const T& value) { - values_.emplace_back(new JSONValue(value)); - } - - void Write(std::ostream& o, uint32_t indent) const override { - std::string indent_str(indent, ' '); - o << "["; - bool is_first = true; - for (const auto& value : values_) { - if (!is_first) { - o << ","; - } - is_first = false; - o << "\n" << indent_str << " "; - value->Write(o, indent + 2); - } - if (!values_.empty()) { - o << "\n" << indent_str; - } - o << "]"; - } - - private: - std::vector> values_; -}; - -void GenerateMetadata(const PackedPixelFile& ppf, std::vector* out) { - JSONDict meta; - // Same order as in 18181-3 CD. - - // Frames. - auto* meta_frames = meta.AddEmpty("frames"); - for (size_t i = 0; i < ppf.frames.size(); i++) { - auto* frame_i = meta_frames->AddEmpty(); - if (ppf.info.have_animation) { - frame_i->Add("duration", - JSONValue(ppf.frames[i].frame_info.duration * 1.0f * - ppf.info.animation.tps_denominator / - ppf.info.animation.tps_numerator)); - } - - frame_i->Add("name", JSONValue(ppf.frames[i].name)); - - if (ppf.info.animation.have_timecodes) { - frame_i->Add("timecode", JSONValue(ppf.frames[i].frame_info.timecode)); - } - } - -#define METADATA(FIELD) meta.Add(#FIELD, ppf.info.FIELD) - - METADATA(intensity_target); - METADATA(min_nits); - METADATA(relative_to_max_display); - METADATA(linear_below); - - if (ppf.info.have_preview) { - meta.AddEmpty("preview"); - // TODO(veluca): can we have duration/name/timecode here? - } - - { - auto* ectype = meta.AddEmpty("extra_channel_type"); - auto* bps = meta.AddEmpty("bits_per_sample"); - auto* ebps = meta.AddEmpty("exp_bits_per_sample"); - bps->Add(ppf.info.bits_per_sample); - ebps->Add(ppf.info.exponent_bits_per_sample); - for (const auto& eci : ppf.extra_channels_info) { - switch (eci.ec_info.type) { - case JXL_CHANNEL_ALPHA: { - ectype->Add(std::string("Alpha")); - break; - } - case JXL_CHANNEL_DEPTH: { - ectype->Add(std::string("Depth")); - break; - } - case JXL_CHANNEL_SPOT_COLOR: { - ectype->Add(std::string("SpotColor")); - break; - } - case JXL_CHANNEL_SELECTION_MASK: { - ectype->Add(std::string("SelectionMask")); - break; - } - case JXL_CHANNEL_BLACK: { - ectype->Add(std::string("Black")); - break; - } - case JXL_CHANNEL_CFA: { - ectype->Add(std::string("CFA")); - break; - } - case JXL_CHANNEL_THERMAL: { - ectype->Add(std::string("Thermal")); - break; - } - default: { - ectype->Add(std::string("UNKNOWN")); - break; - } - } - bps->Add(eci.ec_info.bits_per_sample); - ebps->Add(eci.ec_info.exponent_bits_per_sample); - } - } - - std::ostringstream os; - meta.Write(os, 0); - out->resize(os.str().size()); - memcpy(out->data(), os.str().data(), os.str().size()); -} - -void Append(std::vector* out, const void* data, size_t size) { - size_t pos = out->size(); - out->resize(pos + size); - memcpy(out->data() + pos, data, size); -} - -void WriteNPYHeader(size_t xsize, size_t ysize, uint32_t num_channels, - size_t num_frames, std::vector* out) { - const uint8_t header[] = "\x93NUMPY\x01\x00"; - Append(out, header, 8); - std::stringstream ss; - ss << "{'descr': '(ss.str().size() % 256), - static_cast(ss.str().size() / 256)}; - Append(out, header_len, 2); - Append(out, ss.str().data(), ss.str().size()); -} - -bool WriteFrameToNPYArray(size_t xsize, size_t ysize, const PackedFrame& frame, - std::vector* out) { - const auto& color = frame.color; - if (color.xsize != xsize || color.ysize != ysize) { - return false; - } - for (const auto& ec : frame.extra_channels) { - if (ec.xsize != xsize || ec.ysize != ysize) { - return false; - } - } - // interleave the samples from color and extra channels - for (size_t y = 0; y < ysize; ++y) { - for (size_t x = 0; x < xsize; ++x) { - { - size_t sample_size = color.pixel_stride(); - size_t offset = y * color.stride + x * sample_size; - uint8_t* pixels = reinterpret_cast(color.pixels()); - JXL_ENSURE(offset + sample_size <= color.pixels_size); - Append(out, pixels + offset, sample_size); - } - for (const auto& ec : frame.extra_channels) { - size_t sample_size = ec.pixel_stride(); - size_t offset = y * ec.stride + x * sample_size; - uint8_t* pixels = reinterpret_cast(ec.pixels()); - JXL_ENSURE(offset + sample_size <= ec.pixels_size); - Append(out, pixels + offset, sample_size); - } - } - } - return true; -} - -// Writes a PackedPixelFile as a numpy 4D ndarray in binary format. -bool WriteNPYArray(const PackedPixelFile& ppf, std::vector* out) { - size_t xsize = ppf.info.xsize; - size_t ysize = ppf.info.ysize; - WriteNPYHeader(xsize, ysize, - ppf.info.num_color_channels + ppf.extra_channels_info.size(), - ppf.frames.size(), out); - for (const auto& frame : ppf.frames) { - if (!WriteFrameToNPYArray(xsize, ysize, frame, out)) { - return false; - } - } - return true; -} - -class NumPyEncoder : public Encoder { - public: - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - GenerateMetadata(ppf, &encoded_image->metadata); - encoded_image->bitstreams.emplace_back(); - if (!WriteNPYArray(ppf, &encoded_image->bitstreams.back())) { - return false; - } - if (ppf.preview_frame) { - size_t xsize = ppf.info.preview.xsize; - size_t ysize = ppf.info.preview.ysize; - WriteNPYHeader(xsize, ysize, ppf.info.num_color_channels, 1, - &encoded_image->preview_bitstream); - if (!WriteFrameToNPYArray(xsize, ysize, *ppf.preview_frame, - &encoded_image->preview_bitstream)) { - return false; - } - } - return true; - } - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 3}) { - formats.push_back(JxlPixelFormat{num_channels, JXL_TYPE_FLOAT, - JXL_LITTLE_ENDIAN, /*align=*/0}); - } - return formats; - } -}; - -} // namespace - -std::unique_ptr GetNumPyEncoder() { - return jxl::make_unique(); -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/npy.h b/third_party/jpeg-xl/lib/extras/enc/npy.h deleted file mode 100644 index 3ee6208ec290c..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/npy.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_NPY_H_ -#define LIB_EXTRAS_ENC_NPY_H_ - -// Encodes pixels to numpy array, used for conformance testing. - -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetNumPyEncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_NPY_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/pgx.cc b/third_party/jpeg-xl/lib/extras/enc/pgx.cc deleted file mode 100644 index fb47b3e1a97d5..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/pgx.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/pgx.h" - -#include - -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/byte_order.h" - -namespace jxl { -namespace extras { -namespace { - -constexpr size_t kMaxHeaderSize = 200; - -Status EncodeHeader(const JxlBasicInfo& info, char* header, - int* chars_written) { - if (info.alpha_bits > 0) { - return JXL_FAILURE("PGX: can't store alpha"); - } - if (info.num_color_channels != 1) { - return JXL_FAILURE("PGX: must be grayscale"); - } - // TODO(lode): verify other bit depths: for other bit depths such as 1 or 4 - // bits, have a test case to verify it works correctly. For bits > 16, we may - // need to change the way external_image works. - if (info.bits_per_sample != 8 && info.bits_per_sample != 16) { - return JXL_FAILURE("PGX: bits other than 8 or 16 not yet supported"); - } - - // Use ML (Big Endian), LM may not be well supported by all decoders. - *chars_written = snprintf(header, kMaxHeaderSize, "PG ML + %u %u %u\n", - info.bits_per_sample, info.xsize, info.ysize); - JXL_RETURN_IF_ERROR(static_cast(*chars_written) < - kMaxHeaderSize); - return true; -} - -Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, - std::vector* bytes) { - char header[kMaxHeaderSize]; - int header_size = 0; - JXL_RETURN_IF_ERROR(EncodeHeader(info, header, &header_size)); - - const PackedImage& color = frame.color; - const JxlPixelFormat format = color.format; - const uint8_t* in = reinterpret_cast(color.pixels()); - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(format.data_type)); - size_t data_bits_per_sample = PackedImage::BitsPerChannel(format.data_type); - size_t bytes_per_sample = data_bits_per_sample / kBitsPerByte; - size_t num_samples = info.xsize * info.ysize; - - if (info.bits_per_sample != data_bits_per_sample) { - return JXL_FAILURE("Bit depth does not match pixel data type"); - } - - std::vector pixels(num_samples * bytes_per_sample); - - if (format.data_type == JXL_TYPE_UINT8) { - memcpy(pixels.data(), in, num_samples * bytes_per_sample); - } else if (format.data_type == JXL_TYPE_UINT16) { - if (format.endianness != JXL_BIG_ENDIAN) { - const uint8_t* p_in = in; - uint8_t* p_out = pixels.data(); - for (size_t i = 0; i < num_samples; ++i, p_in += 2, p_out += 2) { - StoreBE16(LoadLE16(p_in), p_out); - } - } else { - memcpy(pixels.data(), in, num_samples * bytes_per_sample); - } - } else { - return JXL_FAILURE("Unsupported pixel data type"); - } - - bytes->resize(static_cast(header_size) + pixels.size()); - memcpy(bytes->data(), header, static_cast(header_size)); - memcpy(bytes->data() + header_size, pixels.data(), pixels.size()); - - return true; -} - -class PGXEncoder : public Encoder { - public: - std::vector AcceptedFormats() const override { - std::vector formats; - for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) { - for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { - formats.push_back(JxlPixelFormat{/*num_channels=*/1, - /*data_type=*/data_type, - /*endianness=*/endianness, - /*align=*/0}); - } - } - return formats; - } - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - encoded_image->icc.assign(ppf.icc.begin(), ppf.icc.end()); - encoded_image->bitstreams.clear(); - encoded_image->bitstreams.reserve(ppf.frames.size()); - for (const auto& frame : ppf.frames) { - JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); - encoded_image->bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR( - EncodeImagePGX(frame, ppf.info, &encoded_image->bitstreams.back())); - } - return true; - } -}; - -} // namespace - -std::unique_ptr GetPGXEncoder() { - return jxl::make_unique(); -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/pgx.h b/third_party/jpeg-xl/lib/extras/enc/pgx.h deleted file mode 100644 index f24e391b0917a..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/pgx.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_PGX_H_ -#define LIB_EXTRAS_ENC_PGX_H_ - -// Encodes PGX pixels in memory. - -#include -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetPGXEncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_PGX_H_ diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.cc b/third_party/jpeg-xl/lib/extras/enc/pnm.cc deleted file mode 100644 index c4aa8d12eb4d6..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/pnm.cc +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/enc/pnm.h" - -#include -#include -#include -#include -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { -namespace { - -constexpr size_t kMaxHeaderSize = 2000; - -class BasePNMEncoder : public Encoder { - public: - Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool) const override { - JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); - if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() || - !ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) { - JXL_WARNING("PNM encoder ignoring metadata - use a different codec"); - } - encoded_image->icc = ppf.icc; - encoded_image->bitstreams.clear(); - encoded_image->bitstreams.reserve(ppf.frames.size()); - for (const auto& frame : ppf.frames) { - JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); - encoded_image->bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR( - EncodeFrame(ppf, frame, &encoded_image->bitstreams.back())); - } - for (size_t i = 0; i < ppf.extra_channels_info.size(); ++i) { - const auto& ec_info = ppf.extra_channels_info[i].ec_info; - encoded_image->extra_channel_bitstreams.emplace_back(); - auto& ec_bitstreams = encoded_image->extra_channel_bitstreams.back(); - for (const auto& frame : ppf.frames) { - ec_bitstreams.emplace_back(); - JXL_RETURN_IF_ERROR(EncodeExtraChannel(frame.extra_channels[i], - ec_info.bits_per_sample, - &ec_bitstreams.back())); - } - } - return true; - } - - protected: - virtual Status EncodeFrame(const PackedPixelFile& ppf, - const PackedFrame& frame, - std::vector* bytes) const = 0; - virtual Status EncodeExtraChannel(const PackedImage& image, - size_t bits_per_sample, - std::vector* bytes) const = 0; -}; - -class PNMEncoder : public BasePNMEncoder { - public: - static const std::vector kAcceptedFormats; - - std::vector AcceptedFormats() const override { - return kAcceptedFormats; - } - - Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, - std::vector* bytes) const override { - return EncodeImage(frame.color, ppf.info.bits_per_sample, bytes); - } - Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, - std::vector* bytes) const override { - return EncodeImage(image, bits_per_sample, bytes); - } - - private: - static Status EncodeImage(const PackedImage& image, size_t bits_per_sample, - std::vector* bytes) { - uint32_t maxval = (1u << bits_per_sample) - 1; - char type = image.format.num_channels == 1 ? '5' : '6'; - char header[kMaxHeaderSize]; - size_t header_size = - snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n", - type, image.xsize, image.ysize, maxval); - JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize); - bytes->resize(header_size + image.pixels_size); - memcpy(bytes->data(), header, header_size); - memcpy(bytes->data() + header_size, - reinterpret_cast(image.pixels()), image.pixels_size); - return true; - } -}; - -class PGMEncoder : public PNMEncoder { - public: - static const std::vector kAcceptedFormats; - - std::vector AcceptedFormats() const override { - return kAcceptedFormats; - } -}; - -const std::vector PGMEncoder::kAcceptedFormats = { - JxlPixelFormat{1, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, - JxlPixelFormat{1, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}}; - -class PPMEncoder : public PNMEncoder { - public: - static const std::vector kAcceptedFormats; - - std::vector AcceptedFormats() const override { - return kAcceptedFormats; - } -}; - -const std::vector PPMEncoder::kAcceptedFormats = { - JxlPixelFormat{3, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, - JxlPixelFormat{3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}}; - -const std::vector PNMEncoder::kAcceptedFormats = [] { - std::vector combined = PPMEncoder::kAcceptedFormats; - combined.insert(combined.end(), PGMEncoder::kAcceptedFormats.begin(), - PGMEncoder::kAcceptedFormats.end()); - return combined; -}(); - -class PFMEncoder : public BasePNMEncoder { - public: - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 3}) { - for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { - formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, - /*data_type=*/JXL_TYPE_FLOAT, - /*endianness=*/endianness, - /*align=*/0}); - } - } - return formats; - } - Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, - std::vector* bytes) const override { - return EncodeImage(frame.color, bytes); - } - Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, - std::vector* bytes) const override { - return EncodeImage(image, bytes); - } - - private: - static Status EncodeImage(const PackedImage& image, - std::vector* bytes) { - char type = image.format.num_channels == 1 ? 'f' : 'F'; - double scale = image.format.endianness == JXL_LITTLE_ENDIAN ? -1.0 : 1.0; - char header[kMaxHeaderSize]; - size_t header_size = - snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%.1f\n", - type, image.xsize, image.ysize, scale); - JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize); - bytes->resize(header_size + image.pixels_size); - memcpy(bytes->data(), header, header_size); - const uint8_t* in = reinterpret_cast(image.pixels()); - uint8_t* out = bytes->data() + header_size; - for (size_t y = 0; y < image.ysize; ++y) { - size_t y_out = image.ysize - 1 - y; - const uint8_t* row_in = &in[y * image.stride]; - uint8_t* row_out = &out[y_out * image.stride]; - memcpy(row_out, row_in, image.stride); - } - return true; - } -}; - -class PAMEncoder : public BasePNMEncoder { - public: - std::vector AcceptedFormats() const override { - std::vector formats; - for (const uint32_t num_channels : {1, 2, 3, 4}) { - for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) { - formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, - /*data_type=*/data_type, - /*endianness=*/JXL_BIG_ENDIAN, - /*align=*/0}); - } - } - return formats; - } - Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, - std::vector* bytes) const override { - const PackedImage& color = frame.color; - const auto& ec_info = ppf.extra_channels_info; - JXL_RETURN_IF_ERROR(frame.extra_channels.size() == ec_info.size()); - for (const auto& ec : frame.extra_channels) { - if (ec.xsize != color.xsize || ec.ysize != color.ysize) { - return JXL_FAILURE("Extra channel and color size mismatch."); - } - if (ec.format.data_type != color.format.data_type || - ec.format.endianness != color.format.endianness) { - return JXL_FAILURE("Extra channel and color format mismatch."); - } - } - if (ppf.info.alpha_bits && - (ppf.info.bits_per_sample != ppf.info.alpha_bits)) { - return JXL_FAILURE("Alpha bit depth does not match image bit depth"); - } - for (const auto& it : ec_info) { - if (it.ec_info.bits_per_sample != ppf.info.bits_per_sample) { - return JXL_FAILURE( - "Extra channel bit depth does not match image bit depth"); - } - } - const char* kColorTypes[4] = {"GRAYSCALE", "GRAYSCALE_ALPHA", "RGB", - "RGB_ALPHA"}; - uint32_t maxval = (1u << ppf.info.bits_per_sample) - 1; - uint32_t depth = color.format.num_channels + ec_info.size(); - char header[kMaxHeaderSize]; - size_t pos = 0; - pos += snprintf(header + pos, kMaxHeaderSize - pos, - "P7\nWIDTH %" PRIuS "\nHEIGHT %" PRIuS - "\nDEPTH %u\n" - "MAXVAL %u\nTUPLTYPE %s\n", - color.xsize, color.ysize, depth, maxval, - kColorTypes[color.format.num_channels - 1]); - JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); - for (const auto& info : ec_info) { - pos += snprintf(header + pos, kMaxHeaderSize - pos, "TUPLTYPE %s\n", - ExtraChannelTypeName(info.ec_info.type).c_str()); - JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); - } - pos += snprintf(header + pos, kMaxHeaderSize - pos, "ENDHDR\n"); - JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); - size_t total_size = color.pixels_size; - for (const auto& ec : frame.extra_channels) { - total_size += ec.pixels_size; - } - bytes->resize(pos + total_size); - memcpy(bytes->data(), header, pos); - // If we have no extra channels, just copy color pixel data over. - if (frame.extra_channels.empty()) { - memcpy(bytes->data() + pos, reinterpret_cast(color.pixels()), - color.pixels_size); - return true; - } - // Interleave color and extra channels. - const uint8_t* in = reinterpret_cast(color.pixels()); - std::vector ec_in(frame.extra_channels.size()); - for (size_t i = 0; i < frame.extra_channels.size(); ++i) { - ec_in[i] = - reinterpret_cast(frame.extra_channels[i].pixels()); - } - uint8_t* out = bytes->data() + pos; - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(color.format.data_type)); - size_t pwidth = PackedImage::BitsPerChannel(color.format.data_type) / 8; - for (size_t y = 0; y < color.ysize; ++y) { - for (size_t x = 0; x < color.xsize; ++x) { - memcpy(out, in, color.pixel_stride()); - out += color.pixel_stride(); - in += color.pixel_stride(); - for (auto& p : ec_in) { - memcpy(out, p, pwidth); - out += pwidth; - p += pwidth; - } - } - } - return true; - } - Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, - std::vector* bytes) const override { - return true; - } - - private: - static std::string ExtraChannelTypeName(JxlExtraChannelType type) { - switch (type) { - case JXL_CHANNEL_ALPHA: - return std::string("Alpha"); - case JXL_CHANNEL_DEPTH: - return std::string("Depth"); - case JXL_CHANNEL_SPOT_COLOR: - return std::string("SpotColor"); - case JXL_CHANNEL_SELECTION_MASK: - return std::string("SelectionMask"); - case JXL_CHANNEL_BLACK: - return std::string("Black"); - case JXL_CHANNEL_CFA: - return std::string("CFA"); - case JXL_CHANNEL_THERMAL: - return std::string("Thermal"); - case JXL_CHANNEL_UNKNOWN: - return std::string("Unknown"); - case JXL_CHANNEL_OPTIONAL: - return std::string("Optional"); - default: - return std::string("UNKNOWN"); - } - } -}; - -} // namespace - -std::unique_ptr GetPPMEncoder() { - return jxl::make_unique(); -} - -std::unique_ptr GetPNMEncoder() { - return jxl::make_unique(); -} - -std::unique_ptr GetPFMEncoder() { - return jxl::make_unique(); -} - -std::unique_ptr GetPGMEncoder() { - return jxl::make_unique(); -} - -std::unique_ptr GetPAMEncoder() { - return jxl::make_unique(); -} - -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.h b/third_party/jpeg-xl/lib/extras/enc/pnm.h deleted file mode 100644 index 1e0020cdbd884..0000000000000 --- a/third_party/jpeg-xl/lib/extras/enc/pnm.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_ENC_PNM_H_ -#define LIB_EXTRAS_ENC_PNM_H_ - -// Encodes/decodes PBM/PGM/PPM/PFM pixels in memory. - -// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown) -#include -#include - -#include "lib/extras/enc/encode.h" - -namespace jxl { -namespace extras { - -std::unique_ptr GetPAMEncoder(); -std::unique_ptr GetPGMEncoder(); -std::unique_ptr GetPNMEncoder(); -std::unique_ptr GetPPMEncoder(); -std::unique_ptr GetPFMEncoder(); - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_ENC_PNM_H_ diff --git a/third_party/jpeg-xl/lib/extras/exif.cc b/third_party/jpeg-xl/lib/extras/exif.cc deleted file mode 100644 index aea632732b8d9..0000000000000 --- a/third_party/jpeg-xl/lib/extras/exif.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/exif.h" - -#include "lib/jxl/base/byte_order.h" - -namespace jxl { - -constexpr uint16_t kExifOrientationTag = 274; - -void ResetExifOrientation(std::vector& exif) { - if (exif.size() < 12) return; // not enough bytes for a valid exif blob - bool bigendian; - uint8_t* t = exif.data(); - if (LoadLE32(t) == 0x2A004D4D) { - bigendian = true; - } else if (LoadLE32(t) == 0x002A4949) { - bigendian = false; - } else { - return; // not a valid tiff header - } - t += 4; - uint64_t offset = (bigendian ? LoadBE32(t) : LoadLE32(t)); - if (exif.size() < 12 + offset + 2 || offset < 8) return; - t += offset - 4; - uint16_t nb_tags = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - while (nb_tags > 0) { - if (t + 12 >= exif.data() + exif.size()) return; - uint16_t tag = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - if (tag == kExifOrientationTag) { - uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t)); - t += 4; - if (type == 3 && count == 1) { - if (bigendian) { - StoreBE16(1, t); - } else { - StoreLE16(1, t); - } - } - return; - } else { - t += 10; - nb_tags--; - } - } -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/exif.h b/third_party/jpeg-xl/lib/extras/exif.h deleted file mode 100644 index f22b2ccef54f8..0000000000000 --- a/third_party/jpeg-xl/lib/extras/exif.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_EXIF_H_ -#define LIB_EXTRAS_EXIF_H_ - -#include - -#include - -namespace jxl { - -// Sets the Exif orientation to the identity, to avoid repeated orientation -void ResetExifOrientation(std::vector& exif); - -} // namespace jxl - -#endif // LIB_EXTRAS_EXIF_H_ diff --git a/third_party/jpeg-xl/lib/extras/gain_map.cc b/third_party/jpeg-xl/lib/extras/gain_map.cc deleted file mode 100644 index 8cce67161bb84..0000000000000 --- a/third_party/jpeg-xl/lib/extras/gain_map.cc +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include -#include -#include -#include - -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/enc_aux_out.h" -#include "lib/jxl/enc_bit_writer.h" -#include "lib/jxl/fields.h" -#include "lib/jxl/memory_manager_internal.h" - -namespace { - -template -class FixedSizeMemoryManager { - public: - FixedSizeMemoryManager() = default; - - JxlMemoryManager* memory_manager() { return &manager_; } - - private: - static void* FixedSizeMemoryManagerAlloc(void* opaque, size_t capacity) { - auto manager = static_cast*>(opaque); - if (capacity > N + jxl::memory_manager_internal::kAlias) { - return nullptr; - } - return manager->memory_; - } - static void FixedSizeMemoryManagerFree(void* opaque, void* pointer) {} - - uint8_t memory_[N + jxl::memory_manager_internal::kAlias]; - JxlMemoryManager manager_ = { - /*opaque=*/this, - /*alloc=*/&FixedSizeMemoryManagerAlloc, - /*free=*/&FixedSizeMemoryManagerFree, - }; -}; - -} // namespace - -JXL_BOOL JxlGainMapGetBundleSize(const JxlGainMapBundle* map_bundle, - size_t* bundle_size) { - if (map_bundle == nullptr) return JXL_FALSE; - - jxl::ColorEncoding internal_color_encoding; - size_t color_encoding_size = 0; - size_t extension_bits = 0; - if (map_bundle->has_color_encoding) { - JXL_RETURN_IF_ERROR( - internal_color_encoding.FromExternal(map_bundle->color_encoding)); - if (!jxl::Bundle::CanEncode(internal_color_encoding, &extension_bits, - &color_encoding_size)) { - return JXL_FALSE; - } - } - - *bundle_size = - 1 + // size of jhgm_version - 2 + // size_of gain_map_metadata_size - map_bundle->gain_map_metadata_size + // size of gain_map_metadata - 1 + // size of color_encoding_size - jxl::DivCeil(color_encoding_size, 8) + // size of the color_encoding - 4 + // size of compressed_icc_size - map_bundle->alt_icc_size + // size of compressed_icc - map_bundle->gain_map_size; // size of gain map - return JXL_TRUE; -} - -JXL_BOOL JxlGainMapWriteBundle(const JxlGainMapBundle* map_bundle, - uint8_t* output_buffer, - size_t output_buffer_size, - size_t* bytes_written) { - if (map_bundle == nullptr) return JXL_FALSE; - - uint8_t jhgm_version = map_bundle->jhgm_version; - - FixedSizeMemoryManager memory_manager; - jxl::ColorEncoding internal_color_encoding; - jxl::BitWriter color_encoding_writer(memory_manager.memory_manager()); - if (map_bundle->has_color_encoding) { - JXL_RETURN_IF_ERROR( - internal_color_encoding.FromExternal(map_bundle->color_encoding)); - if (!jxl::Bundle::Write(internal_color_encoding, &color_encoding_writer, - jxl::LayerType::Header, nullptr)) { - return JXL_FALSE; - } - } - - color_encoding_writer.ZeroPadToByte(); - - uint64_t cursor = 0; - uint64_t next_cursor = 0; - -#define SAFE_CURSOR_UPDATE(n) \ - do { \ - cursor = next_cursor; \ - if (!jxl::SafeAdd(cursor, n, next_cursor) || \ - next_cursor > output_buffer_size) { \ - return JXL_FALSE; \ - } \ - } while (false) - - SAFE_CURSOR_UPDATE(1); - memcpy(output_buffer + cursor, &jhgm_version, 1); - - SAFE_CURSOR_UPDATE(2); - StoreBE16(map_bundle->gain_map_metadata_size, output_buffer + cursor); - - SAFE_CURSOR_UPDATE(map_bundle->gain_map_metadata_size); - memcpy(output_buffer + cursor, map_bundle->gain_map_metadata, - map_bundle->gain_map_metadata_size); - - jxl::Bytes bytes = color_encoding_writer.GetSpan(); - uint8_t color_enc_size = static_cast(bytes.size()); - if (color_enc_size != bytes.size()) return JXL_FALSE; - SAFE_CURSOR_UPDATE(1); - memcpy(output_buffer + cursor, &color_enc_size, 1); - - SAFE_CURSOR_UPDATE(color_enc_size); - memcpy(output_buffer + cursor, bytes.data(), color_enc_size); - - SAFE_CURSOR_UPDATE(4); - StoreBE32(map_bundle->alt_icc_size, output_buffer + cursor); - - SAFE_CURSOR_UPDATE(map_bundle->alt_icc_size); - memcpy(output_buffer + cursor, map_bundle->alt_icc, map_bundle->alt_icc_size); - - SAFE_CURSOR_UPDATE(map_bundle->gain_map_size); - memcpy(output_buffer + cursor, map_bundle->gain_map, - map_bundle->gain_map_size); - -#undef SAFE_CURSOR_UPDATE - - cursor = next_cursor; - - if (bytes_written != nullptr) - *bytes_written = cursor; // Ensure size_t compatibility - return cursor == output_buffer_size ? JXL_TRUE : JXL_FALSE; -} - -JXL_BOOL JxlGainMapReadBundle(JxlGainMapBundle* map_bundle, - const uint8_t* input_buffer, - const size_t input_buffer_size, - size_t* bytes_read) { - if (map_bundle == nullptr || input_buffer == nullptr || - input_buffer_size == 0) { - return JXL_FALSE; - } - - uint64_t cursor = 0; - uint64_t next_cursor = 0; - -#define SAFE_CURSOR_UPDATE(n) \ - do { \ - cursor = next_cursor; \ - if (!jxl::SafeAdd(cursor, n, next_cursor) || \ - next_cursor > input_buffer_size) { \ - return JXL_FALSE; \ - } \ - } while (false) - - // Read the version byte - SAFE_CURSOR_UPDATE(1); - map_bundle->jhgm_version = input_buffer[cursor]; - - // Read gain_map_metadata_size - SAFE_CURSOR_UPDATE(2); - uint16_t gain_map_metadata_size = LoadBE16(input_buffer + cursor); - - SAFE_CURSOR_UPDATE(gain_map_metadata_size); - map_bundle->gain_map_metadata_size = gain_map_metadata_size; - map_bundle->gain_map_metadata = input_buffer + cursor; - - // Read compressed_color_encoding_size - SAFE_CURSOR_UPDATE(1); - uint8_t compressed_color_encoding_size; - memcpy(&compressed_color_encoding_size, input_buffer + cursor, 1); - - map_bundle->has_color_encoding = (compressed_color_encoding_size > 0); - if (map_bundle->has_color_encoding) { - SAFE_CURSOR_UPDATE(compressed_color_encoding_size); - // Decode color encoding - jxl::Span color_encoding_span( - input_buffer + cursor, compressed_color_encoding_size); - jxl::BitReader color_encoding_reader(color_encoding_span); - jxl::ColorEncoding internal_color_encoding; - if (!jxl::Bundle::Read(&color_encoding_reader, &internal_color_encoding)) { - return JXL_FALSE; - } - JXL_RETURN_IF_ERROR(color_encoding_reader.Close()); - map_bundle->color_encoding = internal_color_encoding.ToExternal(); - } - - // Read compressed_icc_size - SAFE_CURSOR_UPDATE(4); - uint32_t compressed_icc_size = LoadBE32(input_buffer + cursor); - - SAFE_CURSOR_UPDATE(compressed_icc_size); - map_bundle->alt_icc_size = compressed_icc_size; - map_bundle->alt_icc = input_buffer + cursor; - - // Calculate remaining bytes for gain map - cursor = next_cursor; - // This calculation is guaranteed not to underflow because `cursor` is always - // updated to a position within or at the end of `input_buffer` (not beyond). - // Thus, subtracting `cursor` from `input_buffer_size` (the total size of the - // buffer) will always result in a non-negative integer representing the - // remaining buffer size. - map_bundle->gain_map_size = input_buffer_size - cursor; - SAFE_CURSOR_UPDATE(map_bundle->gain_map_size); - map_bundle->gain_map = input_buffer + cursor; - -#undef SAFE_CURSOR_UPDATE - - cursor = next_cursor; - - if (bytes_read != nullptr) { - *bytes_read = cursor; - } - return JXL_TRUE; -} diff --git a/third_party/jpeg-xl/lib/extras/gain_map_test.cc b/third_party/jpeg-xl/lib/extras/gain_map_test.cc deleted file mode 100644 index 7b4864974c07a..0000000000000 --- a/third_party/jpeg-xl/lib/extras/gain_map_test.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "jxl/gain_map.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace { - -std::vector GoldenTestGainMap(bool has_icc, bool has_color_encoding) { - // Define the parts of the gain map - uint8_t jhgm_version = 0x00; - std::vector gain_map_metadata_size = {0x00, 0x58}; // 88 in decimal - // TODO(firsching): Replace with more realistic data - std::string first_placeholder = - "placeholder gain map metadata, fill with actual example after (ISO " - "21496-1) is finalized"; - - uint8_t color_encoding_size = has_color_encoding ? 3 : 0; - std::vector color_encoding = {0x50, 0xb4, 0x00}; - - std::vector icc_size = {0x00, 0x00, 0x00, 0x00}; - if (has_icc) { - icc_size = {0x00, 0x00, 0x00, 0x88}; // 136 in decimal - } - std::vector icc_data = jxl::test::GetCompressedIccTestProfile(); - std::string second_placeholder = - "placeholder for an actual naked JPEG XL codestream"; - - // Assemble the gain map - std::vector gain_map; - gain_map.push_back(jhgm_version); - gain_map.insert(gain_map.end(), gain_map_metadata_size.begin(), - gain_map_metadata_size.end()); - gain_map.insert(gain_map.end(), first_placeholder.begin(), - first_placeholder.end()); - gain_map.push_back(color_encoding_size); - if (has_color_encoding) { - gain_map.insert(gain_map.end(), color_encoding.begin(), - color_encoding.end()); - } - gain_map.insert(gain_map.end(), icc_size.begin(), icc_size.end()); - if (has_icc) { - gain_map.insert(gain_map.end(), icc_data.begin(), icc_data.end()); - } - gain_map.insert(gain_map.end(), second_placeholder.begin(), - second_placeholder.end()); - - return gain_map; -} - -} // namespace - -namespace jxl { -namespace { - -struct GainMapTestParams { - bool has_color_encoding; - std::vector icc_data; -}; - -class GainMapTest : public ::testing::TestWithParam {}; - -TEST_P(GainMapTest, GainMapRoundtrip) { - size_t bundle_size; - const GainMapTestParams& params = GetParam(); - std::vector golden_gain_map = - GoldenTestGainMap(!params.icc_data.empty(), params.has_color_encoding); - - JxlGainMapBundle orig_bundle; - // Initialize the bundle with some test data - orig_bundle.jhgm_version = 0; - const char* metadata_str = - "placeholder gain map metadata, fill with actual example after (ISO " - "21496-1) is finalized"; - std::vector gain_map_metadata(metadata_str, - metadata_str + strlen(metadata_str)); - orig_bundle.gain_map_metadata_size = gain_map_metadata.size(); - orig_bundle.gain_map_metadata = gain_map_metadata.data(); - - // Use the ICC profile from the parameter - orig_bundle.has_color_encoding = TO_JXL_BOOL(params.has_color_encoding); - if (orig_bundle.has_color_encoding) { - JxlColorEncoding color_encoding = {}; - JxlColorEncodingSetToLinearSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); - orig_bundle.color_encoding = color_encoding; - } - - std::vector alt_icc(params.icc_data.begin(), params.icc_data.end()); - orig_bundle.alt_icc = alt_icc.data(); - orig_bundle.alt_icc_size = alt_icc.size(); - - const char* gain_map_str = - "placeholder for an actual naked JPEG XL codestream"; - std::vector gain_map(gain_map_str, - gain_map_str + strlen(gain_map_str)); - orig_bundle.gain_map_size = gain_map.size(); - orig_bundle.gain_map = gain_map.data(); - - ASSERT_TRUE(JxlGainMapGetBundleSize(&orig_bundle, &bundle_size)); - EXPECT_EQ(bundle_size, golden_gain_map.size()); - - std::vector buffer(bundle_size); - size_t bytes_written; - ASSERT_TRUE(JxlGainMapWriteBundle(&orig_bundle, buffer.data(), buffer.size(), - &bytes_written)); - EXPECT_EQ(bytes_written, bundle_size); - EXPECT_EQ(buffer[0], orig_bundle.jhgm_version); - EXPECT_EQ(buffer.size(), golden_gain_map.size()); - EXPECT_TRUE( - std::equal(buffer.begin(), buffer.end(), golden_gain_map.begin())); - - JxlGainMapBundle output_bundle; - size_t bytes_read; - ASSERT_TRUE(JxlGainMapReadBundle(&output_bundle, buffer.data(), buffer.size(), - &bytes_read)); - - EXPECT_EQ(output_bundle.gain_map_size, orig_bundle.gain_map_size); - EXPECT_EQ(output_bundle.gain_map_metadata_size, - orig_bundle.gain_map_metadata_size); - EXPECT_EQ(output_bundle.alt_icc_size, orig_bundle.alt_icc_size); - EXPECT_EQ(output_bundle.has_color_encoding, params.has_color_encoding); - EXPECT_EQ(output_bundle.jhgm_version, orig_bundle.jhgm_version); - std::vector output_gain_map_metadata( - output_bundle.gain_map_metadata, - output_bundle.gain_map_metadata + output_bundle.gain_map_metadata_size); - std::vector output_alt_icc( - output_bundle.alt_icc, - output_bundle.alt_icc + output_bundle.alt_icc_size); - std::vector output_gain_map( - output_bundle.gain_map, - output_bundle.gain_map + output_bundle.gain_map_size); - EXPECT_TRUE(std::equal(output_gain_map_metadata.begin(), - output_gain_map_metadata.end(), - gain_map_metadata.begin())); - EXPECT_TRUE(std::equal(output_alt_icc.begin(), output_alt_icc.end(), - alt_icc.begin())); - EXPECT_TRUE(std::equal(output_gain_map.begin(), output_gain_map.end(), - gain_map.begin())); -} - -JXL_GTEST_INSTANTIATE_TEST_SUITE_P( - GainMapTestCases, GainMapTest, - ::testing::Values( - GainMapTestParams{true, std::vector()}, - GainMapTestParams{true, test::GetCompressedIccTestProfile()}, - GainMapTestParams{false, test::GetCompressedIccTestProfile()}, - GainMapTestParams{false, std::vector()}), - [](const testing::TestParamInfo& info) { - std::string name = - "HasColorEncoding" + std::to_string(info.param.has_color_encoding); - - name += "ICCSize" + std::to_string(info.param.icc_data.size()); - - return name; - }); - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/hlg.cc b/third_party/jpeg-xl/lib/extras/hlg.cc deleted file mode 100644 index d92272052b969..0000000000000 --- a/third_party/jpeg-xl/lib/extras/hlg.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/hlg.h" - -#include - -#include - -namespace jxl { - -float GetHlgGamma(const float peak_luminance, const float surround_luminance) { - return 1.2f * std::pow(1.111f, std::log2(peak_luminance / 1000.f)) * - std::pow(0.98f, std::log2(surround_luminance / 5.f)); -} - -Status HlgOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) { - ColorEncoding linear_rec2020; - linear_rec2020.SetColorSpace(ColorSpace::kRGB); - JXL_RETURN_IF_ERROR(linear_rec2020.SetPrimariesType(Primaries::k2100)); - JXL_RETURN_IF_ERROR(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); - linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); - JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC()); - JXL_RETURN_IF_ERROR( - ib->TransformTo(linear_rec2020, *JxlGetDefaultCms(), pool)); - - const auto process_row = [&](const int y, const int thread) -> Status { - float* const JXL_RESTRICT rows[3] = {ib->color()->PlaneRow(0, y), - ib->color()->PlaneRow(1, y), - ib->color()->PlaneRow(2, y)}; - for (size_t x = 0; x < ib->xsize(); ++x) { - float& red = rows[0][x]; - float& green = rows[1][x]; - float& blue = rows[2][x]; - const float luminance = 0.2627f * red + 0.6780f * green + 0.0593f * blue; - const float ratio = std::pow(luminance, gamma - 1); - if (std::isfinite(ratio)) { - red *= ratio; - green *= ratio; - blue *= ratio; - } - } - return true; - }; - - JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, ib->ysize(), ThreadPool::NoInit, - process_row, "HlgOOTF")); - return true; -} - -Status HlgInverseOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) { - return HlgOOTF(ib, 1.f / gamma, pool); -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/hlg.h b/third_party/jpeg-xl/lib/extras/hlg.h deleted file mode 100644 index 4cfec444f4c50..0000000000000 --- a/third_party/jpeg-xl/lib/extras/hlg.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_HLG_H_ -#define LIB_EXTRAS_HLG_H_ - -#include "lib/jxl/image_bundle.h" - -namespace jxl { - -float GetHlgGamma(float peak_luminance, float surround_luminance = 5.f); - -Status HlgOOTF(ImageBundle* ib, float gamma, ThreadPool* pool = nullptr); - -Status HlgInverseOOTF(ImageBundle* ib, float gamma, ThreadPool* pool = nullptr); - -} // namespace jxl - -#endif // LIB_EXTRAS_HLG_H_ diff --git a/third_party/jpeg-xl/lib/extras/jpegli_test.cc b/third_party/jpeg-xl/lib/extras/jpegli_test.cc deleted file mode 100644 index d22afd0e851d3..0000000000000 --- a/third_party/jpeg-xl/lib/extras/jpegli_test.cc +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#if JPEGXL_ENABLE_JPEGLI - -#include "lib/extras/dec/jpegli.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/extras/dec/color_hints.h" -#include "lib/extras/dec/decode.h" -#include "lib/extras/dec/jpg.h" -#include "lib/extras/enc/encode.h" -#include "lib/extras/enc/jpegli.h" -#include "lib/extras/enc/jpg.h" -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/span.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/test_image.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace extras { -namespace { - -using ::jxl::test::Butteraugli3Norm; -using ::jxl::test::ButteraugliDistance; -using ::jxl::test::TestImage; - -Status ReadTestImage(const std::string& pathname, PackedPixelFile* ppf) { - const std::vector encoded = jxl::test::ReadTestData(pathname); - ColorHints color_hints; - if (pathname.find(".ppm") != std::string::npos) { - color_hints.Add("color_space", "RGB_D65_SRG_Rel_SRG"); - } else if (pathname.find(".pgm") != std::string::npos) { - color_hints.Add("color_space", "Gra_D65_Rel_SRG"); - } - return DecodeBytes(Bytes(encoded), color_hints, ppf); -} - -std::vector GetAppData(const std::vector& compressed) { - std::vector result; - size_t pos = 2; // After SOI - while (pos + 4 < compressed.size()) { - if (compressed[pos] != 0xff || compressed[pos + 1] < 0xe0 || - compressed[pos + 1] > 0xf0) { - break; - } - size_t len = (compressed[pos + 2] << 8) + compressed[pos + 3] + 2; - if (pos + len > compressed.size()) { - break; - } - result.insert(result.end(), &compressed[pos], &compressed[pos] + len); - pos += len; - } - return result; -} - -Status DecodeWithLibjpeg(const std::vector& compressed, - PackedPixelFile* ppf, - const JPGDecompressParams* dparams = nullptr) { - return DecodeImageJPG(Bytes(compressed), ColorHints(), ppf, - /*constraints=*/nullptr, dparams); -} - -Status EncodeWithLibjpeg(const PackedPixelFile& ppf, int quality, - std::vector* compressed) { - std::unique_ptr encoder = GetJPEGEncoder(); - encoder->SetOption("q", std::to_string(quality)); - EncodedImage encoded; - JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, nullptr)); - JXL_RETURN_IF_ERROR(!encoded.bitstreams.empty()); - *compressed = std::move(encoded.bitstreams[0]); - return true; -} - -std::string Description(const JxlColorEncoding& color_encoding) { - ColorEncoding c_enc; - EXPECT_TRUE(c_enc.FromExternal(color_encoding)); - return Description(c_enc); -} - -float BitsPerPixel(const PackedPixelFile& ppf, - const std::vector& compressed) { - const size_t num_pixels = ppf.info.xsize * ppf.info.ysize; - return compressed.size() * 8.0 / num_pixels; -} - -TEST(JpegliTest, JpegliSRGBDecodeTest) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf0; - ASSERT_TRUE(ReadTestImage(testimage, &ppf0)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf0.color_encoding)); - EXPECT_EQ(8, ppf0.info.bits_per_sample); - - std::vector compressed; - ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed)); - - PackedPixelFile ppf1; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1)); - PackedPixelFile ppf2; - JpegDecompressParams dparams; - ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf2)); - EXPECT_LT(ButteraugliDistance(ppf0, ppf2), ButteraugliDistance(ppf0, ppf1)); -} - -TEST(JpegliTest, JpegliGrayscaleDecodeTest) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.g.depth8.pgm"; - PackedPixelFile ppf0; - ASSERT_TRUE(ReadTestImage(testimage, &ppf0)); - EXPECT_EQ("Gra_D65_Rel_SRG", Description(ppf0.color_encoding)); - EXPECT_EQ(8, ppf0.info.bits_per_sample); - - std::vector compressed; - ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed)); - - PackedPixelFile ppf1; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1)); - PackedPixelFile ppf2; - JpegDecompressParams dparams; - ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf2)); - EXPECT_LT(ButteraugliDistance(ppf0, ppf2), ButteraugliDistance(ppf0, ppf1)); -} - -TEST(JpegliTest, JpegliXYBEncodeTest) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf_in.color_encoding)); - EXPECT_EQ(8, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - settings.xyb = true; - ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - PackedPixelFile ppf_out; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.45f); - EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); -} - -TEST(JpegliTest, JpegliDecodeTestLargeSmoothArea) { - TEST_LIBJPEG_SUPPORT(); - TestImage t; - const size_t xsize = 2070; - const size_t ysize = 1063; - ASSERT_TRUE(t.SetDimensions(xsize, ysize)); - ASSERT_TRUE(t.SetChannels(3)); - t.SetAllBitDepths(8).SetEndianness(JXL_NATIVE_ENDIAN); - JXL_TEST_ASSIGN_OR_DIE(TestImage::Frame frame, t.AddFrame()); - frame.RandomFill(); - // Create a large smooth area in the top half of the image. This is to test - // that the bias statistics calculation can handle many blocks with all-zero - // AC coefficients. - for (size_t y = 0; y < ysize / 2; ++y) { - for (size_t x = 0; x < xsize; ++x) { - for (size_t c = 0; c < 3; ++c) { - ASSERT_TRUE(frame.SetValue(y, x, c, 0.5f)); - } - } - } - const PackedPixelFile& ppf0 = t.ppf(); - - std::vector compressed; - ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed)); - - PackedPixelFile ppf1; - JpegDecompressParams dparams; - ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf1)); - EXPECT_LT(ButteraugliDistance(ppf0, ppf1), 3.0f); -} - -TEST(JpegliTest, JpegliYUVEncodeTest) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf_in.color_encoding)); - EXPECT_EQ(8, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - settings.xyb = false; - ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - PackedPixelFile ppf_out; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.7f); - EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); -} - -TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf_in.color_encoding)); - EXPECT_EQ(8, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - for (const char* sampling : {"440", "422", "420"}) { - settings.xyb = false; - settings.chroma_subsampling = std::string(sampling); - ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - PackedPixelFile ppf_out; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_LE(BitsPerPixel(ppf_in, compressed), 1.55f); - EXPECT_LE(ButteraugliDistance(ppf_in, ppf_out), 1.82f); - } -} - -TEST(JpegliTest, JpegliYUVEncodeTestNoAq) { - TEST_LIBJPEG_SUPPORT(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf_in.color_encoding)); - EXPECT_EQ(8, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - settings.xyb = false; - settings.use_adaptive_quantization = false; - ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - PackedPixelFile ppf_out; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.85f); - EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.25f); -} - -TEST(JpegliTest, JpegliHDRRoundtripTest) { - std::string testimage = "jxl/hdr_room.png"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("Rec2100HLG", Description(ppf_in.color_encoding)); - EXPECT_EQ(16, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - settings.xyb = false; - ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - PackedPixelFile ppf_out; - JpegDecompressParams dparams; - dparams.output_data_type = JXL_TYPE_UINT16; - ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf_out)); - EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 2.95f); - EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.05f); -} - -TEST(JpegliTest, JpegliSetAppData) { - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf_in; - ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf_in.color_encoding)); - EXPECT_EQ(8, ppf_in.info.bits_per_sample); - - std::vector compressed; - JpegSettings settings; - settings.app_data = {0xff, 0xe3, 0, 4, 0, 1}; - EXPECT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - EXPECT_EQ(settings.app_data, GetAppData(compressed)); - - settings.app_data = {0xff, 0xe3, 0, 6, 0, 1, 2, 3, 0xff, 0xef, 0, 4, 0, 1}; - EXPECT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - EXPECT_EQ(settings.app_data, GetAppData(compressed)); - - settings.xyb = true; - EXPECT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - EXPECT_EQ(0, memcmp(settings.app_data.data(), GetAppData(compressed).data(), - settings.app_data.size())); - - settings.xyb = false; - settings.app_data = {0}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.app_data = {0xff, 0xe0}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.app_data = {0xff, 0xe0, 0, 2}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.app_data = {0xff, 0xeb, 0, 4, 0}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.app_data = {0xff, 0xeb, 0, 4, 0, 1, 2, 3}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.app_data = {0xff, 0xab, 0, 4, 0, 1}; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - - settings.xyb = false; - settings.app_data = { - 0xff, 0xeb, 0, 4, 0, 1, // - 0xff, 0xe2, 0, 20, 0x49, 0x43, 0x43, 0x5F, 0x50, // - 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00, 0, 1, // - 0, 0, 0, 0, // - }; - EXPECT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); - EXPECT_EQ(settings.app_data, GetAppData(compressed)); - - settings.xyb = true; - EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); -} - -struct TestConfig { - int num_colors; - int passes; - int dither; -}; - -class JpegliColorQuantTestParam : public ::testing::TestWithParam { -}; - -TEST_P(JpegliColorQuantTestParam, JpegliColorQuantizeTest) { - TEST_LIBJPEG_SUPPORT(); - TestConfig config = GetParam(); - std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; - PackedPixelFile ppf0; - ASSERT_TRUE(ReadTestImage(testimage, &ppf0)); - EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf0.color_encoding)); - EXPECT_EQ(8, ppf0.info.bits_per_sample); - - std::vector compressed; - ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed)); - - PackedPixelFile ppf1; - JPGDecompressParams dparams1; - dparams1.two_pass_quant = (config.passes == 2); - dparams1.num_colors = config.num_colors; - dparams1.dither_mode = config.dither; - ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1, &dparams1)); - - PackedPixelFile ppf2; - JpegDecompressParams dparams2; - dparams2.two_pass_quant = (config.passes == 2); - dparams2.num_colors = config.num_colors; - dparams2.dither_mode = config.dither; - ASSERT_TRUE(DecodeJpeg(compressed, dparams2, nullptr, &ppf2)); - - double dist1 = Butteraugli3Norm(ppf0, ppf1); - double dist2 = Butteraugli3Norm(ppf0, ppf2); - printf("distance: %f vs %f\n", dist2, dist1); - if (config.passes == 1) { - if (config.num_colors == 16 && config.dither == 2) { - // TODO(szabadka) Fix this case. - EXPECT_LT(dist2, dist1 * 1.5); - } else { - EXPECT_LT(dist2, dist1 * 1.05); - } - } else if (config.num_colors > 64) { - // TODO(szabadka) Fix 2pass quantization for <= 64 colors. - EXPECT_LT(dist2, dist1 * 1.1); - } else if (config.num_colors > 32) { - EXPECT_LT(dist2, dist1 * 1.2); - } else { - EXPECT_LT(dist2, dist1 * 1.7); - } -} - -std::vector GenerateTests() { - std::vector all_tests; - for (int num_colors = 8; num_colors <= 256; num_colors *= 2) { - for (int passes = 1; passes <= 2; ++passes) { - for (int dither = 0; dither < 3; dither += passes) { - TestConfig config; - config.num_colors = num_colors; - config.passes = passes; - config.dither = dither; - all_tests.push_back(config); - } - } - } - return all_tests; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - static constexpr const char* kDitherModeStr[] = {"No", "Ordered", "FS"}; - os << c.passes << "pass"; - os << c.num_colors << "colors"; - os << kDitherModeStr[c.dither] << "dither"; - return os; -} - -std::string TestDescription(const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JXL_GTEST_INSTANTIATE_TEST_SUITE_P(JpegliColorQuantTest, - JpegliColorQuantTestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace extras -} // namespace jxl -#endif // JPEGXL_ENABLE_JPEGLI diff --git a/third_party/jpeg-xl/lib/extras/metrics.cc b/third_party/jpeg-xl/lib/extras/metrics.cc deleted file mode 100644 index 55c1130944480..0000000000000 --- a/third_party/jpeg-xl/lib/extras/metrics.cc +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/metrics.h" - -#include -#include - -#include - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/extras/metrics.cc" -#include -#include - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/color_encoding_internal.h" -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::GetLane; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::Rebind; - -double ComputeDistanceP(const ImageF& distmap, const ButteraugliParams& params, - double p) { - const double onePerPixels = 1.0 / (distmap.ysize() * distmap.xsize()); - if (std::abs(p - 3.0) < 1E-6) { - double sum1[3] = {0.0}; - -// Prefer double if possible, but otherwise use float rather than scalar. -#if HWY_CAP_FLOAT64 - using T = double; - const Rebind df; -#else - using T = float; -#endif - const HWY_FULL(T) d; - constexpr size_t N = MaxLanes(d); - // Manually aligned storage to avoid asan crash on clang-7 due to - // unaligned spill. - HWY_ALIGN T sum_totals0[N] = {0}; - HWY_ALIGN T sum_totals1[N] = {0}; - HWY_ALIGN T sum_totals2[N] = {0}; - - for (size_t y = 0; y < distmap.ysize(); ++y) { - const float* JXL_RESTRICT row = distmap.ConstRow(y); - - auto sums0 = Zero(d); - auto sums1 = Zero(d); - auto sums2 = Zero(d); - - size_t x = 0; - for (; x + Lanes(d) <= distmap.xsize(); x += Lanes(d)) { -#if HWY_CAP_FLOAT64 - const auto d1 = PromoteTo(d, Load(df, row + x)); -#else - const auto d1 = Load(d, row + x); -#endif - const auto d2 = Mul(d1, Mul(d1, d1)); - sums0 = Add(sums0, d2); - const auto d3 = Mul(d2, d2); - sums1 = Add(sums1, d3); - const auto d4 = Mul(d3, d3); - sums2 = Add(sums2, d4); - } - - Store(Add(sums0, Load(d, sum_totals0)), d, sum_totals0); - Store(Add(sums1, Load(d, sum_totals1)), d, sum_totals1); - Store(Add(sums2, Load(d, sum_totals2)), d, sum_totals2); - - for (; x < distmap.xsize(); ++x) { - const double d1 = row[x]; - double d2 = d1 * d1 * d1; - sum1[0] += d2; - d2 *= d2; - sum1[1] += d2; - d2 *= d2; - sum1[2] += d2; - } - } - double v = 0; - v += pow( - onePerPixels * (sum1[0] + GetLane(SumOfLanes(d, Load(d, sum_totals0)))), - 1.0 / (p * 1.0)); - v += pow( - onePerPixels * (sum1[1] + GetLane(SumOfLanes(d, Load(d, sum_totals1)))), - 1.0 / (p * 2.0)); - v += pow( - onePerPixels * (sum1[2] + GetLane(SumOfLanes(d, Load(d, sum_totals2)))), - 1.0 / (p * 4.0)); - v /= 3.0; - return v; - } else { - static std::atomic once{0}; - if (once.fetch_add(1, std::memory_order_relaxed) == 0) { - JXL_WARNING("WARNING: using slow ComputeDistanceP"); - } - double sum1[3] = {0.0}; - for (size_t y = 0; y < distmap.ysize(); ++y) { - const float* JXL_RESTRICT row = distmap.ConstRow(y); - for (size_t x = 0; x < distmap.xsize(); ++x) { - double d2 = std::pow(row[x], p); - sum1[0] += d2; - d2 *= d2; - sum1[1] += d2; - d2 *= d2; - sum1[2] += d2; - } - } - double v = 0; - for (int i = 0; i < 3; ++i) { - v += pow(onePerPixels * (sum1[i]), 1.0 / (p * (1 << i))); - } - v /= 3.0; - return v; - } -} - -void ComputeSumOfSquares(const ImageBundle& ib1, const ImageBundle& ib2, - const JxlCmsInterface& cms, double sum_of_squares[3]) { - sum_of_squares[0] = sum_of_squares[1] = sum_of_squares[2] = - std::numeric_limits::max(); - // Convert to sRGB - closer to perception than linear. - const Image3F* srgb1 = &ib1.color(); - Image3F copy1; - if (!ib1.IsSRGB()) { - if (!ib1.CopyTo(Rect(ib1), ColorEncoding::SRGB(ib1.IsGray()), cms, ©1)) - return; - srgb1 = ©1; - } - const Image3F* srgb2 = &ib2.color(); - Image3F copy2; - if (!ib2.IsSRGB()) { - if (!ib2.CopyTo(Rect(ib2), ColorEncoding::SRGB(ib2.IsGray()), cms, ©2)) - return; - srgb2 = ©2; - } - - if (!SameSize(*srgb1, *srgb2)) return; - - sum_of_squares[0] = sum_of_squares[1] = sum_of_squares[2] = 0.0; - - // TODO(veluca): SIMD. - float yuvmatrix[3][3] = {{0.299, 0.587, 0.114}, - {-0.14713, -0.28886, 0.436}, - {0.615, -0.51499, -0.10001}}; - for (size_t y = 0; y < srgb1->ysize(); ++y) { - const float* JXL_RESTRICT row1[3]; - const float* JXL_RESTRICT row2[3]; - for (size_t j = 0; j < 3; j++) { - row1[j] = srgb1->ConstPlaneRow(j, y); - row2[j] = srgb2->ConstPlaneRow(j, y); - } - for (size_t x = 0; x < srgb1->xsize(); ++x) { - float cdiff[3] = {}; - // YUV conversion is linear, so we can run it on the difference. - for (size_t j = 0; j < 3; j++) { - cdiff[j] = row1[j][x] - row2[j][x]; - } - float yuvdiff[3] = {}; - for (size_t j = 0; j < 3; j++) { - for (size_t k = 0; k < 3; k++) { - yuvdiff[j] += yuvmatrix[j][k] * cdiff[k]; - } - } - for (size_t j = 0; j < 3; j++) { - sum_of_squares[j] += yuvdiff[j] * yuvdiff[j]; - } - } - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jxl { -HWY_EXPORT(ComputeDistanceP); -double ComputeDistanceP(const ImageF& distmap, const ButteraugliParams& params, - double p) { - return HWY_DYNAMIC_DISPATCH(ComputeDistanceP)(distmap, params, p); -} - -HWY_EXPORT(ComputeSumOfSquares); - -double ComputeDistance2(const ImageBundle& ib1, const ImageBundle& ib2, - const JxlCmsInterface& cms) { - double sum_of_squares[3] = {}; - HWY_DYNAMIC_DISPATCH(ComputeSumOfSquares)(ib1, ib2, cms, sum_of_squares); - // Weighted PSNR as in JPEG-XL: chroma counts 1/8. - const float weights[3] = {6.0f / 8, 1.0f / 8, 1.0f / 8}; - // Avoid squaring the weight - 1/64 is too extreme. - double norm = 0; - for (size_t i = 0; i < 3; i++) { - norm += std::sqrt(sum_of_squares[i]) * weights[i]; - } - // This function returns distance *squared*. - return norm * norm; -} - -double ComputePSNR(const ImageBundle& ib1, const ImageBundle& ib2, - const JxlCmsInterface& cms) { - if (!SameSize(ib1, ib2)) return 0.0; - double sum_of_squares[3] = {}; - HWY_DYNAMIC_DISPATCH(ComputeSumOfSquares)(ib1, ib2, cms, sum_of_squares); - constexpr double kChannelWeights[3] = {6.0 / 8, 1.0 / 8, 1.0 / 8}; - double avg_psnr = 0; - const size_t input_pixels = ib1.xsize() * ib1.ysize(); - for (int i = 0; i < 3; ++i) { - const double rmse = std::sqrt(sum_of_squares[i] / input_pixels); - const double psnr = - sum_of_squares[i] == 0 ? 99.99 : (20 * std::log10(1 / rmse)); - avg_psnr += kChannelWeights[i] * psnr; - } - return avg_psnr; -} - -} // namespace jxl -#endif diff --git a/third_party/jpeg-xl/lib/extras/metrics.h b/third_party/jpeg-xl/lib/extras/metrics.h deleted file mode 100644 index 87a69a99cea51..0000000000000 --- a/third_party/jpeg-xl/lib/extras/metrics.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_METRICS_H_ -#define LIB_EXTRAS_METRICS_H_ - -#include - -#include "lib/jxl/butteraugli/butteraugli.h" -#include "lib/jxl/image_bundle.h" - -namespace jxl { - -// Computes p-norm given the butteraugli distmap. -double ComputeDistanceP(const ImageF& distmap, const ButteraugliParams& params, - double p); - -double ComputeDistance2(const ImageBundle& ib1, const ImageBundle& ib2, - const JxlCmsInterface& cms); - -double ComputePSNR(const ImageBundle& ib1, const ImageBundle& ib2, - const JxlCmsInterface& cms); - -} // namespace jxl - -#endif // LIB_EXTRAS_METRICS_H_ diff --git a/third_party/jpeg-xl/lib/extras/mmap.cc b/third_party/jpeg-xl/lib/extras/mmap.cc deleted file mode 100644 index 9f5bba97edae5..0000000000000 --- a/third_party/jpeg-xl/lib/extras/mmap.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "mmap.h" - -#include -#include - -#include "lib/jxl/base/common.h" - -#if defined(__unix__) || defined(__unix) || \ - defined(__APPLE__) && defined(__MACH__) -#include -#include -#include - -namespace jxl { - -struct MemoryMappedFileImpl { - static StatusOr> Init( - const char* path) { - auto f = make_unique(); - f->fd = open(path, O_RDONLY); - if (f->fd == -1) { - return JXL_FAILURE("Cannot open file %s", path); - } - f->mmap_len = lseek(f->fd, 0, SEEK_END); - lseek(f->fd, 0, SEEK_SET); - - f->ptr = mmap(nullptr, f->mmap_len, PROT_READ, MAP_SHARED, f->fd, 0); - if (f->ptr == MAP_FAILED) { - return JXL_FAILURE("mmap failure"); - } - return f; - } - - const uint8_t* data() const { return reinterpret_cast(ptr); } - size_t size() const { return mmap_len; } - - ~MemoryMappedFileImpl() { - if (fd != -1) { - close(fd); - } - if (ptr != nullptr) { - munmap(ptr, mmap_len); - } - } - - int fd = -1; - size_t mmap_len = 0; - void* ptr = nullptr; -}; - -} // namespace jxl - -#elif defined(_WIN32) -#include -#include - -namespace { - -struct HandleDeleter { - void operator()(const HANDLE handle) const { - if (handle != INVALID_HANDLE_VALUE) { - CloseHandle(handle); - } - } -}; -using HandleUniquePtr = - std::unique_ptr::type, HandleDeleter>; - -} // namespace - -namespace jxl { - -struct MemoryMappedFileImpl { - static StatusOr> Init( - const char* path) { - auto f = make_unique(); - std::wstring stemp = std::wstring(path, path + strlen(path)); - f->handle.reset(CreateFileW(stemp.c_str(), GENERIC_READ, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, nullptr)); - if (f->handle.get() == INVALID_HANDLE_VALUE) { - return JXL_FAILURE("Cannot open file %s", path); - } - if (!GetFileSizeEx(f->handle.get(), &f->fsize)) { - return JXL_FAILURE("Cannot get file size (%s)", path); - } - f->handle_mapping.reset(CreateFileMappingW(f->handle.get(), nullptr, - PAGE_READONLY, 0, 0, nullptr)); - if (f->handle_mapping == nullptr) { - return JXL_FAILURE("Cannot create memory mapping (%s)", path); - } - f->ptr = MapViewOfFile(f->handle_mapping.get(), FILE_MAP_READ, 0, 0, 0); - return f; - } - - ~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); } - - const uint8_t* data() const { return reinterpret_cast(ptr); } - size_t size() const { return fsize.QuadPart; } - - HandleUniquePtr handle; - HandleUniquePtr handle_mapping; - LARGE_INTEGER fsize; - void* ptr = nullptr; -}; - -} // namespace jxl - -#else - -namespace jxl { - -struct MemoryMappedFileImpl { - static StatusOr> Init( - const char* path) { - return JXL_FAILURE("Memory mapping not supported on this system"); - } - - const uint8_t* data() const { return nullptr; } - size_t size() const { return 0; } -}; - -} // namespace jxl - -#endif - -namespace jxl { - -StatusOr MemoryMappedFile::Init(const char* path) { - JXL_ASSIGN_OR_RETURN(auto mmf, MemoryMappedFileImpl::Init(path)); - MemoryMappedFile ret; - ret.impl_ = std::move(mmf); - return ret; -} - -MemoryMappedFile::MemoryMappedFile() = default; -MemoryMappedFile::~MemoryMappedFile() = default; -MemoryMappedFile::MemoryMappedFile(MemoryMappedFile&&) noexcept = default; -MemoryMappedFile& MemoryMappedFile::operator=(MemoryMappedFile&&) noexcept = - default; - -const uint8_t* MemoryMappedFile::data() const { return impl_->data(); } -size_t MemoryMappedFile::size() const { return impl_->size(); } -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/mmap.h b/third_party/jpeg-xl/lib/extras/mmap.h deleted file mode 100644 index 60cc215993eea..0000000000000 --- a/third_party/jpeg-xl/lib/extras/mmap.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_MMAP_H_ -#define LIB_EXTRAS_MMAP_H_ - -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { -struct MemoryMappedFileImpl; - -class MemoryMappedFile { - public: - static StatusOr Init(const char* path); - const uint8_t* data() const; - size_t size() const; - MemoryMappedFile(); // NOLINT - ~MemoryMappedFile(); // NOLINT - MemoryMappedFile(MemoryMappedFile&&) noexcept; // NOLINT - MemoryMappedFile& operator=(MemoryMappedFile&&) noexcept; // NOLINT - - private: - std::unique_ptr impl_; -}; -} // namespace jxl - -#endif diff --git a/third_party/jpeg-xl/lib/extras/packed_image.h b/third_party/jpeg-xl/lib/extras/packed_image.h deleted file mode 100644 index 8a88af618811a..0000000000000 --- a/third_party/jpeg-xl/lib/extras/packed_image.h +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_PACKED_IMAGE_H_ -#define LIB_EXTRAS_PACKED_IMAGE_H_ - -// Helper class for storing external (int or float, interleaved) images. This is -// the common format used by other libraries and in the libjxl API. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/status.h" - -namespace jxl { -namespace extras { - -// Class representing an interleaved image with a bunch of channels. -class PackedImage { - public: - static StatusOr Create(size_t xsize, size_t ysize, - const JxlPixelFormat& format) { - PackedImage image(xsize, ysize, format, CalcStride(format, xsize)); - if (!image.pixels()) { - // TODO(szabadka): use specialized OOM error code - return JXL_FAILURE("Failed to allocate memory for image"); - } - return image; - } - - PackedImage Copy() const { - PackedImage copy(xsize, ysize, format, CalcStride(format, xsize)); - memcpy(reinterpret_cast(copy.pixels()), - reinterpret_cast(pixels()), pixels_size); - return copy; - } - - // The interleaved pixels as defined in the storage format. - void* pixels() const { return pixels_.get(); } - - uint8_t* pixels(size_t y, size_t x, size_t c) const { - return (reinterpret_cast(pixels_.get()) + y * stride + - x * pixel_stride_ + c * bytes_per_channel_); - } - - const uint8_t* const_pixels(size_t y, size_t x, size_t c) const { - return (reinterpret_cast(pixels_.get()) + y * stride + - x * pixel_stride_ + c * bytes_per_channel_); - } - - // The image size in pixels. - size_t xsize; - size_t ysize; - - // The number of bytes per row. - size_t stride; - - // Pixel storage format and buffer size of the pixels_ pointer. - JxlPixelFormat format; - size_t pixels_size; - - size_t pixel_stride() const { return pixel_stride_; } - - static Status ValidateDataType(JxlDataType data_type) { - if ((data_type != JXL_TYPE_UINT8) && (data_type != JXL_TYPE_UINT16) && - (data_type != JXL_TYPE_FLOAT) && (data_type != JXL_TYPE_FLOAT16)) { - return JXL_FAILURE("Unhandled data type: %d", - static_cast(data_type)); - } - return true; - } - - static size_t BitsPerChannel(JxlDataType data_type) { - switch (data_type) { - case JXL_TYPE_UINT8: - return 8; - case JXL_TYPE_UINT16: - return 16; - case JXL_TYPE_FLOAT: - return 32; - case JXL_TYPE_FLOAT16: - return 16; - default: - JXL_DEBUG_ABORT("Unreachable"); - return 0; - } - } - - float GetPixelValue(size_t y, size_t x, size_t c) const { - const uint8_t* data = const_pixels(y, x, c); - switch (format.data_type) { - case JXL_TYPE_UINT8: - return data[0] * (1.0f / 255); - case JXL_TYPE_UINT16: { - uint16_t val; - memcpy(&val, data, 2); - return (swap_endianness_ ? JXL_BSWAP16(val) : val) * (1.0f / 65535); - } - case JXL_TYPE_FLOAT: { - float val; - memcpy(&val, data, 4); - return swap_endianness_ ? BSwapFloat(val) : val; - } - default: - JXL_DEBUG_ABORT("Unreachable"); - return 0.0f; - } - } - - void SetPixelValue(size_t y, size_t x, size_t c, float val) const { - uint8_t* data = pixels(y, x, c); - switch (format.data_type) { - case JXL_TYPE_UINT8: - data[0] = Clamp1(std::round(val * 255), 0.0f, 255.0f); - break; - case JXL_TYPE_UINT16: { - uint16_t val16 = Clamp1(std::round(val * 65535), 0.0f, 65535.0f); - if (swap_endianness_) { - val16 = JXL_BSWAP16(val16); - } - memcpy(data, &val16, 2); - break; - } - case JXL_TYPE_FLOAT: { - if (swap_endianness_) { - val = BSwapFloat(val); - } - memcpy(data, &val, 4); - break; - } - default: - JXL_DEBUG_ABORT("Unreachable"); - } - } - - private: - PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format, - size_t stride) - : xsize(xsize), - ysize(ysize), - stride(stride), - format(format), - pixels_size(ysize * stride), - pixels_(malloc(std::max(1, pixels_size)), free) { - bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte; - pixel_stride_ = format.num_channels * bytes_per_channel_; - swap_endianness_ = SwapEndianness(format.endianness); - } - - static size_t CalcStride(const JxlPixelFormat& format, size_t xsize) { - size_t stride = xsize * (BitsPerChannel(format.data_type) * - format.num_channels / jxl::kBitsPerByte); - if (format.align > 1) { - stride = jxl::DivCeil(stride, format.align) * format.align; - } - return stride; - } - - size_t bytes_per_channel_; - size_t pixel_stride_; - bool swap_endianness_; - std::unique_ptr pixels_; -}; - -// Helper class representing a frame, as seen from the API. Animations will have -// multiple frames, but a single frame can have a color/grayscale channel and -// multiple extra channels. The order of the extra channels should be the same -// as all other frames in the same image. -class PackedFrame { - public: - explicit PackedFrame(PackedImage&& image) : color(std::move(image)) {} - - static StatusOr Create(size_t xsize, size_t ysize, - const JxlPixelFormat& format) { - JXL_ASSIGN_OR_RETURN(PackedImage image, - PackedImage::Create(xsize, ysize, format)); - PackedFrame frame(std::move(image)); - return frame; - } - - StatusOr Copy() const { - JXL_ASSIGN_OR_RETURN( - PackedFrame copy, - PackedFrame::Create(color.xsize, color.ysize, color.format)); - copy.frame_info = frame_info; - copy.name = name; - copy.color = color.Copy(); - for (const auto& ec : extra_channels) { - copy.extra_channels.emplace_back(ec.Copy()); - } - return copy; - } - - // The Frame metadata. - JxlFrameHeader frame_info = {}; - std::string name; - - // The pixel data for the color (or grayscale) channels. - PackedImage color; - // Extra channel image data. - std::vector extra_channels; -}; - -class ChunkedPackedFrame { - public: - ChunkedPackedFrame( - size_t xsize, size_t ysize, - std::function get_input_source) - : xsize(xsize), - ysize(ysize), - get_input_source_(std::move(get_input_source)) { - const auto input_source = get_input_source_(); - input_source.get_color_channels_pixel_format(input_source.opaque, &format); - } - - JxlChunkedFrameInputSource GetInputSource() { return get_input_source_(); } - - // The Frame metadata. - JxlFrameHeader frame_info = {}; - std::string name; - - size_t xsize; - size_t ysize; - JxlPixelFormat format; - - private: - std::function get_input_source_; -}; - -// Optional metadata associated with a file -class PackedMetadata { - public: - std::vector exif; - std::vector iptc; - std::vector jhgm; - std::vector jumbf; - std::vector xmp; -}; - -// The extra channel metadata information. -struct PackedExtraChannel { - JxlExtraChannelInfo ec_info; - size_t index; - std::string name; -}; - -// Helper class representing a JXL image file as decoded to pixels from the API. -class PackedPixelFile { - public: - JxlBasicInfo info = {}; - - std::vector extra_channels_info; - - // Color information of the decoded pixels. - // `primary_color_representation` indicates whether `color_encoding` or `icc` - // is the “authoritative” encoding of the colorspace, as opposed to a fallback - // encoding. For example, if `color_encoding` is the primary one, as would - // occur when decoding a jxl file with such a representation, then `enc/jxl` - // will use it and ignore the ICC profile, whereas `enc/png` will include the - // ICC profile for compatibility. - // If `icc` is the primary representation, `enc/jxl` will preserve it when - // compressing losslessly, but *may* encode it as a color_encoding when - // compressing lossily. - enum { - kColorEncodingIsPrimary, - kIccIsPrimary - } primary_color_representation = kColorEncodingIsPrimary; - JxlColorEncoding color_encoding = {}; - std::vector icc; - // The icc profile of the original image. - std::vector orig_icc; - - JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; - - std::unique_ptr preview_frame; - std::vector frames; - mutable std::vector chunked_frames; - - PackedMetadata metadata; - PackedPixelFile() { JxlEncoderInitBasicInfo(&info); }; - - size_t num_frames() const { - return chunked_frames.empty() ? frames.size() : chunked_frames.size(); - } - size_t xsize() const { return info.xsize; } - size_t ysize() const { return info.ysize; } -}; - -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_PACKED_IMAGE_H_ diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc deleted file mode 100644 index 7bdb3cdf64a3d..0000000000000 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/packed_image_convert.h" - -#include -#include -#include -#include - -#include -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/dec_external_image.h" -#include "lib/jxl/enc_external_image.h" -#include "lib/jxl/enc_image_bundle.h" -#include "lib/jxl/luminance.h" - -namespace jxl { -namespace extras { - -Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, - const JxlBitDepth& input_bitdepth, - const PackedFrame& frame, - const CodecInOut& io, ThreadPool* pool, - ImageBundle* bundle) { - JxlMemoryManager* memory_manager = io.memory_manager; - JXL_ENSURE(frame.color.pixels() != nullptr); - size_t frame_bits_per_sample; - if (input_bitdepth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) { - JXL_RETURN_IF_ERROR( - PackedImage::ValidateDataType(frame.color.format.data_type)); - frame_bits_per_sample = - PackedImage::BitsPerChannel(frame.color.format.data_type); - } else { - frame_bits_per_sample = info.bits_per_sample; - } - JXL_ENSURE(frame_bits_per_sample != 0); - // It is ok for the frame.color.format.num_channels to not match the - // number of channels on the image. - JXL_ENSURE(1 <= frame.color.format.num_channels && - frame.color.format.num_channels <= 4); - - const Span span( - static_cast(frame.color.pixels()), - frame.color.pixels_size); - JXL_ENSURE(Rect(frame.frame_info.layer_info.crop_x0, - frame.frame_info.layer_info.crop_y0, - frame.frame_info.layer_info.xsize, - frame.frame_info.layer_info.ysize) - .IsInside(Rect(0, 0, info.xsize, info.ysize))); - if (info.have_animation) { - bundle->duration = frame.frame_info.duration; - bundle->blend = frame.frame_info.layer_info.blend_info.blendmode > 0; - bundle->use_for_next_frame = - frame.frame_info.layer_info.save_as_reference > 0; - bundle->origin.x0 = frame.frame_info.layer_info.crop_x0; - bundle->origin.y0 = frame.frame_info.layer_info.crop_y0; - } - bundle->name = frame.name; // frame.frame_info.name_length is ignored here. - JXL_ENSURE(io.metadata.m.color_encoding.IsGray() == - (frame.color.format.num_channels <= 2)); - - JXL_RETURN_IF_ERROR(ConvertFromExternal( - span, frame.color.xsize, frame.color.ysize, io.metadata.m.color_encoding, - frame_bits_per_sample, frame.color.format, pool, bundle)); - - bundle->extra_channels().resize(io.metadata.m.extra_channel_info.size()); - for (size_t i = 0; i < frame.extra_channels.size(); i++) { - const auto& ppf_ec = frame.extra_channels[i]; - JXL_ASSIGN_OR_RETURN( - bundle->extra_channels()[i], - ImageF::Create(memory_manager, ppf_ec.xsize, ppf_ec.ysize)); - JXL_RETURN_IF_ERROR(BufferToImageF( - ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize, ppf_ec.pixels(), - ppf_ec.pixels_size, pool, &bundle->extra_channels()[i])); - } - return true; -} - -Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, - ThreadPool* pool, CodecInOut* io) { - JxlMemoryManager* memory_manager = io->memory_manager; - const bool has_alpha = ppf.info.alpha_bits != 0; - JXL_ENSURE(!ppf.frames.empty()); - if (has_alpha) { - JXL_ENSURE(ppf.info.alpha_bits == ppf.info.bits_per_sample); - JXL_ENSURE(ppf.info.alpha_exponent_bits == - ppf.info.exponent_bits_per_sample); - } - - const bool is_gray = (ppf.info.num_color_channels == 1); - JXL_ENSURE(ppf.info.num_color_channels == 1 || - ppf.info.num_color_channels == 3); - - // Convert the image metadata - JXL_RETURN_IF_ERROR(io->SetSize(ppf.info.xsize, ppf.info.ysize)); - io->metadata.m.bit_depth.bits_per_sample = ppf.info.bits_per_sample; - io->metadata.m.bit_depth.exponent_bits_per_sample = - ppf.info.exponent_bits_per_sample; - io->metadata.m.bit_depth.floating_point_sample = - ppf.info.exponent_bits_per_sample != 0; - io->metadata.m.modular_16_bit_buffer_sufficient = - ppf.info.exponent_bits_per_sample == 0 && ppf.info.bits_per_sample <= 12; - - io->metadata.m.SetAlphaBits(ppf.info.alpha_bits, - FROM_JXL_BOOL(ppf.info.alpha_premultiplied)); - ExtraChannelInfo* alpha = io->metadata.m.Find(ExtraChannel::kAlpha); - if (alpha) alpha->bit_depth = io->metadata.m.bit_depth; - - io->metadata.m.xyb_encoded = !FROM_JXL_BOOL(ppf.info.uses_original_profile); - JXL_ENSURE(ppf.info.orientation > 0 && ppf.info.orientation <= 8); - io->metadata.m.orientation = ppf.info.orientation; - - // Convert animation metadata - JXL_ENSURE(ppf.frames.size() == 1 || ppf.info.have_animation); - io->metadata.m.have_animation = FROM_JXL_BOOL(ppf.info.have_animation); - io->metadata.m.animation.tps_numerator = ppf.info.animation.tps_numerator; - io->metadata.m.animation.tps_denominator = ppf.info.animation.tps_denominator; - io->metadata.m.animation.num_loops = ppf.info.animation.num_loops; - - // Convert the color encoding. - if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { - IccBytes icc = ppf.icc; - if (!io->metadata.m.color_encoding.SetICC(std::move(icc), - JxlGetDefaultCms())) { - fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB\n"); - io->metadata.m.color_encoding = ColorEncoding::SRGB(is_gray); - } else { - if (io->metadata.m.color_encoding.IsCMYK()) { - // We expect gray or tri-color. - return JXL_FAILURE("Embedded ICC is CMYK"); - } - if (io->metadata.m.color_encoding.IsGray() != is_gray) { - // E.g. JPG image has 3 channels, but gray ICC. - return JXL_FAILURE("Embedded ICC does not match image color type"); - } - } - } else { - JXL_RETURN_IF_ERROR( - io->metadata.m.color_encoding.FromExternal(ppf.color_encoding)); - if (io->metadata.m.color_encoding.ICC().empty()) { - return JXL_FAILURE("Failed to serialize ICC"); - } - } - - // Convert the extra blobs - io->blobs.exif = ppf.metadata.exif; - io->blobs.iptc = ppf.metadata.iptc; - io->blobs.jhgm = ppf.metadata.jhgm; - io->blobs.jumbf = ppf.metadata.jumbf; - io->blobs.xmp = ppf.metadata.xmp; - - // Append all other extra channels. - for (const auto& info : ppf.extra_channels_info) { - ExtraChannelInfo out; - out.type = static_cast(info.ec_info.type); - out.bit_depth.bits_per_sample = info.ec_info.bits_per_sample; - out.bit_depth.exponent_bits_per_sample = - info.ec_info.exponent_bits_per_sample; - out.bit_depth.floating_point_sample = - info.ec_info.exponent_bits_per_sample != 0; - out.dim_shift = info.ec_info.dim_shift; - out.name = info.name; - out.alpha_associated = (info.ec_info.alpha_premultiplied != 0); - out.spot_color[0] = info.ec_info.spot_color[0]; - out.spot_color[1] = info.ec_info.spot_color[1]; - out.spot_color[2] = info.ec_info.spot_color[2]; - out.spot_color[3] = info.ec_info.spot_color[3]; - io->metadata.m.extra_channel_info.push_back(std::move(out)); - } - - // Convert the preview - if (ppf.preview_frame) { - size_t preview_xsize = ppf.preview_frame->color.xsize; - size_t preview_ysize = ppf.preview_frame->color.ysize; - io->metadata.m.have_preview = true; - JXL_RETURN_IF_ERROR( - io->metadata.m.preview_size.Set(preview_xsize, preview_ysize)); - JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( - ppf.info, ppf.input_bitdepth, *ppf.preview_frame, *io, pool, - &io->preview_frame)); - } - - // Convert the pixels - io->frames.clear(); - for (const auto& frame : ppf.frames) { - ImageBundle bundle(memory_manager, &io->metadata.m); - JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( - ppf.info, ppf.input_bitdepth, frame, *io, pool, &bundle)); - io->frames.push_back(std::move(bundle)); - } - - if (ppf.info.exponent_bits_per_sample == 0) { - // uint case. - io->metadata.m.bit_depth.bits_per_sample = io->Main().DetectRealBitdepth(); - } - if (ppf.info.intensity_target != 0) { - io->metadata.m.SetIntensityTarget(ppf.info.intensity_target); - } else { - SetIntensityTarget(&io->metadata.m); - } - JXL_RETURN_IF_ERROR(io->CheckMetadata()); - return true; -} - -StatusOr ConvertImage3FToPackedPixelFile( - const Image3F& image, const ColorEncoding& c_enc, JxlPixelFormat format, - ThreadPool* pool) { - PackedPixelFile ppf{}; - ppf.info.xsize = image.xsize(); - ppf.info.ysize = image.ysize(); - ppf.info.num_color_channels = 3; - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(format.data_type)); - ppf.info.bits_per_sample = PackedImage::BitsPerChannel(format.data_type); - ppf.info.exponent_bits_per_sample = format.data_type == JXL_TYPE_FLOAT ? 8 - : format.data_type == JXL_TYPE_FLOAT16 - ? 5 - : 0; - ppf.color_encoding = c_enc.ToExternal(); - ppf.frames.clear(); - JXL_ASSIGN_OR_RETURN( - PackedFrame frame, - PackedFrame::Create(image.xsize(), image.ysize(), format)); - const ImageF* channels[3]; - for (int c = 0; c < 3; ++c) { - channels[c] = &image.Plane(c); - } - bool float_samples = ppf.info.exponent_bits_per_sample > 0; - JXL_RETURN_IF_ERROR(ConvertChannelsToExternal( - channels, 3, ppf.info.bits_per_sample, float_samples, format.endianness, - frame.color.stride, pool, frame.color.pixels(0, 0, 0), - frame.color.pixels_size, PixelCallback(), Orientation::kIdentity)); - ppf.frames.emplace_back(std::move(frame)); - return ppf; -} - -// Allows converting from internal CodecInOut to external PackedPixelFile -Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, - const JxlPixelFormat& pixel_format, - const ColorEncoding& c_desired, - ThreadPool* pool, - PackedPixelFile* ppf) { - JxlMemoryManager* memory_manager = io.memory_manager; - const bool has_alpha = io.metadata.m.HasAlpha(); - JXL_ENSURE(!io.frames.empty()); - - if (has_alpha) { - JXL_ENSURE(io.metadata.m.GetAlphaBits() == - io.metadata.m.bit_depth.bits_per_sample); - const auto* alpha_channel = io.metadata.m.Find(ExtraChannel::kAlpha); - JXL_ENSURE(alpha_channel->bit_depth.exponent_bits_per_sample == - io.metadata.m.bit_depth.exponent_bits_per_sample); - ppf->info.alpha_bits = alpha_channel->bit_depth.bits_per_sample; - ppf->info.alpha_exponent_bits = - alpha_channel->bit_depth.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = - TO_JXL_BOOL(alpha_channel->alpha_associated); - } - - // Convert the image metadata - ppf->info.xsize = io.metadata.size.xsize(); - ppf->info.ysize = io.metadata.size.ysize(); - ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels(); - ppf->info.bits_per_sample = io.metadata.m.bit_depth.bits_per_sample; - ppf->info.exponent_bits_per_sample = - io.metadata.m.bit_depth.exponent_bits_per_sample; - - ppf->info.intensity_target = io.metadata.m.tone_mapping.intensity_target; - ppf->info.linear_below = io.metadata.m.tone_mapping.linear_below; - ppf->info.min_nits = io.metadata.m.tone_mapping.min_nits; - ppf->info.relative_to_max_display = - TO_JXL_BOOL(io.metadata.m.tone_mapping.relative_to_max_display); - - ppf->info.uses_original_profile = TO_JXL_BOOL(!io.metadata.m.xyb_encoded); - JXL_ENSURE(0 < io.metadata.m.orientation && io.metadata.m.orientation <= 8); - ppf->info.orientation = - static_cast(io.metadata.m.orientation); - ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels(); - - // Convert animation metadata - JXL_ENSURE(io.frames.size() == 1 || io.metadata.m.have_animation); - ppf->info.have_animation = TO_JXL_BOOL(io.metadata.m.have_animation); - ppf->info.animation.tps_numerator = io.metadata.m.animation.tps_numerator; - ppf->info.animation.tps_denominator = io.metadata.m.animation.tps_denominator; - ppf->info.animation.num_loops = io.metadata.m.animation.num_loops; - - // Convert the color encoding - ppf->icc.assign(c_desired.ICC().begin(), c_desired.ICC().end()); - ppf->primary_color_representation = - c_desired.WantICC() ? PackedPixelFile::kIccIsPrimary - : PackedPixelFile::kColorEncodingIsPrimary; - ppf->color_encoding = c_desired.ToExternal(); - - // Convert the extra blobs - ppf->metadata.exif = io.blobs.exif; - ppf->metadata.iptc = io.blobs.iptc; - ppf->metadata.jhgm = io.blobs.jhgm; - ppf->metadata.jumbf = io.blobs.jumbf; - ppf->metadata.xmp = io.blobs.xmp; - const bool float_out = pixel_format.data_type == JXL_TYPE_FLOAT || - pixel_format.data_type == JXL_TYPE_FLOAT16; - // Convert the pixels - ppf->frames.clear(); - for (const auto& frame : io.frames) { - JXL_ENSURE(frame.metadata()->bit_depth.bits_per_sample != 0); - // It is ok for the frame.color().kNumPlanes to not match the - // number of channels on the image. - const uint32_t alpha_channels = has_alpha ? 1 : 0; - const uint32_t num_channels = - frame.metadata()->color_encoding.Channels() + alpha_channels; - JxlPixelFormat format{/*num_channels=*/num_channels, - /*data_type=*/pixel_format.data_type, - /*endianness=*/pixel_format.endianness, - /*align=*/pixel_format.align}; - - JXL_ASSIGN_OR_RETURN(PackedFrame packed_frame, - PackedFrame::Create(frame.oriented_xsize(), - frame.oriented_ysize(), format)); - JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(pixel_format.data_type)); - const size_t bits_per_sample = - float_out ? packed_frame.color.BitsPerChannel(pixel_format.data_type) - : ppf->info.bits_per_sample; - packed_frame.name = frame.name; - packed_frame.frame_info.name_length = frame.name.size(); - // Color transform - JXL_ASSIGN_OR_RETURN(ImageBundle ib, frame.Copy()); - const ImageBundle* to_color_transform = &ib; - ImageMetadata metadata = io.metadata.m; - ImageBundle store(memory_manager, &metadata); - const ImageBundle* transformed; - // TODO(firsching): handle the transform here. - JXL_RETURN_IF_ERROR(TransformIfNeeded(*to_color_transform, c_desired, - *JxlGetDefaultCms(), pool, &store, - &transformed)); - - JXL_RETURN_IF_ERROR(ConvertToExternal( - *transformed, bits_per_sample, float_out, format.num_channels, - format.endianness, - /* stride_out=*/packed_frame.color.stride, pool, - packed_frame.color.pixels(), packed_frame.color.pixels_size, - /*out_callback=*/{}, frame.metadata()->GetOrientation())); - - // TODO(firsching): Convert the extra channels, beside one potential alpha - // channel. FIXME! - JXL_ENSURE(frame.extra_channels().size() <= (has_alpha ? 1 : 0)); - ppf->frames.push_back(std::move(packed_frame)); - } - - return true; -} -} // namespace extras -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.h b/third_party/jpeg-xl/lib/extras/packed_image_convert.h deleted file mode 100644 index 20ed330b84719..0000000000000 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_PACKED_IMAGE_CONVERT_H_ -#define LIB_EXTRAS_PACKED_IMAGE_CONVERT_H_ - -// Helper functions to convert from the external image types to the internal -// CodecInOut to help transitioning to the external types. - -#include - -#include "lib/extras/packed_image.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/codec_in_out.h" - -namespace jxl { -namespace extras { - -// Converts an external PackedPixelFile to the internal CodecInOut for use with -// internal functions directly. -Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, - ThreadPool* pool, CodecInOut* io); - -// Converts an internal CodecInOut for use with internal function to an external -// PackedPixelFile. -Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, - const JxlPixelFormat& pixel_format, - const ColorEncoding& c_desired, - ThreadPool* pool, - PackedPixelFile* ppf); - -StatusOr ConvertImage3FToPackedPixelFile( - const Image3F& image, const ColorEncoding& c_enc, JxlPixelFormat format, - ThreadPool* pool); -} // namespace extras -} // namespace jxl - -#endif // LIB_EXTRAS_PACKED_IMAGE_CONVERT_H_ diff --git a/third_party/jpeg-xl/lib/extras/size_constraints.h b/third_party/jpeg-xl/lib/extras/size_constraints.h deleted file mode 100644 index cf06f8cb22814..0000000000000 --- a/third_party/jpeg-xl/lib/extras/size_constraints.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_SIZE_CONSTRAINTS_H_ -#define LIB_JXL_SIZE_CONSTRAINTS_H_ - -#include -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { - -struct SizeConstraints { - // Upper limit on pixel dimensions/area, enforced by VerifyDimensions - // (called from decoders). Fuzzers set smaller values to limit memory use. - uint32_t dec_max_xsize = 0xFFFFFFFFu; - uint32_t dec_max_ysize = 0xFFFFFFFFu; - uint64_t dec_max_pixels = 0xFFFFFFFFu; // Might be up to ~0ull -}; - -template ::value>::type> -Status VerifyDimensions(const SizeConstraints* constraints, T xs, T ys) { - if (!constraints) return true; - - if (xs == 0 || ys == 0) return JXL_FAILURE("Empty image."); - if (xs > constraints->dec_max_xsize) return JXL_FAILURE("Image too wide."); - if (ys > constraints->dec_max_ysize) return JXL_FAILURE("Image too tall."); - - const uint64_t num_pixels = static_cast(xs) * ys; - if (num_pixels > constraints->dec_max_pixels) { - return JXL_FAILURE("Image too big."); - } - - return true; -} - -} // namespace jxl - -#endif // LIB_JXL_SIZE_CONSTRAINTS_H_ diff --git a/third_party/jpeg-xl/lib/extras/time.cc b/third_party/jpeg-xl/lib/extras/time.cc deleted file mode 100644 index 344994f78da7a..0000000000000 --- a/third_party/jpeg-xl/lib/extras/time.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/time.h" - -#include -#include - -#include - -#include "lib/jxl/base/os_macros.h" // for JXL_OS_* - -#if JXL_OS_WIN -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX -#include -#endif // JXL_OS_WIN - -#if JXL_OS_MAC -#include -#include -#endif // JXL_OS_MAC - -#if JXL_OS_HAIKU -#include -#endif // JXL_OS_HAIKU - -namespace jxl { - -double Now() { -#if JXL_OS_WIN - LARGE_INTEGER counter; - (void)QueryPerformanceCounter(&counter); - LARGE_INTEGER freq; - (void)QueryPerformanceFrequency(&freq); - return double(counter.QuadPart) / freq.QuadPart; -#elif JXL_OS_MAC - const auto t = mach_absolute_time(); - // On OSX/iOS platform the elapsed time is cpu time unit - // We have to query the time base information to convert it back - // See https://developer.apple.com/library/mac/qa/qa1398/_index.html - static mach_timebase_info_data_t timebase; - if (timebase.denom == 0) { - (void)mach_timebase_info(&timebase); - } - return double(t) * timebase.numer / timebase.denom * 1E-9; // notypo -#elif JXL_OS_HAIKU - return double(system_time_nsecs()) * 1E-9; -#else - timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - return t.tv_sec + t.tv_nsec * 1E-9; -#endif -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/time.h b/third_party/jpeg-xl/lib/extras/time.h deleted file mode 100644 index c71414b877b84..0000000000000 --- a/third_party/jpeg-xl/lib/extras/time.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_TIME_H_ -#define LIB_EXTRAS_TIME_H_ - -// OS-specific function for timing. - -namespace jxl { - -// Returns current time [seconds] from a monotonic clock with unspecified -// starting point - only suitable for computing elapsed time. -double Now(); - -} // namespace jxl - -#endif // LIB_EXTRAS_TIME_H_ diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping.cc b/third_party/jpeg-xl/lib/extras/tone_mapping.cc deleted file mode 100644 index 9a700c48a32f0..0000000000000 --- a/third_party/jpeg-xl/lib/extras/tone_mapping.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/tone_mapping.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/extras/tone_mapping.cc" -#include - -#include -#include - -#include "lib/jxl/cms/tone_mapping-inl.h" -#include "lib/jxl/image_bundle.h" - -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { - -static constexpr Vector3 rec2020_luminances{0.2627f, 0.6780f, 0.0593f}; - -Status ToneMapFrame(const std::pair display_nits, - ImageBundle* const ib, ThreadPool* const pool) { - // Perform tone mapping as described in Report ITU-R BT.2390-8, section 5.4 - // (pp. 23-25). - // https://www.itu.int/pub/R-REP-BT.2390-8-2020 - - HWY_FULL(float) df; - using V = decltype(Zero(df)); - - ColorEncoding linear_rec2020; - linear_rec2020.SetColorSpace(ColorSpace::kRGB); - JXL_RETURN_IF_ERROR(linear_rec2020.SetPrimariesType(Primaries::k2100)); - JXL_RETURN_IF_ERROR(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); - linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); - JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC()); - JXL_RETURN_IF_ERROR( - ib->TransformTo(linear_rec2020, *JxlGetDefaultCms(), pool)); - - Rec2408ToneMapper tone_mapper( - {ib->metadata()->tone_mapping.min_nits, - ib->metadata()->IntensityTarget()}, - display_nits, rec2020_luminances); - - const auto process_row = [&](const uint32_t y, - size_t /* thread */) -> Status { - float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y); - float* const JXL_RESTRICT row_g = ib->color()->PlaneRow(1, y); - float* const JXL_RESTRICT row_b = ib->color()->PlaneRow(2, y); - for (size_t x = 0; x < ib->xsize(); x += Lanes(df)) { - V red = Load(df, row_r + x); - V green = Load(df, row_g + x); - V blue = Load(df, row_b + x); - tone_mapper.ToneMap(&red, &green, &blue); - Store(red, df, row_r + x); - Store(green, df, row_g + x); - Store(blue, df, row_b + x); - } - return true; - }; - JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, ib->ysize(), ThreadPool::NoInit, - process_row, "ToneMap")); - return true; -} - -Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation, - ThreadPool* const pool) { - HWY_FULL(float) df; - using V = decltype(Zero(df)); - - ColorEncoding linear_rec2020; - linear_rec2020.SetColorSpace(ColorSpace::kRGB); - JXL_RETURN_IF_ERROR(linear_rec2020.SetPrimariesType(Primaries::k2100)); - JXL_RETURN_IF_ERROR(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); - linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); - JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC()); - JXL_RETURN_IF_ERROR( - ib->TransformTo(linear_rec2020, *JxlGetDefaultCms(), pool)); - - const auto process_row = [&](const uint32_t y, size_t /* thread*/) -> Status { - float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y); - float* const JXL_RESTRICT row_g = ib->color()->PlaneRow(1, y); - float* const JXL_RESTRICT row_b = ib->color()->PlaneRow(2, y); - for (size_t x = 0; x < ib->xsize(); x += Lanes(df)) { - V red = Load(df, row_r + x); - V green = Load(df, row_g + x); - V blue = Load(df, row_b + x); - GamutMap(&red, &green, &blue, rec2020_luminances, preserve_saturation); - Store(red, df, row_r + x); - Store(green, df, row_g + x); - Store(blue, df, row_b + x); - } - return true; - }; - - JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, ib->ysize(), ThreadPool::NoInit, - process_row, "GamutMap")); - - return true; -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jxl { - -namespace { -HWY_EXPORT(ToneMapFrame); -HWY_EXPORT(GamutMapFrame); -} // namespace - -Status ToneMapTo(const std::pair display_nits, - CodecInOut* const io, ThreadPool* const pool) { - const auto tone_map_frame = HWY_DYNAMIC_DISPATCH(ToneMapFrame); - for (ImageBundle& ib : io->frames) { - JXL_RETURN_IF_ERROR(tone_map_frame(display_nits, &ib, pool)); - } - io->metadata.m.SetIntensityTarget(display_nits.second); - return true; -} - -Status GamutMap(CodecInOut* const io, float preserve_saturation, - ThreadPool* const pool) { - const auto gamut_map_frame = HWY_DYNAMIC_DISPATCH(GamutMapFrame); - for (ImageBundle& ib : io->frames) { - JXL_RETURN_IF_ERROR(gamut_map_frame(&ib, preserve_saturation, pool)); - } - return true; -} - -} // namespace jxl -#endif diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping.h b/third_party/jpeg-xl/lib/extras/tone_mapping.h deleted file mode 100644 index 1f474101eb443..0000000000000 --- a/third_party/jpeg-xl/lib/extras/tone_mapping.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_EXTRAS_TONE_MAPPING_H_ -#define LIB_EXTRAS_TONE_MAPPING_H_ - -#include "lib/jxl/codec_in_out.h" - -namespace jxl { - -// Important: after calling this, the result will contain many out-of-gamut -// colors. It is very strongly recommended to call GamutMap afterwards to -// rectify this. -Status ToneMapTo(std::pair display_nits, CodecInOut* io, - ThreadPool* pool = nullptr); - -// `preserve_saturation` indicates to what extent to favor saturation over -// luminance when mapping out-of-gamut colors to Rec. 2020. 0 preserves -// luminance at the complete expense of saturation, while 1 gives the most -// saturated color with the same hue that Rec. 2020 can represent even if it -// means lowering the luminance. Values in between correspond to linear mixtures -// of those two extremes. -Status GamutMap(CodecInOut* io, float preserve_saturation, - ThreadPool* pool = nullptr); - -} // namespace jxl - -#endif // LIB_EXTRAS_TONE_MAPPING_H_ diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc deleted file mode 100644 index 30b4e6e7e0930..0000000000000 --- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include "benchmark/benchmark.h" -#include "lib/extras/tone_mapping.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/image.h" -#include "tools/no_memory_manager.h" - -namespace jxl { - -#define QUIT(M) \ - state.SkipWithError(M); \ - return; - -#define BM_CHECK(C) \ - if (!(C)) { \ - QUIT(#C) \ - } - -static void BM_ToneMapping(benchmark::State& state) { - JxlMemoryManager* memory_manager = jpegxl::tools::NoMemoryManager(); - JXL_ASSIGN_OR_QUIT(Image3F color, Image3F::Create(memory_manager, 2268, 1512), - "Failed to allocate color plane"); - FillImage(0.5f, &color); - - // Use linear Rec. 2020 so that `ToneMapTo` doesn't have to convert to it and - // we mainly measure the tone mapping itself. - ColorEncoding linear_rec2020; - linear_rec2020.SetColorSpace(ColorSpace::kRGB); - BM_CHECK(linear_rec2020.SetPrimariesType(Primaries::k2100)); - BM_CHECK(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); - linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); - BM_CHECK(linear_rec2020.CreateICC()); - - for (auto _ : state) { - (void)_; - state.PauseTiming(); - CodecInOut tone_mapping_input{memory_manager}; - JXL_ASSIGN_OR_QUIT( - Image3F color2, - Image3F::Create(memory_manager, color.xsize(), color.ysize()), - "Failed to allocate color plane"); - BM_CHECK(CopyImageTo(color, &color2)); - BM_CHECK( - tone_mapping_input.SetFromImage(std::move(color2), linear_rec2020)); - tone_mapping_input.metadata.m.SetIntensityTarget(255); - state.ResumeTiming(); - - BM_CHECK(ToneMapTo({0.1, 100}, &tone_mapping_input)); - } - - state.SetItemsProcessed(state.iterations() * color.xsize() * color.ysize()); -} -BENCHMARK(BM_ToneMapping); - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/gbench_main.cc b/third_party/jpeg-xl/lib/gbench_main.cc deleted file mode 100644 index 1cc17720177d8..0000000000000 --- a/third_party/jpeg-xl/lib/gbench_main.cc +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "benchmark/benchmark.h" - -BENCHMARK_MAIN(); diff --git a/third_party/jpeg-xl/lib/include/jxl/cms.h b/third_party/jpeg-xl/lib/include/jxl/cms.h deleted file mode 100644 index 6616a2681c58f..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/cms.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef JXL_CMS_H_ -#define JXL_CMS_H_ - -// ICC profiles and color space conversions. - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -JXL_CMS_EXPORT const JxlCmsInterface* JxlGetDefaultCms(); - -#ifdef __cplusplus -} -#endif - -#endif // JXL_CMS_H_ diff --git a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h b/third_party/jpeg-xl/lib/include/jxl/cms_interface.h deleted file mode 100644 index 137fb42d8ca9e..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h +++ /dev/null @@ -1,255 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_color - * @{ - * @file cms_interface.h - * @brief Interface to allow the injection of different color management systems - * (CMSes, also called color management modules, or CMMs) in JPEG XL. - * - * A CMS is needed by the JPEG XL encoder and decoder to perform colorspace - * conversions. This defines an interface that can be implemented for different - * CMSes and then passed to the library. - */ - -#ifndef JXL_CMS_INTERFACE_H_ -#define JXL_CMS_INTERFACE_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Parses an ICC profile and populates @p c and @p cmyk with the data. - * - * @param user_data @ref JxlCmsInterface::set_fields_data passed as-is. - * @param icc_data the ICC data to parse. - * @param icc_size how many bytes of icc_data are valid. - * @param c a @ref JxlColorEncoding to populate if applicable. - * @param cmyk a boolean to set to whether the colorspace is a CMYK colorspace. - * @return Whether the relevant fields in @p c were successfully populated. - */ -typedef JXL_BOOL (*jpegxl_cms_set_fields_from_icc_func)(void* user_data, - const uint8_t* icc_data, - size_t icc_size, - JxlColorEncoding* c, - JXL_BOOL* cmyk); - -/** Represents an input or output colorspace to a color transform, as a - * serialized ICC profile. */ -typedef struct { - /** The serialized ICC profile. This is guaranteed to be present and valid. */ - struct { - const uint8_t* data; - size_t size; - } icc; - - /** Structured representation of the colorspace, if applicable. If all fields - * are different from their "unknown" value, then this is equivalent to the - * ICC representation of the colorspace. If some are "unknown", those that are - * not are still valid and can still be used on their own if they are useful. - */ - JxlColorEncoding color_encoding; - - /** Number of components per pixel. This can be deduced from the other - * representations of the colorspace but is provided for convenience and - * validation. */ - size_t num_channels; -} JxlColorProfile; - -/** Allocates and returns the data needed for @p num_threads parallel transforms - * from the @p input colorspace to @p output, with up to @p pixels_per_thread - * pixels to transform per call to @ref JxlCmsInterface::run. @p init_data comes - * directly from the @ref JxlCmsInterface instance. Since @c run only receives - * the data returned by @c init, a reference to @p init_data should be kept - * there if access to it is desired in @c run. Likewise for @ref - * JxlCmsInterface::destroy. - * - * The ICC data in @p input and @p output is guaranteed to outlive the @c init / - * @c run / @c destroy cycle. - * - * @param init_data @ref JxlCmsInterface::init_data passed as-is. - * @param num_threads the maximum number of threads from which - * @ref JxlCmsInterface::run will be called. - * @param pixels_per_thread the maximum number of pixels that each call to - * @ref JxlCmsInterface::run will have to transform. - * @param input_profile the input colorspace for the transform. - * @param output_profile the colorspace to which @ref JxlCmsInterface::run - * should convert the input data. - * @param intensity_target for colorspaces where luminance is relative - * (essentially: not PQ), indicates the luminance at which (1, 1, 1) will - * be displayed. This is useful for conversions between PQ and a relative - * luminance colorspace, in either direction: @p intensity_target cd/m² - * in PQ should map to and from (1, 1, 1) in the relative one.\n - * It is also used for conversions to and from HLG, as it is - * scene-referred while other colorspaces are assumed to be - * display-referred. That is, conversions from HLG should apply the OOTF - * for a peak display luminance of @p intensity_target, and conversions - * to HLG should undo it. The OOTF is a gamma function applied to the - * luminance channel (https://www.itu.int/rec/R-REC-BT.2100-2-201807-I - * page 7), with the gamma value computed as - * 1.2 * 1.111^log2(intensity_target / 1000) (footnote 2 page 8 - * of the same document). - * @return The data needed for the transform, or @c NULL in case of failure. - * This will be passed to the other functions as @c user_data. - */ -typedef void* (*jpegxl_cms_init_func)(void* init_data, size_t num_threads, - size_t pixels_per_thread, - const JxlColorProfile* input_profile, - const JxlColorProfile* output_profile, - float intensity_target); - -/** Returns a buffer that can be used by callers of the interface to store the - * input of the conversion or read its result, if they pass it as the input or - * output of the @c run function. - * @param user_data the data returned by @c init. - * @param thread the index of the thread for which to return a buffer. - * @return A buffer that can be used by the caller for passing to @c run. - */ -typedef float* (*jpegxl_cms_get_buffer_func)(void* user_data, size_t thread); - -/** Executes one transform and returns true on success or false on error. It - * must be possible to call this from different threads with different values - * for @p thread, all between 0 (inclusive) and the value of @p num_threads - * passed to @c init (exclusive). It is allowed to implement this by locking - * such that the transforms are essentially performed sequentially, if such a - * performance profile is acceptable. @p user_data is the data returned by - * @c init. - * The buffers each contain @p num_pixels × @c num_channels interleaved floating - * point (0..1) samples where @c num_channels is the number of color channels of - * their respective color profiles. It is guaranteed that the only case in which - * they might overlap is if the output has fewer channels than the input, in - * which case the pointers may be identical. - * For CMYK data, 0 represents the maximum amount of ink while 1 represents no - * ink. - * @param user_data the data returned by @c init. - * @param thread the index of the thread from which the function is being - * called. - * @param input_buffer the buffer containing the pixel data to be transformed. - * @param output_buffer the buffer receiving the transformed pixel data. - * @param num_pixels the number of pixels to transform from @p input to - * @p output. - * @return ::JXL_TRUE on success, ::JXL_FALSE on failure. - */ -typedef JXL_BOOL (*jpegxl_cms_run_func)(void* user_data, size_t thread, - const float* input_buffer, - float* output_buffer, - size_t num_pixels); - -/** Performs the necessary clean-up and frees the memory allocated for user - * data. - */ -typedef void (*jpegxl_cms_destroy_func)(void*); - -/** - * Interface for performing colorspace transforms. The @c init function can be - * called several times to instantiate several transforms, including before - * other transforms have been destroyed. - * - * The call sequence for a given colorspace transform could look like the - * following: - * @dot - * digraph calls { - * newrank = true - * node [shape = box, fontname = monospace] - * init [label = "user_data <- init(\l\ - * init_data = data,\l\ - * num_threads = 3,\l\ - * pixels_per_thread = 20,\l\ - * input = (sRGB, 3 channels),\l\ - * output = (Display-P3, 3 channels),\l\ - * intensity_target = 255\l\ - * )\l"] - * subgraph cluster_0 { - * color = lightgrey - * label = "thread 1" - * labeljust = "c" - * run_1_1 [label = "run(\l\ - * user_data,\l\ - * thread = 1,\l\ - * input = in[0],\l\ - * output = out[0],\l\ - * num_pixels = 20\l\ - * )\l"] - * run_1_2 [label = "run(\l\ - * user_data,\l\ - * thread = 1,\l\ - * input = in[3],\l\ - * output = out[3],\l\ - * num_pixels = 20\l\ - * )\l"] - * } - * subgraph cluster_1 { - * color = lightgrey - * label = "thread 2" - * labeljust = "l" - * run_2_1 [label = "run(\l\ - * user_data,\l\ - * thread = 2,\l\ - * input = in[1],\l\ - * output = out[1],\l\ - * num_pixels = 20\l\ - * )\l"] - * run_2_2 [label = "run(\l\ - * user_data,\l\ - * thread = 2,\l\ - * input = in[4],\l\ - * output = out[4],\l\ - * num_pixels = 13\l\ - * )\l"] - * } - * subgraph cluster_3 { - * color = lightgrey - * label = "thread 3" - * labeljust = "c" - * run_3_1 [label = "run(\l\ - * user_data,\l\ - * thread = 3,\l\ - * input = in[2],\l\ - * output = out[2],\l\ - * num_pixels = 20\l\ - * )\l"] - * } - * init -> {run_1_1; run_2_1; run_3_1; rank = same} - * run_1_1 -> run_1_2 - * run_2_1 -> run_2_2 - * {run_1_2; run_2_2, run_3_1} -> "destroy(user_data)" - * } - * @enddot - */ -typedef struct { - /** CMS-specific data that will be passed to @ref set_fields_from_icc. */ - void* set_fields_data; - /** Populates a @ref JxlColorEncoding from an ICC profile. */ - jpegxl_cms_set_fields_from_icc_func set_fields_from_icc; - - /** CMS-specific data that will be passed to @ref init. */ - void* init_data; - /** Prepares a colorspace transform as described in the documentation of @ref - * jpegxl_cms_init_func. */ - jpegxl_cms_init_func init; - /** Returns a buffer that can be used as input to @c run. */ - jpegxl_cms_get_buffer_func get_src_buf; - /** Returns a buffer that can be used as output from @c run. */ - jpegxl_cms_get_buffer_func get_dst_buf; - /** Executes the transform on a batch of pixels, per @ref jpegxl_cms_run_func. - */ - jpegxl_cms_run_func run; - /** Cleans up the transform. */ - jpegxl_cms_destroy_func destroy; -} JxlCmsInterface; - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_CMS_INTERFACE_H_ */ - -/** @} */ diff --git a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h b/third_party/jpeg-xl/lib/include/jxl/codestream_header.h deleted file mode 100644 index 513c92f1dda2d..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h +++ /dev/null @@ -1,433 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_metadata - * @{ - * @file codestream_header.h - * @brief Definitions of structs and enums for the metadata from the JPEG XL - * codestream headers (signature, metadata, preview dimensions, ...), excluding - * color encoding which is in color_encoding.h. - */ - -#ifndef JXL_CODESTREAM_HEADER_H_ -#define JXL_CODESTREAM_HEADER_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Image orientation metadata. - * Values 1..8 match the EXIF definitions. - * The name indicates the operation to perform to transform from the encoded - * image to the display image. - */ -typedef enum { - JXL_ORIENT_IDENTITY = 1, - JXL_ORIENT_FLIP_HORIZONTAL = 2, - JXL_ORIENT_ROTATE_180 = 3, - JXL_ORIENT_FLIP_VERTICAL = 4, - JXL_ORIENT_TRANSPOSE = 5, - JXL_ORIENT_ROTATE_90_CW = 6, - JXL_ORIENT_ANTI_TRANSPOSE = 7, - JXL_ORIENT_ROTATE_90_CCW = 8, -} JxlOrientation; - -/** Given type of an extra channel. - */ -typedef enum { - JXL_CHANNEL_ALPHA, - JXL_CHANNEL_DEPTH, - JXL_CHANNEL_SPOT_COLOR, - JXL_CHANNEL_SELECTION_MASK, - JXL_CHANNEL_BLACK, - JXL_CHANNEL_CFA, - JXL_CHANNEL_THERMAL, - JXL_CHANNEL_RESERVED0, - JXL_CHANNEL_RESERVED1, - JXL_CHANNEL_RESERVED2, - JXL_CHANNEL_RESERVED3, - JXL_CHANNEL_RESERVED4, - JXL_CHANNEL_RESERVED5, - JXL_CHANNEL_RESERVED6, - JXL_CHANNEL_RESERVED7, - JXL_CHANNEL_UNKNOWN, - JXL_CHANNEL_OPTIONAL -} JxlExtraChannelType; - -/** The codestream preview header */ -typedef struct { - /** Preview width in pixels */ - uint32_t xsize; - - /** Preview height in pixels */ - uint32_t ysize; -} JxlPreviewHeader; - -/** The codestream animation header, optionally present in the beginning of - * the codestream, and if it is it applies to all animation frames, unlike @ref - * JxlFrameHeader which applies to an individual frame. - */ -typedef struct { - /** Numerator of ticks per second of a single animation frame time unit */ - uint32_t tps_numerator; - - /** Denominator of ticks per second of a single animation frame time unit */ - uint32_t tps_denominator; - - /** Amount of animation loops, or 0 to repeat infinitely */ - uint32_t num_loops; - - /** Whether animation time codes are present at animation frames in the - * codestream */ - JXL_BOOL have_timecodes; -} JxlAnimationHeader; - -/** Basic image information. This information is available from the file - * signature and first part of the codestream header. - */ -typedef struct { - /* TODO(lode): need additional fields for (transcoded) JPEG? For reusable - * fields orientation must be read from Exif APP1. For has_icc_profile: must - * look up where ICC profile is guaranteed to be in a JPEG file to be able to - * indicate this. */ - - /* TODO(lode): make struct packed, and/or make this opaque struct with getter - * functions (still separate struct from opaque decoder) */ - - /** Whether the codestream is embedded in the container format. If true, - * metadata information and extensions may be available in addition to the - * codestream. - */ - JXL_BOOL have_container; - - /** Width of the image in pixels, before applying orientation. - */ - uint32_t xsize; - - /** Height of the image in pixels, before applying orientation. - */ - uint32_t ysize; - - /** Original image color channel bit depth. - */ - uint32_t bits_per_sample; - - /** Original image color channel floating point exponent bits, or 0 if they - * are unsigned integer. For example, if the original data is half-precision - * (binary16) floating point, bits_per_sample is 16 and - * exponent_bits_per_sample is 5, and so on for other floating point - * precisions. - */ - uint32_t exponent_bits_per_sample; - - /** Upper bound on the intensity level present in the image in nits. For - * unsigned integer pixel encodings, this is the brightness of the largest - * representable value. The image does not necessarily contain a pixel - * actually this bright. An encoder is allowed to set 255 for SDR images - * without computing a histogram. - * Leaving this set to its default of 0 lets libjxl choose a sensible default - * value based on the color encoding. - */ - float intensity_target; - - /** Lower bound on the intensity level present in the image. This may be - * loose, i.e. lower than the actual darkest pixel. When tone mapping, a - * decoder will map [min_nits, intensity_target] to the display range. - */ - float min_nits; - - /** See the description of @see linear_below. - */ - JXL_BOOL relative_to_max_display; - - /** The tone mapping will leave unchanged (linear mapping) any pixels whose - * brightness is strictly below this. The interpretation depends on - * relative_to_max_display. If true, this is a ratio [0, 1] of the maximum - * display brightness [nits], otherwise an absolute brightness [nits]. - */ - float linear_below; - - /** Whether the data in the codestream is encoded in the original color - * profile that is attached to the codestream metadata header, or is - * encoded in an internally supported absolute color space (which the decoder - * can always convert to linear or non-linear sRGB or to XYB). If the original - * profile is used, the decoder outputs pixel data in the color space matching - * that profile, but doesn't convert it to any other color space. If the - * original profile is not used, the decoder only outputs the data as sRGB - * (linear if outputting to floating point, nonlinear with standard sRGB - * transfer function if outputting to unsigned integers) but will not convert - * it to to the original color profile. The decoder also does not convert to - * the target display color profile. To convert the pixel data produced by - * the decoder to the original color profile, one of the JxlDecoderGetColor* - * functions needs to be called with - * ::JXL_COLOR_PROFILE_TARGET_DATA to get the color profile of the decoder - * output, and then an external CMS can be used for conversion. Note that for - * lossy compression, this should be set to false for most use cases, and if - * needed, the image should be converted to the original color profile after - * decoding, as described above. - */ - JXL_BOOL uses_original_profile; - - /** Indicates a preview image exists near the beginning of the codestream. - * The preview itself or its dimensions are not included in the basic info. - */ - JXL_BOOL have_preview; - - /** Indicates animation frames exist in the codestream. The animation - * information is not included in the basic info. - */ - JXL_BOOL have_animation; - - /** Image orientation, value 1-8 matching the values used by JEITA CP-3451C - * (Exif version 2.3). - */ - JxlOrientation orientation; - - /** Number of color channels encoded in the image, this is either 1 for - * grayscale data, or 3 for colored data. This count does not include - * the alpha channel or other extra channels. To check presence of an alpha - * channel, such as in the case of RGBA color, check alpha_bits != 0. - * If and only if this is 1, the @ref JxlColorSpace in the @ref - * JxlColorEncoding is - * ::JXL_COLOR_SPACE_GRAY. - */ - uint32_t num_color_channels; - - /** Number of additional image channels. This includes the main alpha channel, - * but can also include additional channels such as depth, additional alpha - * channels, spot colors, and so on. Information about the extra channels - * can be queried with @ref JxlDecoderGetExtraChannelInfo. The main alpha - * channel, if it exists, also has its information available in the - * alpha_bits, alpha_exponent_bits and alpha_premultiplied fields in this @ref - * JxlBasicInfo. - */ - uint32_t num_extra_channels; - - /** Bit depth of the encoded alpha channel, or 0 if there is no alpha channel. - * If present, matches the alpha_bits value of the JxlExtraChannelInfo - * associated with this alpha channel. - */ - uint32_t alpha_bits; - - /** Alpha channel floating point exponent bits, or 0 if they are unsigned. If - * present, matches the alpha_bits value of the JxlExtraChannelInfo associated - * with this alpha channel. integer. - */ - uint32_t alpha_exponent_bits; - - /** Whether the alpha channel is premultiplied. Only used if there is a main - * alpha channel. Matches the alpha_premultiplied value of the - * JxlExtraChannelInfo associated with this alpha channel. - */ - JXL_BOOL alpha_premultiplied; - - /** Dimensions of encoded preview image, only used if have_preview is - * JXL_TRUE. - */ - JxlPreviewHeader preview; - - /** Animation header with global animation properties for all frames, only - * used if have_animation is JXL_TRUE. - */ - JxlAnimationHeader animation; - - /** Intrinsic width of the image. - * The intrinsic size can be different from the actual size in pixels - * (as given by xsize and ysize) and it denotes the recommended dimensions - * for displaying the image, i.e. applications are advised to resample the - * decoded image to the intrinsic dimensions. - */ - uint32_t intrinsic_xsize; - - /** Intrinsic height of the image. - * The intrinsic size can be different from the actual size in pixels - * (as given by xsize and ysize) and it denotes the recommended dimensions - * for displaying the image, i.e. applications are advised to resample the - * decoded image to the intrinsic dimensions. - */ - uint32_t intrinsic_ysize; - - /** Padding for forwards-compatibility, in case more fields are exposed - * in a future version of the library. - */ - uint8_t padding[100]; -} JxlBasicInfo; - -/** Information for a single extra channel. - */ -typedef struct { - /** Given type of an extra channel. - */ - JxlExtraChannelType type; - - /** Total bits per sample for this channel. - */ - uint32_t bits_per_sample; - - /** Floating point exponent bits per channel, or 0 if they are unsigned - * integer. - */ - uint32_t exponent_bits_per_sample; - - /** The exponent the channel is downsampled by on each axis. - * TODO(lode): expand this comment to match the JPEG XL specification, - * specify how to upscale, how to round the size computation, and to which - * extra channels this field applies. - */ - uint32_t dim_shift; - - /** Length of the extra channel name in bytes, or 0 if no name. - * Excludes null termination character. - */ - uint32_t name_length; - - /** Whether alpha channel uses premultiplied alpha. Only applicable if - * type is JXL_CHANNEL_ALPHA. - */ - JXL_BOOL alpha_premultiplied; - - /** Spot color of the current spot channel in linear RGBA. Only applicable if - * type is JXL_CHANNEL_SPOT_COLOR. - */ - float spot_color[4]; - - /** Only applicable if type is JXL_CHANNEL_CFA. - * TODO(lode): add comment about the meaning of this field. - */ - uint32_t cfa_channel; -} JxlExtraChannelInfo; - -/* TODO(lode): add API to get the codestream header extensions. */ -/** Extensions in the codestream header. */ -typedef struct { - /** Extension bits. */ - uint64_t extensions; -} JxlHeaderExtensions; - -/** Frame blend modes. - * When decoding, if coalescing is enabled (default), this can be ignored. - */ -typedef enum { - JXL_BLEND_REPLACE = 0, - JXL_BLEND_ADD = 1, - JXL_BLEND_BLEND = 2, - JXL_BLEND_MULADD = 3, - JXL_BLEND_MUL = 4, -} JxlBlendMode; - -/** The information about blending the color channels or a single extra channel. - * When decoding, if coalescing is enabled (default), this can be ignored and - * the blend mode is considered to be JXL_BLEND_REPLACE. - * When encoding, these settings apply to the pixel data given to the encoder. - */ -typedef struct { - /** Blend mode. - */ - JxlBlendMode blendmode; - /** Reference frame ID to use as the 'bottom' layer (0-3). - */ - uint32_t source; - /** Which extra channel to use as the 'alpha' channel for blend modes - * JXL_BLEND_BLEND and JXL_BLEND_MULADD. - */ - uint32_t alpha; - /** Clamp values to [0,1] for the purpose of blending. - */ - JXL_BOOL clamp; -} JxlBlendInfo; - -/** The information about layers. - * When decoding, if coalescing is enabled (default), this can be ignored. - * When encoding, these settings apply to the pixel data given to the encoder, - * the encoder could choose an internal representation that differs. - */ -typedef struct { - /** Whether cropping is applied for this frame. When decoding, if false, - * crop_x0 and crop_y0 are set to zero, and xsize and ysize to the main - * image dimensions. When encoding and this is false, those fields are - * ignored. When decoding, if coalescing is enabled (default), this is always - * false, regardless of the internal encoding in the JPEG XL codestream. - */ - JXL_BOOL have_crop; - - /** Horizontal offset of the frame (can be negative). - */ - int32_t crop_x0; - - /** Vertical offset of the frame (can be negative). - */ - int32_t crop_y0; - - /** Width of the frame (number of columns). - */ - uint32_t xsize; - - /** Height of the frame (number of rows). - */ - uint32_t ysize; - - /** The blending info for the color channels. Blending info for extra channels - * has to be retrieved separately using JxlDecoderGetExtraChannelBlendInfo. - */ - JxlBlendInfo blend_info; - - /** After blending, save the frame as reference frame with this ID (0-3). - * Special case: if the frame duration is nonzero, ID 0 means "will not be - * referenced in the future". This value is not used for the last frame. - * When encoding, ID 3 is reserved to frames that are generated internally by - * the encoder, and should not be used by applications. - */ - uint32_t save_as_reference; -} JxlLayerInfo; - -/** The header of one displayed frame or non-coalesced layer. */ -typedef struct { - /** How long to wait after rendering in ticks. The duration in seconds of a - * tick is given by tps_numerator and tps_denominator in @ref - * JxlAnimationHeader. - */ - uint32_t duration; - - /** SMPTE timecode of the current frame in form 0xHHMMSSFF, or 0. The bits are - * interpreted from most-significant to least-significant as hour, minute, - * second, and frame. If timecode is nonzero, it is strictly larger than that - * of a previous frame with nonzero duration. These values are only available - * if have_timecodes in @ref JxlAnimationHeader is ::JXL_TRUE. - * This value is only used if have_timecodes in @ref JxlAnimationHeader is - * ::JXL_TRUE. - */ - uint32_t timecode; - - /** Length of the frame name in bytes, or 0 if no name. - * Excludes null termination character. This value is set by the decoder. - * For the encoder, this value is ignored and @ref JxlEncoderSetFrameName is - * used instead to set the name and the length. - */ - uint32_t name_length; - - /** Indicates this is the last animation frame. This value is set by the - * decoder to indicate no further frames follow. For the encoder, it is not - * required to set this value and it is ignored, @ref JxlEncoderCloseFrames is - * used to indicate the last frame to the encoder instead. - */ - JXL_BOOL is_last; - - /** Information about the layer in case of no coalescing. - */ - JxlLayerInfo layer_info; -} JxlFrameHeader; - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_CODESTREAM_HEADER_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h deleted file mode 100644 index 14dcdeb0a2123..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h +++ /dev/null @@ -1,161 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_color - * @{ - * @file color_encoding.h - * @brief Color Encoding definitions used by JPEG XL. - * All CIE units are for the standard 1931 2 degree observer. - */ - -#ifndef JXL_COLOR_ENCODING_H_ -#define JXL_COLOR_ENCODING_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** Color space of the image data. */ -typedef enum { - /** Tristimulus RGB */ - JXL_COLOR_SPACE_RGB, - /** Luminance based, the primaries in @ref JxlColorEncoding must be ignored. - * This value implies that num_color_channels in @ref JxlBasicInfo is 1, any - * other value implies num_color_channels is 3. */ - JXL_COLOR_SPACE_GRAY, - /** XYB (opsin) color space */ - JXL_COLOR_SPACE_XYB, - /** None of the other table entries describe the color space appropriately */ - JXL_COLOR_SPACE_UNKNOWN, -} JxlColorSpace; - -/** Built-in white points for color encoding. When decoding, the numerical xy - * white point value can be read from the @ref JxlColorEncoding white_point - * field regardless of the enum value. When encoding, enum values except - * ::JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values - * match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however - * the white point and RGB primaries are separate enums here. - */ -typedef enum { - /** CIE Standard Illuminant D65: 0.3127, 0.3290 */ - JXL_WHITE_POINT_D65 = 1, - /** White point must be read from the @ref JxlColorEncoding white_point field, - * or as ICC profile. This enum value is not an exact match of the - * corresponding CICP value. */ - JXL_WHITE_POINT_CUSTOM = 2, - /** CIE Standard Illuminant E (equal-energy): 1/3, 1/3 */ - JXL_WHITE_POINT_E = 10, - /** DCI-P3 from SMPTE RP 431-2: 0.314, 0.351 */ - JXL_WHITE_POINT_DCI = 11, -} JxlWhitePoint; - -/** Built-in primaries for color encoding. When decoding, the primaries can be - * read from the @ref JxlColorEncoding primaries_red_xy, primaries_green_xy and - * primaries_blue_xy fields regardless of the enum value. When encoding, the - * enum values except ::JXL_PRIMARIES_CUSTOM override the numerical fields. - * Some enum values match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC - * 23091-2:2019(E)), however the white point and RGB primaries are separate - * enums here. - */ -typedef enum { - /** The CIE xy values of the red, green and blue primaries are: 0.639998686, - 0.330010138; 0.300003784, 0.600003357; 0.150002046, 0.059997204 */ - JXL_PRIMARIES_SRGB = 1, - /** Primaries must be read from the @ref JxlColorEncoding primaries_red_xy, - * primaries_green_xy and primaries_blue_xy fields, or as ICC profile. This - * enum value is not an exact match of the corresponding CICP value. */ - JXL_PRIMARIES_CUSTOM = 2, - /** As specified in Rec. ITU-R BT.2100-1 */ - JXL_PRIMARIES_2100 = 9, - /** As specified in SMPTE RP 431-2 */ - JXL_PRIMARIES_P3 = 11, -} JxlPrimaries; - -/** Built-in transfer functions for color encoding. Enum values match a subset - * of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)) unless specified - * otherwise. */ -typedef enum { - /** As specified in ITU-R BT.709-6 */ - JXL_TRANSFER_FUNCTION_709 = 1, - /** None of the other table entries describe the transfer function. */ - JXL_TRANSFER_FUNCTION_UNKNOWN = 2, - /** The gamma exponent is 1 */ - JXL_TRANSFER_FUNCTION_LINEAR = 8, - /** As specified in IEC 61966-2-1 sRGB */ - JXL_TRANSFER_FUNCTION_SRGB = 13, - /** As specified in SMPTE ST 2084 */ - JXL_TRANSFER_FUNCTION_PQ = 16, - /** As specified in SMPTE ST 428-1 */ - JXL_TRANSFER_FUNCTION_DCI = 17, - /** As specified in Rec. ITU-R BT.2100-1 (HLG) */ - JXL_TRANSFER_FUNCTION_HLG = 18, - /** Transfer function follows power law given by the gamma value in @ref - JxlColorEncoding. Not a CICP value. */ - JXL_TRANSFER_FUNCTION_GAMMA = 65535, -} JxlTransferFunction; - -/** Rendering intent for color encoding, as specified in ISO 15076-1:2010 */ -typedef enum { - /** vendor-specific */ - JXL_RENDERING_INTENT_PERCEPTUAL = 0, - /** media-relative */ - JXL_RENDERING_INTENT_RELATIVE, - /** vendor-specific */ - JXL_RENDERING_INTENT_SATURATION, - /** ICC-absolute */ - JXL_RENDERING_INTENT_ABSOLUTE, -} JxlRenderingIntent; - -/** Color encoding of the image as structured information. - */ -typedef struct { - /** Color space of the image data. - */ - JxlColorSpace color_space; - - /** Built-in white point. If this value is ::JXL_WHITE_POINT_CUSTOM, must - * use the numerical white point values from white_point_xy. - */ - JxlWhitePoint white_point; - - /** Numerical whitepoint values in CIE xy space. */ - double white_point_xy[2]; - - /** Built-in RGB primaries. If this value is ::JXL_PRIMARIES_CUSTOM, must - * use the numerical primaries values below. This field and the custom values - * below are unused and must be ignored if the color space is - * ::JXL_COLOR_SPACE_GRAY or ::JXL_COLOR_SPACE_XYB. - */ - JxlPrimaries primaries; - - /** Numerical red primary values in CIE xy space. */ - double primaries_red_xy[2]; - - /** Numerical green primary values in CIE xy space. */ - double primaries_green_xy[2]; - - /** Numerical blue primary values in CIE xy space. */ - double primaries_blue_xy[2]; - - /** Transfer function if have_gamma is 0 */ - JxlTransferFunction transfer_function; - - /** Gamma value used when transfer_function is @ref - * JXL_TRANSFER_FUNCTION_GAMMA - */ - double gamma; - - /** Rendering intent defined for the color profile. */ - JxlRenderingIntent rendering_intent; -} JxlColorEncoding; - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_COLOR_ENCODING_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/compressed_icc.h b/third_party/jpeg-xl/lib/include/jxl/compressed_icc.h deleted file mode 100644 index 10232794b69d3..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/compressed_icc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_metadata - * @{ - * @file compressed_icc.h - * @brief Utility functions to compress and decompress ICC streams. - */ - -#ifndef JXL_COMPRESSED_ICC_H_ -#define JXL_COMPRESSED_ICC_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Allocates a buffer using the memory manager, fills it with a compressed - * representation of an ICC profile, returns the result through @c output_buffer - * and indicates its size through @c output_size. - * - * The result must be freed using the memory manager once it is not of any more - * use. - * - * @param[in] memory_manager Pointer to a JxlMemoryManager. - * @param[in] icc Pointer to a buffer containing the uncompressed ICC profile. - * @param[in] icc_size Size of the buffer containing the ICC profile. - * @param[out] compressed_icc Will be set to a pointer to the buffer containing - * the result. - * @param[out] compressed_icc_size Will be set to the size of the buffer - * containing the result. - * @return Whether compressing the profile was successful. - */ -JXL_EXPORT JXL_BOOL JxlICCProfileEncode(const JxlMemoryManager* memory_manager, - const uint8_t* icc, size_t icc_size, - uint8_t** compressed_icc, - size_t* compressed_icc_size); - -/** - * Allocates a buffer using the memory manager, fills it with the decompressed - * version of the ICC profile in @c compressed_icc, returns the result through - * @c output_buffer and indicates its size through @c output_size. - * - * The result must be freed using the memory manager once it is not of any more - * use. - * - * @param[in] memory_manager Pointer to a JxlMemoryManager. - * @param[in] compressed_icc Pointer to a buffer containing the compressed ICC - * profile. - * @param[in] compressed_icc_size Size of the buffer containing the compressed - * ICC profile. - * @param[out] icc Will be set to a pointer to the buffer containing the result. - * @param[out] icc_size Will be set to the size of the buffer containing the - * result. - * @return Whether decompressing the profile was successful. - */ -JXL_EXPORT JXL_BOOL JxlICCProfileDecode(const JxlMemoryManager* memory_manager, - const uint8_t* compressed_icc, - size_t compressed_icc_size, - uint8_t** icc, size_t* icc_size); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_COMPRESSED_ICC_H_ */ - -/** @} */ diff --git a/third_party/jpeg-xl/lib/include/jxl/decode.h b/third_party/jpeg-xl/lib/include/jxl/decode.h deleted file mode 100644 index 69c00243d6633..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/decode.h +++ /dev/null @@ -1,1472 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_decoder - * @{ - * @file decode.h - * @brief Decoding API for JPEG XL. - */ - -#ifndef JXL_DECODE_H_ -#define JXL_DECODE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include // TODO(eustas): remove before v1.0 -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Decoder library version. - * - * @return the decoder library version as an integer: - * MAJOR_VERSION * 1000000 + MINOR_VERSION * 1000 + PATCH_VERSION. For example, - * version 1.2.3 would return 1002003. - */ -JXL_EXPORT uint32_t JxlDecoderVersion(void); - -/** The result of @ref JxlSignatureCheck. - */ -typedef enum { - /** Not enough bytes were passed to determine if a valid signature was found. - */ - JXL_SIG_NOT_ENOUGH_BYTES = 0, - - /** No valid JPEG XL header was found. */ - JXL_SIG_INVALID = 1, - - /** A valid JPEG XL codestream signature was found, that is a JPEG XL image - * without container. - */ - JXL_SIG_CODESTREAM = 2, - - /** A valid container signature was found, that is a JPEG XL image embedded - * in a box format container. - */ - JXL_SIG_CONTAINER = 3, -} JxlSignature; - -/** - * JPEG XL signature identification. - * - * Checks if the passed buffer contains a valid JPEG XL signature. The passed @p - * buf of size - * @p size doesn't need to be a full image, only the beginning of the file. - * - * @return a flag indicating if a JPEG XL signature was found and what type. - * - ::JXL_SIG_NOT_ENOUGH_BYTES if not enough bytes were passed to - * determine if a valid signature is there. - * - ::JXL_SIG_INVALID if no valid signature found for JPEG XL decoding. - * - ::JXL_SIG_CODESTREAM if a valid JPEG XL codestream signature was - * found. - * - ::JXL_SIG_CONTAINER if a valid JPEG XL container signature was found. - */ -JXL_EXPORT JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len); - -/** - * Opaque structure that holds the JPEG XL decoder. - * - * Allocated and initialized with @ref JxlDecoderCreate(). - * Cleaned up and deallocated with @ref JxlDecoderDestroy(). - */ -typedef struct JxlDecoderStruct JxlDecoder; - -/** - * Creates an instance of @ref JxlDecoder and initializes it. - * - * @p memory_manager will be used for all the library dynamic allocations made - * from this instance. The parameter may be NULL, in which case the default - * allocator will be used. See jxl/memory_manager.h for details. - * - * @param memory_manager custom allocator function. It may be NULL. The memory - * manager will be copied internally. - * @return @c NULL if the instance can not be allocated or initialized - * @return pointer to initialized @ref JxlDecoder otherwise - */ -JXL_EXPORT JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager); - -/** - * Re-initializes a @ref JxlDecoder instance, so it can be re-used for decoding - * another image. All state and settings are reset as if the object was - * newly created with @ref JxlDecoderCreate, but the memory manager is kept. - * - * @param dec instance to be re-initialized. - */ -JXL_EXPORT void JxlDecoderReset(JxlDecoder* dec); - -/** - * Deinitializes and frees @ref JxlDecoder instance. - * - * @param dec instance to be cleaned up and deallocated. - */ -JXL_EXPORT void JxlDecoderDestroy(JxlDecoder* dec); - -/** - * Return value for @ref JxlDecoderProcessInput. - * The values from ::JXL_DEC_BASIC_INFO onwards are optional informative - * events that can be subscribed to, they are never returned if they - * have not been registered with @ref JxlDecoderSubscribeEvents. - */ -typedef enum { - /** Function call finished successfully, or decoding is finished and there is - * nothing more to be done. - * - * Note that @ref JxlDecoderProcessInput will return ::JXL_DEC_SUCCESS if - * all events that were registered with @ref JxlDecoderSubscribeEvents were - * processed, even before the end of the JPEG XL codestream. - * - * In this case, the return value @ref JxlDecoderReleaseInput will be the same - * as it was at the last signaled event. E.g. if ::JXL_DEC_FULL_IMAGE was - * subscribed to, then all bytes from the end of the JPEG XL codestream - * (including possible boxes needed for jpeg reconstruction) will be returned - * as unprocessed. - */ - JXL_DEC_SUCCESS = 0, - - /** An error occurred, for example invalid input file or out of memory. - * TODO(lode): add function to get error information from decoder. - */ - JXL_DEC_ERROR = 1, - - /** The decoder needs more input bytes to continue. Before the next @ref - * JxlDecoderProcessInput call, more input data must be set, by calling @ref - * JxlDecoderReleaseInput (if input was set previously) and then calling @ref - * JxlDecoderSetInput. @ref JxlDecoderReleaseInput returns how many bytes - * are not yet processed, before a next call to @ref JxlDecoderProcessInput - * all unprocessed bytes must be provided again (the address need not match, - * but the contents must), and more bytes must be concatenated after the - * unprocessed bytes. - * In most cases, @ref JxlDecoderReleaseInput will return no unprocessed bytes - * at this event, the only exceptions are if the previously set input ended - * within (a) the raw codestream signature, (b) the signature box, (c) a box - * header, or (d) the first 4 bytes of a `brob`, `ftyp`, or `jxlp` box. In any - * of these cases the number of unprocessed bytes is less than 20. - */ - JXL_DEC_NEED_MORE_INPUT = 2, - - /** The decoder is able to decode a preview image and requests setting a - * preview output buffer using @ref JxlDecoderSetPreviewOutBuffer. This occurs - * if ::JXL_DEC_PREVIEW_IMAGE is requested and it is possible to decode a - * preview image from the codestream and the preview out buffer was not yet - * set. There is maximum one preview image in a codestream. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the frame header (including ToC) of the preview frame as - * unprocessed. - */ - JXL_DEC_NEED_PREVIEW_OUT_BUFFER = 3, - - /** The decoder requests an output buffer to store the full resolution image, - * which can be set with @ref JxlDecoderSetImageOutBuffer or with @ref - * JxlDecoderSetImageOutCallback. This event re-occurs for new frames if - * there are multiple animation frames and requires setting an output again. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the frame header (including ToC) as unprocessed. - */ - JXL_DEC_NEED_IMAGE_OUT_BUFFER = 5, - - /** The JPEG reconstruction buffer is too small for reconstructed JPEG - * codestream to fit. @ref JxlDecoderSetJPEGBuffer must be called again to - * make room for remaining bytes. This event may occur multiple times - * after ::JXL_DEC_JPEG_RECONSTRUCTION. - */ - JXL_DEC_JPEG_NEED_MORE_OUTPUT = 6, - - /** The box contents output buffer is too small. @ref JxlDecoderSetBoxBuffer - * must be called again to make room for remaining bytes. This event may occur - * multiple times after ::JXL_DEC_BOX. - */ - JXL_DEC_BOX_NEED_MORE_OUTPUT = 7, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": Basic information such as image dimensions and - * extra channels. This event occurs max once per image. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the basic info as unprocessed (including the last byte of basic info - * if it did not end on a byte boundary). - */ - JXL_DEC_BASIC_INFO = 0x40, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": Color encoding or ICC profile from the - * codestream header. This event occurs max once per image and always later - * than ::JXL_DEC_BASIC_INFO and earlier than any pixel data. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the image header (which is the start of the first frame) as - * unprocessed. - */ - JXL_DEC_COLOR_ENCODING = 0x100, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": Preview image, a small frame, decoded. This - * event can only happen if the image has a preview frame encoded. This event - * occurs max once for the codestream and always later than @ref - * JXL_DEC_COLOR_ENCODING and before ::JXL_DEC_FRAME. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the preview frame as unprocessed. - */ - JXL_DEC_PREVIEW_IMAGE = 0x200, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": Beginning of a frame. @ref - * JxlDecoderGetFrameHeader can be used at this point. A note on frames: - * a JPEG XL image can have internal frames that are not intended to be - * displayed (e.g. used for compositing a final frame), but this only returns - * displayed frames, unless @ref JxlDecoderSetCoalescing was set to @ref - * JXL_FALSE "JXL_FALSE": in that case, the individual layers are returned, - * without blending. Note that even when coalescing is disabled, only frames - * of type kRegularFrame are returned; frames of type kReferenceOnly - * and kLfFrame are always for internal purposes only and cannot be accessed. - * A displayed frame either has an animation duration or is the only or last - * frame in the image. This event occurs max once per displayed frame, always - * later than ::JXL_DEC_COLOR_ENCODING, and always earlier than any pixel - * data. While JPEG XL supports encoding a single frame as the composition of - * multiple internal sub-frames also called frames, this event is not - * indicated for the internal frames. In this case, @ref - * JxlDecoderReleaseInput will return all bytes from the end of the frame - * header (including ToC) as unprocessed. - */ - JXL_DEC_FRAME = 0x400, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": full frame (or layer, in case coalescing is - * disabled) is decoded. @ref JxlDecoderSetImageOutBuffer must be used after - * getting the basic image information to be able to get the image pixels, if - * not this return status only indicates we're past this point in the - * codestream. This event occurs max once per frame. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the frame (or if ::JXL_DEC_JPEG_RECONSTRUCTION is subscribed to, - * from the end of the last box that is needed for jpeg reconstruction) as - * unprocessed. - */ - JXL_DEC_FULL_IMAGE = 0x1000, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": JPEG reconstruction data decoded. @ref - * JxlDecoderSetJPEGBuffer may be used to set a JPEG reconstruction buffer - * after getting the JPEG reconstruction data. If a JPEG reconstruction buffer - * is set a byte stream identical to the JPEG codestream used to encode the - * image will be written to the JPEG reconstruction buffer instead of pixels - * to the image out buffer. This event occurs max once per image and always - * before ::JXL_DEC_FULL_IMAGE. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the `jbrd` box as unprocessed. - */ - JXL_DEC_JPEG_RECONSTRUCTION = 0x2000, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": The header of a box of the container format - * (BMFF) is decoded. The following API functions related to boxes can be used - * after this event: - * - @ref JxlDecoderSetBoxBuffer and @ref JxlDecoderReleaseBoxBuffer - * "JxlDecoderReleaseBoxBuffer": set and release a buffer to get the box - * data. - * - @ref JxlDecoderGetBoxType get the 4-character box typename. - * - @ref JxlDecoderGetBoxSizeRaw get the size of the box as it appears in - * the container file, not decompressed. - * - @ref JxlDecoderSetDecompressBoxes to configure whether to get the box - * data decompressed, or possibly compressed. - * - * Boxes can be compressed. This is so when their box type is - * "brob". In that case, they have an underlying decompressed box - * type and decompressed data. @ref JxlDecoderSetDecompressBoxes allows - * configuring which data to get. Decompressing requires - * Brotli. @ref JxlDecoderGetBoxType has a flag to get the compressed box - * type, which can be "brob", or the decompressed box type. If a box - * is not compressed (its compressed type is not "brob"), then - * the output decompressed box type and data is independent of what - * setting is configured. - * - * The buffer set with @ref JxlDecoderSetBoxBuffer must be set again for each - * next box to be obtained, or can be left unset to skip outputting this box. - * The output buffer contains the full box data when the - * ::JXL_DEC_BOX_COMPLETE (if subscribed to) or subsequent ::JXL_DEC_SUCCESS - * or ::JXL_DEC_BOX event occurs. ::JXL_DEC_BOX occurs for all boxes, - * including non-metadata boxes such as the signature box or codestream boxes. - * To check whether the box is a metadata type for respectively EXIF, XMP or - * JUMBF, use @ref JxlDecoderGetBoxType and check for types "Exif", "xml " and - * "jumb" respectively. - * - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * start of the box header as unprocessed. - */ - JXL_DEC_BOX = 0x4000, - - /** Informative event by @ref JxlDecoderProcessInput - * "JxlDecoderProcessInput": a progressive step in decoding the frame is - * reached. When calling @ref JxlDecoderFlushImage at this point, the flushed - * image will correspond exactly to this point in decoding, and not yet - * contain partial results (such as partially more fine detail) of a next - * step. By default, this event will trigger maximum once per frame, when a - * 8x8th resolution (DC) image is ready (the image data is still returned at - * full resolution, giving upscaled DC). Use @ref - * JxlDecoderSetProgressiveDetail to configure more fine-grainedness. The - * event is not guaranteed to trigger, not all images have progressive steps - * or DC encoded. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the section that was needed to produce this progressive event as - * unprocessed. - */ - JXL_DEC_FRAME_PROGRESSION = 0x8000, - - /** The box being decoded is now complete. This is only emitted if a buffer - * was set for the box. - */ - JXL_DEC_BOX_COMPLETE = 0x10000, -} JxlDecoderStatus; - -/** Types of progressive detail. - * Setting a progressive detail with value N implies all progressive details - * with smaller or equal value. Currently only the following level of - * progressive detail is implemented: - * - @ref kDC (which implies kFrames) - * - @ref kLastPasses (which implies @ref kDC and @ref kFrames) - * - @ref kPasses (which implies @ref kLastPasses, kDC and @ref kFrames) - */ -typedef enum { - /** - * after completed kRegularFrames - */ - kFrames = 0, - /** - * after completed DC (1:8) - */ - kDC = 1, - /** - * after completed AC passes that are the last pass for their resolution - * target. - */ - kLastPasses = 2, - /** - * after completed AC passes that are not the last pass for their resolution - * target. - */ - kPasses = 3, - /** - * during DC frame when lower resolution are completed (1:32, 1:16) - */ - kDCProgressive = 4, - /** - * after completed groups - */ - kDCGroups = 5, - /** - * after completed groups - */ - kGroups = 6, -} JxlProgressiveDetail; - -/** Rewinds decoder to the beginning. The same input must be given again from - * the beginning of the file and the decoder will emit events from the beginning - * again. When rewinding (as opposed to @ref JxlDecoderReset), the decoder can - * keep state about the image, which it can use to skip to a requested frame - * more efficiently with @ref JxlDecoderSkipFrames. Settings such as parallel - * runner or subscribed events are kept. After rewind, @ref - * JxlDecoderSubscribeEvents can be used again, and it is feasible to leave out - * events that were already handled before, such as ::JXL_DEC_BASIC_INFO - * and ::JXL_DEC_COLOR_ENCODING, since they will provide the same information - * as before. - * The difference to @ref JxlDecoderReset is that some state is kept, namely - * settings set by a call to - * - @ref JxlDecoderSetCoalescing, - * - @ref JxlDecoderSetDesiredIntensityTarget, - * - @ref JxlDecoderSetDecompressBoxes, - * - @ref JxlDecoderSetKeepOrientation, - * - @ref JxlDecoderSetUnpremultiplyAlpha, - * - @ref JxlDecoderSetParallelRunner, - * - @ref JxlDecoderSetRenderSpotcolors, and - * - @ref JxlDecoderSubscribeEvents. - * - * @param dec decoder object - */ -JXL_EXPORT void JxlDecoderRewind(JxlDecoder* dec); - -/** Makes the decoder skip the next `amount` frames. It still needs to process - * the input, but will not output the frame events. It can be more efficient - * when skipping frames, and even more so when using this after @ref - * JxlDecoderRewind. If the decoder is already processing a frame (could - * have emitted ::JXL_DEC_FRAME but not yet ::JXL_DEC_FULL_IMAGE), it - * starts skipping from the next frame. If the amount is larger than the amount - * of frames remaining in the image, all remaining frames are skipped. Calling - * this function multiple times adds the amount to skip to the already existing - * amount. - * - * A frame here is defined as a frame that without skipping emits events such - * as ::JXL_DEC_FRAME and ::JXL_DEC_FULL_IMAGE, frames that are internal - * to the file format but are not rendered as part of an animation, or are not - * the final still frame of a still image, are not counted. - * - * @param dec decoder object - * @param amount the amount of frames to skip - */ -JXL_EXPORT void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount); - -/** - * Skips processing the current frame. Can be called after frame processing - * already started, signaled by a ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event, - * but before the corresponding ::JXL_DEC_FULL_IMAGE event. The next signaled - * event will be another ::JXL_DEC_FRAME, or ::JXL_DEC_SUCCESS if there - * are no more frames. If pixel data is required from the already processed part - * of the frame, @ref JxlDecoderFlushImage must be called before this. - * - * @param dec decoder object - * @return ::JXL_DEC_SUCCESS if there is a frame to skip, and @ref - * JXL_DEC_ERROR if the function was not called during frame processing. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec); - -/** - * Set the parallel runner for multithreading. May only be set before starting - * decoding. - * - * @param dec decoder object - * @param parallel_runner function pointer to runner for multithreading. It may - * be NULL to use the default, single-threaded, runner. A multithreaded - * runner should be set to reach fast performance. - * @param parallel_runner_opaque opaque pointer for parallel_runner. - * @return ::JXL_DEC_SUCCESS if the runner was set, ::JXL_DEC_ERROR - * otherwise (the previous runner remains set). - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetParallelRunner(JxlDecoder* dec, JxlParallelRunner parallel_runner, - void* parallel_runner_opaque); - -/** - * Returns a hint indicating how many more bytes the decoder is expected to - * need to make @ref JxlDecoderGetBasicInfo available after the next @ref - * JxlDecoderProcessInput call. This is a suggested large enough value for - * the amount of bytes to provide in the next @ref JxlDecoderSetInput call, but - * it is not guaranteed to be an upper bound nor a lower bound. This number does - * not include bytes that have already been released from the input. Can be used - * before the first @ref JxlDecoderProcessInput call, and is correct the first - * time in most cases. If not, @ref JxlDecoderSizeHintBasicInfo can be called - * again to get an updated hint. - * - * @param dec decoder object - * @return the size hint in bytes if the basic info is not yet fully decoded. - * @return 0 when the basic info is already available. - */ -JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec); - -/** Select for which informative events, i.e. ::JXL_DEC_BASIC_INFO, etc., the - * decoder should return with a status. It is not required to subscribe to any - * events, data can still be requested from the decoder as soon as it available. - * By default, the decoder is subscribed to no events (events_wanted == 0), and - * the decoder will then only return when it cannot continue because it needs - * more input data or more output buffer. This function may only be be called - * before using @ref JxlDecoderProcessInput. - * - * @param dec decoder object - * @param events_wanted bitfield of desired events. - * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, - int events_wanted); - -/** Enables or disables preserving of as-in-bitstream pixeldata - * orientation. Some images are encoded with an Orientation tag - * indicating that the decoder must perform a rotation and/or - * mirroring to the encoded image data. - * - * - If skip_reorientation is ::JXL_FALSE (the default): the decoder - * will apply the transformation from the orientation setting, hence - * rendering the image according to its specified intent. When - * producing a @ref JxlBasicInfo, the decoder will always set the - * orientation field to JXL_ORIENT_IDENTITY (matching the returned - * pixel data) and also align xsize and ysize so that they correspond - * to the width and the height of the returned pixel data. - * - If skip_reorientation is ::JXL_TRUE "JXL_TRUE": the decoder will skip - * applying the transformation from the orientation setting, returning - * the image in the as-in-bitstream pixeldata orientation. - * This may be faster to decode since the decoder doesn't have to apply the - * transformation, but can cause wrong display of the image if the - * orientation tag is not correctly taken into account by the user. - * - * By default, this option is disabled, and the returned pixel data is - * re-oriented according to the image's Orientation setting. - * - * This function must be called at the beginning, before decoding is performed. - * - * @see JxlBasicInfo for the orientation field, and @ref JxlOrientation for the - * possible values. - * - * @param dec decoder object - * @param skip_reorientation JXL_TRUE to enable, JXL_FALSE to disable. - * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation); - -/** - * Enables or disables preserving of associated alpha channels. If - * unpremul_alpha is set to ::JXL_FALSE then for associated alpha channel, - * the pixel data is returned with premultiplied colors. If it is set to @ref - * JXL_TRUE, The colors will be unpremultiplied based on the alpha channel. This - * function has no effect if the image does not have an associated alpha - * channel. - * - * By default, this option is disabled, and the returned pixel data "as is". - * - * This function must be called at the beginning, before decoding is performed. - * - * @param dec decoder object - * @param unpremul_alpha JXL_TRUE to enable, JXL_FALSE to disable. - * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec, JXL_BOOL unpremul_alpha); - -/** Enables or disables rendering spot colors. By default, spot colors - * are rendered, which is OK for viewing the decoded image. If render_spotcolors - * is ::JXL_FALSE, then spot colors are not rendered, and have to be - * retrieved separately using @ref JxlDecoderSetExtraChannelBuffer. This is - * useful for e.g. printing applications. - * - * @param dec decoder object - * @param render_spotcolors JXL_TRUE to enable (default), JXL_FALSE to disable. - * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors); - -/** Enables or disables coalescing of zero-duration frames. By default, frames - * are returned with coalescing enabled, i.e. all frames have the image - * dimensions, and are blended if needed. When coalescing is disabled, frames - * can have arbitrary dimensions, a non-zero crop offset, and blending is not - * performed. For display, coalescing is recommended. For loading a multi-layer - * still image as separate layers (as opposed to the merged image), coalescing - * has to be disabled. - * - * @param dec decoder object - * @param coalescing JXL_TRUE to enable coalescing (default), JXL_FALSE to - * disable it. - * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, - JXL_BOOL coalescing); - -/** - * Decodes JPEG XL file using the available bytes. Requires input has been - * set with @ref JxlDecoderSetInput. After @ref JxlDecoderProcessInput, input - * can optionally be released with @ref JxlDecoderReleaseInput and then set - * again to next bytes in the stream. @ref JxlDecoderReleaseInput returns how - * many bytes are not yet processed, before a next call to @ref - * JxlDecoderProcessInput all unprocessed bytes must be provided again (the - * address need not match, but the contents must), and more bytes may be - * concatenated after the unprocessed bytes. - * - * The returned status indicates whether the decoder needs more input bytes, or - * more output buffer for a certain type of output data. No matter what the - * returned status is (other than ::JXL_DEC_ERROR), new information, such - * as @ref JxlDecoderGetBasicInfo, may have become available after this call. - * When the return value is not ::JXL_DEC_ERROR or ::JXL_DEC_SUCCESS, the - * decoding requires more @ref JxlDecoderProcessInput calls to continue. - * - * @param dec decoder object - * @return ::JXL_DEC_SUCCESS when decoding finished and all events handled. - * If you still have more unprocessed input data anyway, then you can still - * continue by using @ref JxlDecoderSetInput and calling @ref - * JxlDecoderProcessInput again, similar to handling @ref - * JXL_DEC_NEED_MORE_INPUT. ::JXL_DEC_SUCCESS can occur instead of @ref - * JXL_DEC_NEED_MORE_INPUT when, for example, the input data ended right at - * the boundary of a box of the container format, all essential codestream - * boxes were already decoded, but extra metadata boxes are still present in - * the next data. @ref JxlDecoderProcessInput cannot return success if all - * codestream boxes have not been seen yet. - * @return ::JXL_DEC_ERROR when decoding failed, e.g. invalid codestream. - * TODO(lode): document the input data mechanism - * @return ::JXL_DEC_NEED_MORE_INPUT when more input data is necessary. - * @return ::JXL_DEC_BASIC_INFO when basic info such as image dimensions is - * available and this informative event is subscribed to. - * @return ::JXL_DEC_COLOR_ENCODING when color profile information is - * available and this informative event is subscribed to. - * @return ::JXL_DEC_PREVIEW_IMAGE when preview pixel information is - * available and output in the preview buffer. - * @return ::JXL_DEC_FULL_IMAGE when all pixel information at highest detail - * is available and has been output in the pixel buffer. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec); - -/** - * Sets input data for @ref JxlDecoderProcessInput. The data is owned by the - * caller and may be used by the decoder until @ref JxlDecoderReleaseInput is - * called or the decoder is destroyed or reset so must be kept alive until then. - * Cannot be called if @ref JxlDecoderSetInput was already called and @ref - * JxlDecoderReleaseInput was not yet called, and cannot be called after @ref - * JxlDecoderCloseInput indicating the end of input was called. - * - * @param dec decoder object - * @param data pointer to next bytes to read from - * @param size amount of bytes available starting from data - * @return ::JXL_DEC_ERROR if input was already set without releasing or @ref - * JxlDecoderCloseInput was already called, ::JXL_DEC_SUCCESS otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, - const uint8_t* data, - size_t size); - -/** - * Releases input which was provided with @ref JxlDecoderSetInput. Between @ref - * JxlDecoderProcessInput and @ref JxlDecoderReleaseInput, the user may not - * alter the data in the buffer. Calling @ref JxlDecoderReleaseInput is required - * whenever any input is already set and new input needs to be added with @ref - * JxlDecoderSetInput, but is not required before @ref JxlDecoderDestroy or @ref - * JxlDecoderReset. Calling @ref JxlDecoderReleaseInput when no input is set is - * not an error and returns `0`. - * - * @param dec decoder object - * @return The amount of bytes the decoder has not yet processed that are still - * remaining in the data set by @ref JxlDecoderSetInput, or `0` if no input - * is set or @ref JxlDecoderReleaseInput was already called. For a next call to - * @ref JxlDecoderProcessInput, the buffer must start with these unprocessed - * bytes. From this value it is possible to infer the position of certain JPEG - * XL codestream elements (e.g. end of headers, frame start/end). See the - * documentation of individual values of @ref JxlDecoderStatus for more - * information. - */ -JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec); - -/** - * Marks the input as finished, indicates that no more @ref JxlDecoderSetInput - * will be called. This function allows the decoder to determine correctly if it - * should return success, need more input or error in certain cases. For - * backwards compatibility with a previous version of the API, using this - * function is optional when not using the ::JXL_DEC_BOX event (the decoder - * is able to determine the end of the image frames without marking the end), - * but using this function is required when using ::JXL_DEC_BOX for getting - * metadata box contents. This function does not replace @ref - * JxlDecoderReleaseInput, that function should still be called if its return - * value is needed. - * - * @ref JxlDecoderCloseInput should be called as soon as all known input bytes - * are set (e.g. at the beginning when not streaming but setting all input - * at once), before the final @ref JxlDecoderProcessInput calls. - * - * @param dec decoder object - */ -JXL_EXPORT void JxlDecoderCloseInput(JxlDecoder* dec); - -/** - * Outputs the basic image information, such as image dimensions, bit depth and - * all other JxlBasicInfo fields, if available. - * - * @param dec decoder object - * @param info struct to copy the information into, or NULL to only check - * whether the information is available through the return value. - * @return ::JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR - * in case of other error conditions. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, - JxlBasicInfo* info); - -/** - * Outputs information for extra channel at the given index. The index must be - * smaller than num_extra_channels in the associated @ref JxlBasicInfo. - * - * @param dec decoder object - * @param index index of the extra channel to query. - * @param info struct to copy the information into, or NULL to only check - * whether the information is available through the return value. - * @return ::JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR - * in case of other error conditions. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelInfo( - const JxlDecoder* dec, size_t index, JxlExtraChannelInfo* info); - -/** - * Outputs name for extra channel at the given index in UTF-8. The index must be - * smaller than `num_extra_channels` in the associated @ref JxlBasicInfo. The - * buffer for name must have at least `name_length + 1` bytes allocated, gotten - * from the associated @ref JxlExtraChannelInfo. - * - * @param dec decoder object - * @param index index of the extra channel to query. - * @param name buffer to copy the name into - * @param size size of the name buffer in bytes - * @return ::JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR - * in case of other error conditions. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelName(const JxlDecoder* dec, - size_t index, - char* name, - size_t size); - -/** Defines which color profile to get: the profile from the codestream - * metadata header, which represents the color profile of the original image, - * or the color profile from the pixel data produced by the decoder. Both are - * the same if the JxlBasicInfo has uses_original_profile set. - */ -typedef enum { - /** Get the color profile of the original image from the metadata. - */ - JXL_COLOR_PROFILE_TARGET_ORIGINAL = 0, - - /** Get the color profile of the pixel data the decoder outputs. */ - JXL_COLOR_PROFILE_TARGET_DATA = 1, -} JxlColorProfileTarget; - -/** - * Outputs the color profile as JPEG XL encoded structured data, if available. - * This is an alternative to an ICC Profile, which can represent a more limited - * amount of color spaces, but represents them exactly through enum values. - * - * It is often possible to use @ref JxlDecoderGetColorAsICCProfile as an - * alternative anyway. The following scenarios are possible: - * - The JPEG XL image has an attached ICC Profile, in that case, the encoded - * structured data is not available and this function will return an error - * status. @ref JxlDecoderGetColorAsICCProfile should be called instead. - * - The JPEG XL image has an encoded structured color profile, and it - * represents an RGB or grayscale color space. This function will return it. - * You can still use @ref JxlDecoderGetColorAsICCProfile as well as an - * alternative if desired, though depending on which RGB color space is - * represented, the ICC profile may be a close approximation. It is also not - * always feasible to deduce from an ICC profile which named color space it - * exactly represents, if any, as it can represent any arbitrary space. - * HDR color spaces such as those using PQ and HLG are also potentially - * problematic, in that: while ICC profiles can encode a transfer function - * that happens to approximate those of PQ and HLG (HLG for only one given - * system gamma at a time, and necessitating a 3D LUT if gamma is to be - * different from `1`), they cannot (before ICCv4.4) semantically signal that - * this is the color space that they represent. Therefore, they will - * typically not actually be interpreted as representing an HDR color space. - * This is especially detrimental to PQ which will then be interpreted as if - * the maximum signal value represented SDR white instead of 10000 cd/m^2, - * meaning that the image will be displayed two orders of magnitude (5-7 EV) - * too dim. - * - The JPEG XL image has an encoded structured color profile, and it - * indicates an unknown or xyb color space. In that case, @ref - * JxlDecoderGetColorAsICCProfile is not available. - * - * When rendering an image on a system where ICC-based color management is used, - * @ref JxlDecoderGetColorAsICCProfile should generally be used first as it will - * return a ready-to-use profile (with the aforementioned caveat about HDR). - * When knowledge about the nominal color space is desired if available, @ref - * JxlDecoderGetColorAsEncodedProfile should be used first. - * - * @param dec decoder object - * @param target whether to get the original color profile from the metadata - * or the color profile of the decoded pixels. - * @param color_encoding struct to copy the information into, or NULL to only - * check whether the information is available through the return value. - * @return ::JXL_DEC_SUCCESS if the data is available and returned, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in - * case the encoded structured color profile does not exist in the - * codestream. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile( - const JxlDecoder* dec, JxlColorProfileTarget target, - JxlColorEncoding* color_encoding); - -/** - * Outputs the size in bytes of the ICC profile returned by @ref - * JxlDecoderGetColorAsICCProfile, if available, or indicates there is none - * available. In most cases, the image will have an ICC profile available, but - * if it does not, @ref JxlDecoderGetColorAsEncodedProfile must be used instead. - * - * @see JxlDecoderGetColorAsEncodedProfile for more information. The ICC - * profile is either the exact ICC profile attached to the codestream metadata, - * or a close approximation generated from JPEG XL encoded structured data, - * depending of what is encoded in the codestream. - * - * @param dec decoder object - * @param target whether to get the original color profile from the metadata - * or the color profile of the decoded pixels. - * @param size variable to output the size into, or NULL to only check the - * return status. - * @return ::JXL_DEC_SUCCESS if the ICC profile is available, @ref - * JXL_DEC_NEED_MORE_INPUT if the decoder has not yet received enough - * input data to determine whether an ICC profile is available or what its - * size is, ::JXL_DEC_ERROR in case the ICC profile is not available and - * cannot be generated. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize( - const JxlDecoder* dec, JxlColorProfileTarget target, size_t* size); - -/** - * Outputs ICC profile if available. The profile is only available if @ref - * JxlDecoderGetICCProfileSize returns success. The output buffer must have - * at least as many bytes as given by @ref JxlDecoderGetICCProfileSize. - * - * @param dec decoder object - * @param target whether to get the original color profile from the metadata - * or the color profile of the decoded pixels. - * @param icc_profile buffer to copy the ICC profile into - * @param size size of the icc_profile buffer in bytes - * @return ::JXL_DEC_SUCCESS if the profile was successfully returned, - * ::JXL_DEC_NEED_MORE_INPUT if not yet available, @ref - * JXL_DEC_ERROR if the profile doesn't exist or the output size is not - * large enough. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsICCProfile( - const JxlDecoder* dec, JxlColorProfileTarget target, uint8_t* icc_profile, - size_t size); - -/** Sets the desired output color profile of the decoded image by calling - * @ref JxlDecoderSetOutputColorProfile, passing on @c color_encoding and - * setting @c icc_data to NULL. See @ref JxlDecoderSetOutputColorProfile for - * details. - * - * @param dec decoder object - * @param color_encoding the default color encoding to set - * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref - * JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile( - JxlDecoder* dec, const JxlColorEncoding* color_encoding); - -/** Requests that the decoder perform tone mapping to the peak display luminance - * passed as @c desired_intensity_target, if appropriate. - * @note This is provided for convenience and the exact tone mapping that is - * performed is not meant to be considered authoritative in any way. It may - * change from version to version. - * @param dec decoder object - * @param desired_intensity_target the intended target peak luminance - * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref - * JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget( - JxlDecoder* dec, float desired_intensity_target); - -/** - * Sets the desired output color profile of the decoded image either from a - * color encoding or an ICC profile. Valid calls of this function have either @c - * color_encoding or @c icc_data set to NULL and @c icc_size must be `0` if and - * only if @c icc_data is NULL. - * - * Depending on whether a color management system (CMS) has been set the - * behavior is as follows: - * - * If a color management system (CMS) has been set with @ref JxlDecoderSetCms, - * and the CMS supports output to the desired color encoding or ICC profile, - * then it will provide the output in that color encoding or ICC profile. If the - * desired color encoding or the ICC is not supported, then an error will be - * returned. - * - * If no CMS has been set with @ref JxlDecoderSetCms, there are two cases: - * - * (1) Calling this function with a color encoding will convert XYB images to - * the desired color encoding. In this case, if the requested color encoding has - * a narrower gamut, or the white points differ, then the resulting image can - * have significant color distortion. Non-XYB images will not be converted to - * the desired color space. - * - * (2) Calling this function with an ICC profile will result in an error. - * - * If called with an ICC profile (after a call to @ref JxlDecoderSetCms), the - * ICC profile has to be a valid RGB or grayscale color profile. - * - * Can only be set after the ::JXL_DEC_COLOR_ENCODING event occurred and - * before any other event occurred, and should be used before getting - * ::JXL_COLOR_PROFILE_TARGET_DATA. - * - * This function must not be called before @ref JxlDecoderSetCms. - * - * @param dec decoder object - * @param color_encoding the output color encoding - * @param icc_data bytes of the icc profile - * @param icc_size size of the icc profile in bytes - * @return ::JXL_DEC_SUCCESS if the color profile was set successfully, @ref - * JXL_DEC_ERROR otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetOutputColorProfile( - JxlDecoder* dec, const JxlColorEncoding* color_encoding, - const uint8_t* icc_data, size_t icc_size); - -/** - * Sets the color management system (CMS) that will be used for color - * conversion (if applicable) during decoding. May only be set before starting - * decoding and must not be called after @ref JxlDecoderSetOutputColorProfile. - * - * See @ref JxlDecoderSetOutputColorProfile for how color conversions are done - * depending on whether or not a CMS has been set with @ref JxlDecoderSetCms. - * - * @param dec decoder object. - * @param cms structure representing a CMS implementation. See @ref - * JxlCmsInterface for more details. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec, - JxlCmsInterface cms); -// TODO(firsching): add a function JxlDecoderSetDefaultCms() for setting a -// default in case libjxl is build with a CMS. - -/** - * Returns the minimum size in bytes of the preview image output pixel buffer - * for the given format. This is the buffer for @ref - * JxlDecoderSetPreviewOutBuffer. Requires the preview header information is - * available in the decoder. - * - * @param dec decoder object - * @param format format of pixels - * @param size output value, buffer size in bytes - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * information not available yet. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( - const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size); - -/** - * Sets the buffer to write the low-resolution preview image - * to. The size of the buffer must be at least as large as given by @ref - * JxlDecoderPreviewOutBufferSize. The buffer follows the format described - * by @ref JxlPixelFormat. The preview image dimensions are given by the - * @ref JxlPreviewHeader. The buffer is owned by the caller. - * - * @param dec decoder object - * @param format format of pixels. Object owned by user and its contents are - * copied internally. - * @param buffer buffer type to output the pixel data to - * @param size size of buffer in bytes - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * size too small. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer( - JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size); - -/** - * Outputs the information from the frame, such as duration when have_animation. - * This function can be called when ::JXL_DEC_FRAME occurred for the current - * frame, even when have_animation in the JxlBasicInfo is JXL_FALSE. - * - * @param dec decoder object - * @param header struct to copy the information into, or NULL to only check - * whether the information is available through the return value. - * @return ::JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in - * case of other error conditions. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec, - JxlFrameHeader* header); - -/** - * Outputs name for the current frame. The buffer for name must have at least - * `name_length + 1` bytes allocated, gotten from the associated JxlFrameHeader. - * - * @param dec decoder object - * @param name buffer to copy the name into - * @param size size of the name buffer in bytes, including zero termination - * character, so this must be at least @ref JxlFrameHeader.name_length + 1. - * @return ::JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in - * case of other error conditions. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec, - char* name, size_t size); - -/** - * Outputs the blend information for the current frame for a specific extra - * channel. This function can be called once the ::JXL_DEC_FRAME event occurred - * for the current frame, even if the `have_animation` field in the @ref - * JxlBasicInfo is @ref JXL_FALSE. This information is only useful if coalescing - * is disabled; otherwise the decoder will have performed blending already. - * - * @param dec decoder object - * @param index the index of the extra channel - * @param blend_info struct to copy the information into - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo( - const JxlDecoder* dec, size_t index, JxlBlendInfo* blend_info); - -/** - * Returns the minimum size in bytes of the image output pixel buffer for the - * given format. This is the buffer for @ref JxlDecoderSetImageOutBuffer. - * Requires that the basic image information is available in the decoder in the - * case of coalescing enabled (default). In case coalescing is disabled, this - * can only be called after the ::JXL_DEC_FRAME event occurs. In that case, - * it will return the size required to store the possibly cropped frame (which - * can be larger or smaller than the image dimensions). - * - * @param dec decoder object - * @param format format of the pixels. - * @param size output value, buffer size in bytes - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * information not available yet. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize( - const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size); - -/** - * Sets the buffer to write the full resolution image to. This can be set when - * the ::JXL_DEC_FRAME event occurs, must be set when the @ref - * JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, and applies only for the - * current frame. The size of the buffer must be at least as large as given - * by @ref JxlDecoderImageOutBufferSize. The buffer follows the format described - * by @ref JxlPixelFormat. The buffer is owned by the caller. - * - * @param dec decoder object - * @param format format of the pixels. Object owned by user and its contents - * are copied internally. - * @param buffer buffer type to output the pixel data to - * @param size size of buffer in bytes - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * size too small. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer( - JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size); - -/** - * Function type for @ref JxlDecoderSetImageOutCallback. - * - * The callback may be called simultaneously by different threads when using a - * threaded parallel runner, on different pixels. - * - * @param opaque optional user data, as given to @ref - * JxlDecoderSetImageOutCallback. - * @param x horizontal position of leftmost pixel of the pixel data. - * @param y vertical position of the pixel data. - * @param num_pixels amount of pixels included in the pixel data, horizontally. - * This is not the same as xsize of the full image, it may be smaller. - * @param pixels pixel data as a horizontal stripe, in the format passed to @ref - * JxlDecoderSetImageOutCallback. The memory is not owned by the user, and - * is only valid during the time the callback is running. - */ -typedef void (*JxlImageOutCallback)(void* opaque, size_t x, size_t y, - size_t num_pixels, const void* pixels); - -/** - * Initialization callback for @ref JxlDecoderSetMultithreadedImageOutCallback. - * - * @param init_opaque optional user data, as given to @ref - * JxlDecoderSetMultithreadedImageOutCallback. - * @param num_threads maximum number of threads that will call the @c run - * callback concurrently. - * @param num_pixels_per_thread maximum number of pixels that will be passed in - * one call to @c run. - * @return a pointer to data that will be passed to the @c run callback, or - * @c NULL if initialization failed. - */ -typedef void* (*JxlImageOutInitCallback)(void* init_opaque, size_t num_threads, - size_t num_pixels_per_thread); - -/** - * Worker callback for @ref JxlDecoderSetMultithreadedImageOutCallback. - * - * @param run_opaque user data returned by the @c init callback. - * @param thread_id number in `[0, num_threads)` identifying the thread of the - * current invocation of the callback. - * @param x horizontal position of the first (leftmost) pixel of the pixel data. - * @param y vertical position of the pixel data. - * @param num_pixels number of pixels in the pixel data. May be less than the - * full @c xsize of the image, and will be at most equal to the @c - * num_pixels_per_thread that was passed to @c init. - * @param pixels pixel data as a horizontal stripe, in the format passed to @ref - * JxlDecoderSetMultithreadedImageOutCallback. The data pointed to - * remains owned by the caller and is only guaranteed to outlive the current - * callback invocation. - */ -typedef void (*JxlImageOutRunCallback)(void* run_opaque, size_t thread_id, - size_t x, size_t y, size_t num_pixels, - const void* pixels); - -/** - * Destruction callback for @ref JxlDecoderSetMultithreadedImageOutCallback, - * called after all invocations of the @c run callback to perform any - * appropriate clean-up of the @c run_opaque data returned by @c init. - * - * @param run_opaque user data returned by the @c init callback. - */ -typedef void (*JxlImageOutDestroyCallback)(void* run_opaque); - -/** - * Sets pixel output callback. This is an alternative to @ref - * JxlDecoderSetImageOutBuffer. This can be set when the ::JXL_DEC_FRAME - * event occurs, must be set when the ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event - * occurs, and applies only for the current frame. Only one of @ref - * JxlDecoderSetImageOutBuffer or @ref JxlDecoderSetImageOutCallback may be used - * for the same frame, not both at the same time. - * - * The callback will be called multiple times, to receive the image - * data in small chunks. The callback receives a horizontal stripe of pixel - * data, `1` pixel high, xsize pixels wide, called a scanline. The xsize here is - * not the same as the full image width, the scanline may be a partial section, - * and xsize may differ between calls. The user can then process and/or copy the - * partial scanline to an image buffer. The callback may be called - * simultaneously by different threads when using a threaded parallel runner, on - * different pixels. - * - * If @ref JxlDecoderFlushImage is not used, then each pixel will be visited - * exactly once by the different callback calls, during processing with one or - * more @ref JxlDecoderProcessInput calls. These pixels are decoded to full - * detail, they are not part of a lower resolution or lower quality progressive - * pass, but the final pass. - * - * If @ref JxlDecoderFlushImage is used, then in addition each pixel will be - * visited zero or one times during the blocking @ref JxlDecoderFlushImage call. - * Pixels visited as a result of @ref JxlDecoderFlushImage may represent a lower - * resolution or lower quality intermediate progressive pass of the image. Any - * visited pixel will be of a quality at least as good or better than previous - * visits of this pixel. A pixel may be visited zero times if it cannot be - * decoded yet or if it was already decoded to full precision (this behavior is - * not guaranteed). - * - * @param dec decoder object - * @param format format of the pixels. Object owned by user; its contents are - * copied internally. - * @param callback the callback function receiving partial scanlines of pixel - * data. - * @param opaque optional user data, which will be passed on to the callback, - * may be NULL. - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such - * as @ref JxlDecoderSetImageOutBuffer already set. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format, - JxlImageOutCallback callback, void* opaque); - -/** Similar to @ref JxlDecoderSetImageOutCallback except that the callback is - * allowed an initialization phase during which it is informed of how many - * threads will call it concurrently, and those calls are further informed of - * which thread they are occurring in. - * - * @param dec decoder object - * @param format format of the pixels. Object owned by user; its contents are - * copied internally. - * @param init_callback initialization callback. - * @param run_callback the callback function receiving partial scanlines of - * pixel data. - * @param destroy_callback clean-up callback invoked after all calls to @c - * run_callback. May be NULL if no clean-up is necessary. - * @param init_opaque optional user data passed to @c init_callback, may be NULL - * (unlike the return value from @c init_callback which may only be NULL if - * initialization failed). - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such - * as @ref JxlDecoderSetImageOutBuffer having already been called. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback( - JxlDecoder* dec, const JxlPixelFormat* format, - JxlImageOutInitCallback init_callback, JxlImageOutRunCallback run_callback, - JxlImageOutDestroyCallback destroy_callback, void* init_opaque); - -/** - * Returns the minimum size in bytes of an extra channel pixel buffer for the - * given format. This is the buffer for @ref JxlDecoderSetExtraChannelBuffer. - * Requires the basic image information is available in the decoder. - * - * @param dec decoder object - * @param format format of the pixels. The num_channels value is ignored and is - * always treated to be `1`. - * @param size output value, buffer size in bytes - * @param index which extra channel to get, matching the index used in @ref - * JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in - * the associated @ref JxlBasicInfo. - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * information not available yet or invalid index. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize( - const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size, - uint32_t index); - -/** - * Sets the buffer to write an extra channel to. This can be set when - * the ::JXL_DEC_FRAME or ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, - * and applies only for the current frame. The size of the buffer must be at - * least as large as given by @ref JxlDecoderExtraChannelBufferSize. The buffer - * follows the format described by @ref JxlPixelFormat, but where num_channels - * is `1`. The buffer is owned by the caller. The amount of extra channels is - * given by the num_extra_channels field in the associated @ref JxlBasicInfo, - * and the information of individual extra channels can be queried with @ref - * JxlDecoderGetExtraChannelInfo. To get multiple extra channels, this function - * must be called multiple times, once for each wanted index. Not all images - * have extra channels. The alpha channel is an extra channel and can be gotten - * as part of the color channels when using an RGBA pixel buffer with @ref - * JxlDecoderSetImageOutBuffer, but additionally also can be gotten - * separately as extra channel. The color channels themselves cannot be gotten - * this way. - * - * - * @param dec decoder object - * @param format format of the pixels. Object owned by user and its contents - * are copied internally. The num_channels value is ignored and is always - * treated to be `1`. - * @param buffer buffer type to output the pixel data to - * @param size size of buffer in bytes - * @param index which extra channel to get, matching the index used in @ref - * JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in - * the associated @ref JxlBasicInfo. - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * size too small or invalid index. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec, const JxlPixelFormat* format, - void* buffer, size_t size, uint32_t index); - -/** - * Sets output buffer for reconstructed JPEG codestream. - * - * The data is owned by the caller and may be used by the decoder until @ref - * JxlDecoderReleaseJPEGBuffer is called or the decoder is destroyed or - * reset so must be kept alive until then. - * - * If a JPEG buffer was set before and released with @ref - * JxlDecoderReleaseJPEGBuffer, bytes that the decoder has already output - * should not be included, only the remaining bytes output must be set. - * - * @param dec decoder object - * @param data pointer to next bytes to write to - * @param size amount of bytes available starting from data - * @return ::JXL_DEC_ERROR if output buffer was already set and @ref - * JxlDecoderReleaseJPEGBuffer was not called on it, ::JXL_DEC_SUCCESS - * otherwise - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, - uint8_t* data, size_t size); - -/** - * Releases buffer which was provided with @ref JxlDecoderSetJPEGBuffer. - * - * Calling @ref JxlDecoderReleaseJPEGBuffer is required whenever - * a buffer is already set and a new buffer needs to be added with @ref - * JxlDecoderSetJPEGBuffer, but is not required before @ref - * JxlDecoderDestroy or @ref JxlDecoderReset. - * - * Calling @ref JxlDecoderReleaseJPEGBuffer when no buffer is set is - * not an error and returns `0`. - * - * @param dec decoder object - * @return the amount of bytes the decoder has not yet written to of the data - * set by @ref JxlDecoderSetJPEGBuffer, or `0` if no buffer is set or @ref - * JxlDecoderReleaseJPEGBuffer was already called. - */ -JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec); - -/** - * Sets output buffer for box output codestream. - * - * The data is owned by the caller and may be used by the decoder until @ref - * JxlDecoderReleaseBoxBuffer is called or the decoder is destroyed or - * reset so must be kept alive until then. - * - * If for the current box a box buffer was set before and released with @ref - * JxlDecoderReleaseBoxBuffer, bytes that the decoder has already output - * should not be included, only the remaining bytes output must be set. - * - * The @ref JxlDecoderReleaseBoxBuffer must be used at the next ::JXL_DEC_BOX - * event or final ::JXL_DEC_SUCCESS event to compute the size of the output - * box bytes. - * - * @param dec decoder object - * @param data pointer to next bytes to write to - * @param size amount of bytes available starting from data - * @return ::JXL_DEC_ERROR if output buffer was already set and @ref - * JxlDecoderReleaseBoxBuffer was not called on it, ::JXL_DEC_SUCCESS - * otherwise - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec, - uint8_t* data, size_t size); - -/** - * Releases buffer which was provided with @ref JxlDecoderSetBoxBuffer. - * - * Calling @ref JxlDecoderReleaseBoxBuffer is required whenever - * a buffer is already set and a new buffer needs to be added with @ref - * JxlDecoderSetBoxBuffer, but is not required before @ref - * JxlDecoderDestroy or @ref JxlDecoderReset. - * - * Calling @ref JxlDecoderReleaseBoxBuffer when no buffer is set is - * not an error and returns `0`. - * - * @param dec decoder object - * @return the amount of bytes the decoder has not yet written to of the data - * set by @ref JxlDecoderSetBoxBuffer, or `0` if no buffer is set or @ref - * JxlDecoderReleaseBoxBuffer was already called. - */ -JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec); - -/** - * Configures whether to get boxes in raw mode or in decompressed mode. In raw - * mode, boxes are output as their bytes appear in the container file, which may - * be decompressed, or compressed if their type is "brob". In decompressed mode, - * "brob" boxes are decompressed with Brotli before outputting them. The size of - * the decompressed stream is not known before the decompression has already - * finished. - * - * The default mode is raw. This setting can only be changed before decoding, or - * directly after a ::JXL_DEC_BOX event, and is remembered until the decoder - * is reset or destroyed. - * - * Enabling decompressed mode requires Brotli support from the library. - * - * @param dec decoder object - * @param decompress ::JXL_TRUE to transparently decompress, ::JXL_FALSE - * to get boxes in raw mode. - * @return ::JXL_DEC_ERROR if decompressed mode is set and Brotli is not - * available, ::JXL_DEC_SUCCESS otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, - JXL_BOOL decompress); - -/** - * Outputs the type of the current box, after a ::JXL_DEC_BOX event occurred, - * as `4` characters without null termination character. In case of a compressed - * "brob" box, this will return "brob" if the decompressed argument is - * JXL_FALSE, or the underlying box type if the decompressed argument is - * JXL_TRUE. - * - * The following box types are currently described in ISO/IEC 18181-2: - * - "Exif": a box with EXIF metadata. Starts with a 4-byte tiff header offset - * (big-endian uint32) that indicates the start of the actual EXIF data - * (which starts with a tiff header). Usually the offset will be zero and the - * EXIF data starts immediately after the offset field. The Exif orientation - * should be ignored by applications; the JPEG XL codestream orientation - * takes precedence and libjxl will by default apply the correct orientation - * automatically (see @ref JxlDecoderSetKeepOrientation). - * - "xml ": a box with XML data, in particular XMP metadata. - * - "jumb": a JUMBF superbox (JPEG Universal Metadata Box Format, ISO/IEC - * 19566-5). - * - "JXL ": mandatory signature box, must come first, `12` bytes long - * including the box header - * - "ftyp": a second mandatory signature box, must come second, `20` bytes - * long including the box header - * - "jxll": a JXL level box. This indicates if the codestream is level `5` or - * level `10` compatible. If not present, it is level `5`. Level `10` allows - * more features such as very high image resolution and bit-depths above `16` - * bits per channel. Added automatically by the encoder when - * @ref JxlEncoderSetCodestreamLevel is used - * - "jxlc": a box with the image codestream, in case the codestream is not - * split across multiple boxes. The codestream contains the JPEG XL image - * itself, including the basic info such as image dimensions, ICC color - * profile, and all the pixel data of all the image frames. - * - "jxlp": a codestream box in case it is split across multiple boxes. - * The contents are the same as in case of a jxlc box, when concatenated. - * - "brob": a Brotli-compressed box, which otherwise represents an existing - * type of box such as Exif or "xml ". When @ref JxlDecoderSetDecompressBoxes - * is set to JXL_TRUE, these boxes will be transparently decompressed by the - * decoder. - * - "jxli": frame index box, can list the keyframes in case of a JPEG XL - * animation allowing the decoder to jump to individual frames more - * efficiently. - * - "jbrd": JPEG reconstruction box, contains the information required to - * byte-for-byte losslessly reconstruct a JPEG-1 image. The JPEG DCT - * coefficients (pixel content) themselves as well as the ICC profile are - * encoded in the JXL codestream (jxlc or jxlp) itself. EXIF, XMP and JUMBF - * metadata is encoded in the corresponding boxes. The jbrd box itself - * contains information such as the remaining app markers of the JPEG-1 file - * and everything else required to fit the information together into the - * exact original JPEG file. - * - * Other application-specific boxes can exist. Their typename should not begin - * with "jxl" or "JXL" or conflict with other existing typenames. - * - * The signature, jxl* and jbrd boxes are processed by the decoder and would - * typically be ignored by applications. The typical way to use this function is - * to check if an encountered box contains metadata that the application is - * interested in (e.g. EXIF or XMP metadata), in order to conditionally set a - * box buffer. - * - * @param dec decoder object - * @param type buffer to copy the type into - * @param decompressed which box type to get: JXL_FALSE to get the raw box type, - * which can be "brob", JXL_TRUE, get the underlying box type. - * @return ::JXL_DEC_SUCCESS if the value is available, ::JXL_DEC_ERROR if - * not, for example the JPEG XL file does not use the container format. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, - JxlBoxType type, - JXL_BOOL decompressed); - -/** - * Returns the size of a box as it appears in the container file, after the @ref - * JXL_DEC_BOX event. This includes all the box headers. - * - * @param dec decoder object - * @param size raw size of the box in bytes - * @return ::JXL_DEC_ERROR if no box size is available, ::JXL_DEC_SUCCESS - * otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec, - uint64_t* size); - -/** - * Returns the size of the contents of a box, after the @ref - * JXL_DEC_BOX event. This does not include any of the headers of the box. For - * compressed "brob" boxes, this is the size of the compressed content. Even - * when @ref JxlDecoderSetDecompressBoxes is enabled, the return value of - * function does not change, and the decompressed size is not known before it - * has already been decompressed and output. - * - * @param dec decoder object - * @param size size of the payload of the box in bytes - * @return @ref JXL_DEC_ERROR if no box size is available, @ref JXL_DEC_SUCCESS - * otherwise. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec, - uint64_t* size); - -/** - * Configures at which progressive steps in frame decoding these @ref - * JXL_DEC_FRAME_PROGRESSION event occurs. The default value for the level - * of detail if this function is never called is `kDC`. - * - * @param dec decoder object - * @param detail at which level of detail to trigger @ref - * JXL_DEC_FRAME_PROGRESSION - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * an invalid value for the progressive detail. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetProgressiveDetail(JxlDecoder* dec, JxlProgressiveDetail detail); - -/** - * Returns the intended downsampling ratio for the progressive frame produced - * by @ref JxlDecoderFlushImage after the latest ::JXL_DEC_FRAME_PROGRESSION - * event. - * - * @param dec decoder object - * @return The intended downsampling ratio, can be `1`, `2`, `4` or `8`. - */ -JXL_EXPORT size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec); - -/** - * Outputs progressive step towards the decoded image so far when only partial - * input was received. If the flush was successful, the buffer set with @ref - * JxlDecoderSetImageOutBuffer will contain partial image data. - * - * Can be called when @ref JxlDecoderProcessInput returns @ref - * JXL_DEC_NEED_MORE_INPUT, after the ::JXL_DEC_FRAME event already occurred - * and before the ::JXL_DEC_FULL_IMAGE event occurred for a frame. - * - * @param dec decoder object - * @return ::JXL_DEC_SUCCESS if image data was flushed to the output buffer, - * or ::JXL_DEC_ERROR when no flush was done, e.g. if not enough image - * data was available yet even for flush, or no output buffer was set yet. - * This error is not fatal, it only indicates no flushed image is available - * right now. Regular decoding can still be performed. - */ -JXL_EXPORT JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec); - -/** - * Sets the bit depth of the output buffer or callback. - * - * Can be called after @ref JxlDecoderSetImageOutBuffer or @ref - * JxlDecoderSetImageOutCallback. For float pixel data types, only the default - * ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT setting is supported. - * - * @param dec decoder object - * @param bit_depth the bit depth setting of the pixel output - * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as - * incompatible custom bit depth and pixel data type. - */ -JXL_EXPORT JxlDecoderStatus -JxlDecoderSetImageOutBitDepth(JxlDecoder* dec, const JxlBitDepth* bit_depth); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_DECODE_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/decode_cxx.h b/third_party/jpeg-xl/lib/include/jxl/decode_cxx.h deleted file mode 100644 index 6089538dbe76c..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/decode_cxx.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/// @addtogroup libjxl_cpp -/// @{ -/// -/// @file decode_cxx.h -/// @brief C++ header-only helper for @ref decode.h. -/// -/// There's no binary library associated with the header since this is a header -/// only library. - -#ifndef JXL_DECODE_CXX_H_ -#define JXL_DECODE_CXX_H_ - -#include -#include - -#include - -#ifndef __cplusplus -#error "This a C++ only header. Use jxl/decode.h from C sources." -#endif - -/// Struct to call JxlDecoderDestroy from the JxlDecoderPtr unique_ptr. -struct JxlDecoderDestroyStruct { - /// Calls @ref JxlDecoderDestroy() on the passed decoder. - void operator()(JxlDecoder* decoder) { JxlDecoderDestroy(decoder); } -}; - -/// std::unique_ptr<> type that calls JxlDecoderDestroy() when releasing the -/// decoder. -/// -/// Use this helper type from C++ sources to ensure the decoder is destroyed and -/// their internal resources released. -typedef std::unique_ptr JxlDecoderPtr; - -/// Creates an instance of JxlDecoder into a JxlDecoderPtr and initializes it. -/// -/// This function returns a unique_ptr that will call JxlDecoderDestroy() when -/// releasing the pointer. See @ref JxlDecoderCreate for details on the -/// instance creation. -/// -/// @param memory_manager custom allocator function. It may be NULL. The memory -/// manager will be copied internally. -/// @return a @c NULL JxlDecoderPtr if the instance can not be allocated or -/// initialized -/// @return initialized JxlDecoderPtr instance otherwise. -static inline JxlDecoderPtr JxlDecoderMake( - const JxlMemoryManager* memory_manager) { - return JxlDecoderPtr(JxlDecoderCreate(memory_manager)); -} - -#endif // JXL_DECODE_CXX_H_ - -/// @} diff --git a/third_party/jpeg-xl/lib/include/jxl/encode.h b/third_party/jpeg-xl/lib/include/jxl/encode.h deleted file mode 100644 index 633a98e41754e..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/encode.h +++ /dev/null @@ -1,1602 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_encoder - * @{ - * @file encode.h - * @brief Encoding API for JPEG XL. - */ - -#ifndef JXL_ENCODE_H_ -#define JXL_ENCODE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include // TODO(eustas): remove before v1.0 -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Encoder library version. - * - * @return the encoder library version as an integer: - * MAJOR_VERSION * 1000000 + MINOR_VERSION * 1000 + PATCH_VERSION. For example, - * version 1.2.3 would return 1002003. - */ -JXL_EXPORT uint32_t JxlEncoderVersion(void); - -/** - * Opaque structure that holds the JPEG XL encoder. - * - * Allocated and initialized with @ref JxlEncoderCreate(). - * Cleaned up and deallocated with @ref JxlEncoderDestroy(). - */ -typedef struct JxlEncoderStruct JxlEncoder; - -/** - * Settings and metadata for a single image frame. This includes encoder options - * for a frame such as compression quality and speed. - * - * Allocated and initialized with @ref JxlEncoderFrameSettingsCreate(). - * Cleaned up and deallocated when the encoder is destroyed with - * @ref JxlEncoderDestroy(). - */ -typedef struct JxlEncoderFrameSettingsStruct JxlEncoderFrameSettings; - -/** - * Return value for multiple encoder functions. - */ -typedef enum { - /** Function call finished successfully, or encoding is finished and there is - * nothing more to be done. - */ - JXL_ENC_SUCCESS = 0, - - /** An error occurred, for example out of memory. - */ - JXL_ENC_ERROR = 1, - - /** The encoder needs more output buffer to continue encoding. - */ - JXL_ENC_NEED_MORE_OUTPUT = 2, - -} JxlEncoderStatus; - -/** - * Error conditions: - * API usage errors have the 0x80 bit set to 1 - * Other errors have the 0x80 bit set to 0 - */ -typedef enum { - /** No error - */ - JXL_ENC_ERR_OK = 0, - - /** Generic encoder error due to unspecified cause - */ - JXL_ENC_ERR_GENERIC = 1, - - /** Out of memory - * TODO(jon): actually catch this and return this error - */ - JXL_ENC_ERR_OOM = 2, - - /** JPEG bitstream reconstruction data could not be - * represented (e.g. too much tail data) - */ - JXL_ENC_ERR_JBRD = 3, - - /** Input is invalid (e.g. corrupt JPEG file or ICC profile) - */ - JXL_ENC_ERR_BAD_INPUT = 4, - - /** The encoder doesn't (yet) support this. Either no version of libjxl - * supports this, and the API is used incorrectly, or the libjxl version - * should have been checked before trying to do this. - */ - JXL_ENC_ERR_NOT_SUPPORTED = 0x80, - - /** The encoder API is used in an incorrect way. - * In this case, a debug build of libjxl should output a specific error - * message. (if not, please open an issue about it) - */ - JXL_ENC_ERR_API_USAGE = 0x81, - -} JxlEncoderError; - -/** - * Id of encoder options for a frame. This includes options such as setting - * encoding effort/speed or overriding the use of certain coding tools, for this - * frame. This does not include non-frame related encoder options such as for - * boxes. - */ -typedef enum { - /** Sets encoder effort/speed level without affecting decoding speed. Valid - * values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon - * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise 10:glacier. - * Default: squirrel (7). - */ - JXL_ENC_FRAME_SETTING_EFFORT = 0, - - /** Sets the decoding speed tier for the provided options. Minimum is 0 - * (slowest to decode, best quality/density), and maximum is 4 (fastest to - * decode, at the cost of some quality/density). Default is 0. - */ - JXL_ENC_FRAME_SETTING_DECODING_SPEED = 1, - - /** Sets resampling option. If enabled, the image is downsampled before - * compression, and upsampled to original size in the decoder. Integer option, - * use -1 for the default behavior (resampling only applied for low quality), - * 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4 - * downsampling, 8 for 8x8 downsampling. - */ - JXL_ENC_FRAME_SETTING_RESAMPLING = 2, - - /** Similar to ::JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels. - * Integer option, use -1 for the default behavior (depends on encoder - * implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for - * 4x4 downsampling, 8 for 8x8 downsampling. - */ - JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING = 3, - - /** Indicates the frame added with @ref JxlEncoderAddImageFrame is already - * downsampled by the downsampling factor set with @ref - * JXL_ENC_FRAME_SETTING_RESAMPLING. The input frame must then be given in the - * downsampled resolution, not the full image resolution. The downsampled - * resolution is given by ceil(xsize / resampling), ceil(ysize / resampling) - * with xsize and ysize the dimensions given in the basic info, and resampling - * the factor set with ::JXL_ENC_FRAME_SETTING_RESAMPLING. - * Use 0 to disable, 1 to enable. Default value is 0. - */ - JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED = 4, - - /** Adds noise to the image emulating photographic film noise, the higher the - * given number, the grainier the image will be. As an example, a value of 100 - * gives low noise whereas a value of 3200 gives a lot of noise. The default - * value is 0. - */ - JXL_ENC_FRAME_SETTING_PHOTON_NOISE = 5, - - /** Enables adaptive noise generation. This setting is not recommended for - * use, please use ::JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for - * the default (encoder chooses), 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_NOISE = 6, - - /** Enables or disables dots generation. Use -1 for the default (encoder - * chooses), 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_DOTS = 7, - - /** Enables or disables patches generation. Use -1 for the default (encoder - * chooses), 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_PATCHES = 8, - - /** Edge preserving filter level, -1 to 3. Use -1 for the default (encoder - * chooses), 0 to 3 to set a strength. - */ - JXL_ENC_FRAME_SETTING_EPF = 9, - - /** Enables or disables the gaborish filter. Use -1 for the default (encoder - * chooses), 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_GABORISH = 10, - - /** Enables modular encoding. Use -1 for default (encoder - * chooses), 0 to enforce VarDCT mode (e.g. for photographic images), 1 to - * enforce modular mode (e.g. for lossless images). - */ - JXL_ENC_FRAME_SETTING_MODULAR = 11, - - /** Enables or disables preserving color of invisible pixels. Use -1 for the - * default (1 if lossless, 0 if lossy), 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_KEEP_INVISIBLE = 12, - - /** Determines the order in which 256x256 regions are stored in the codestream - * for progressive rendering. Use -1 for the encoder - * default, 0 for scanline order, 1 for center-first order. - */ - JXL_ENC_FRAME_SETTING_GROUP_ORDER = 13, - - /** Determines the horizontal position of center for the center-first group - * order. Use -1 to automatically use the middle of the image, 0..xsize to - * specifically set it. - */ - JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_X = 14, - - /** Determines the center for the center-first group order. Use -1 to - * automatically use the middle of the image, 0..ysize to specifically set it. - */ - JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_Y = 15, - - /** Enables or disables progressive encoding for modular mode. Use -1 for the - * encoder default, 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_RESPONSIVE = 16, - - /** Set the progressive mode for the AC coefficients of VarDCT, using spectral - * progression from the DCT coefficients. Use -1 for the encoder default, 0 to - * disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC = 17, - - /** Set the progressive mode for the AC coefficients of VarDCT, using - * quantization of the least significant bits. Use -1 for the encoder default, - * 0 to disable, 1 to enable. - */ - JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC = 18, - - /** Set the progressive mode using lower-resolution DC images for VarDCT. Use - * -1 for the encoder default, 0 to disable, 1 to have an extra 64x64 lower - * resolution pass, 2 to have a 512x512 and 64x64 lower resolution pass. - */ - JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC = 19, - - /** Use Global channel palette if the amount of colors is smaller than this - * percentage of range. Use 0-100 to set an explicit percentage, -1 to use the - * encoder default. Used for modular encoding. - */ - JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT = 20, - - /** Use Local (per-group) channel palette if the amount of colors is smaller - * than this percentage of range. Use 0-100 to set an explicit percentage, -1 - * to use the encoder default. Used for modular encoding. - */ - JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT = 21, - - /** Use color palette if amount of colors is smaller than or equal to this - * amount, or -1 to use the encoder default. Used for modular encoding. - */ - JXL_ENC_FRAME_SETTING_PALETTE_COLORS = 22, - - /** Enables or disables delta palette. Use -1 for the default (encoder - * chooses), 0 to disable, 1 to enable. Used in modular mode. - */ - JXL_ENC_FRAME_SETTING_LOSSY_PALETTE = 23, - - /** Color transform for internal encoding: -1 = default, 0=XYB, 1=none (RGB), - * 2=YCbCr. The XYB setting performs the forward XYB transform. None and - * YCbCr both perform no transform, but YCbCr is used to indicate that the - * encoded data losslessly represents YCbCr values. - */ - JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM = 24, - - /** Reversible color transform for modular encoding: -1=default, 0-41=RCT - * index, e.g. index 0 = none, index 6 = YCoCg. - * If this option is set to a non-default value, the RCT will be globally - * applied to the whole frame. - * The default behavior is to try several RCTs locally per modular group, - * depending on the speed and distance setting. - */ - JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE = 25, - - /** Group size for modular encoding: -1=default, 0=128, 1=256, 2=512, 3=1024. - */ - JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE = 26, - - /** Predictor for modular encoding. -1 = default, 0=zero, 1=left, 2=top, - * 3=avg0, 4=select, 5=gradient, 6=weighted, 7=topright, 8=topleft, - * 9=leftleft, 10=avg1, 11=avg2, 12=avg3, 13=toptop predictive average 14=mix - * 5 and 6, 15=mix everything. - */ - JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR = 27, - - /** Fraction of pixels used to learn MA trees as a percentage. -1 = default, - * 0 = no MA and fast decode, 50 = default value, 100 = all, values above - * 100 are also permitted. Higher values use more encoder memory. - */ - JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT = 28, - - /** Number of extra (previous-channel) MA tree properties to use. -1 = - * default, 0-11 = valid values. Recommended values are in the range 0 to 3, - * or 0 to amount of channels minus 1 (including all extra channels, and - * excluding color channels when using VarDCT mode). Higher value gives slower - * encoding and slower decoding. - */ - JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS = 29, - - /** Enable or disable CFL (chroma-from-luma) for lossless JPEG recompression. - * -1 = default, 0 = disable CFL, 1 = enable CFL. - */ - JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL = 30, - - /** Prepare the frame for indexing in the frame index box. - * 0 = ignore this frame (same as not setting a value), - * 1 = index this frame within the Frame Index Box. - * If any frames are indexed, the first frame needs to - * be indexed, too. If the first frame is not indexed, and - * a later frame is attempted to be indexed, ::JXL_ENC_ERROR will occur. - * If non-keyframes, i.e., frames with cropping, blending or patches are - * attempted to be indexed, ::JXL_ENC_ERROR will occur. - */ - JXL_ENC_FRAME_INDEX_BOX = 31, - - /** Sets brotli encode effort for use in JPEG recompression and - * compressed metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11 - * (slowest). Default is based on the general encode effort in case of JPEG - * recompression, and 4 for brob boxes. - */ - JXL_ENC_FRAME_SETTING_BROTLI_EFFORT = 32, - - /** Enables or disables brotli compression of metadata boxes derived from - * a JPEG frame when using @ref JxlEncoderAddJPEGFrame. This has no effect on - * boxes added using @ref JxlEncoderAddBox. -1 = default, 0 = disable - * compression, 1 = enable compression. - */ - JXL_ENC_FRAME_SETTING_JPEG_COMPRESS_BOXES = 33, - - /** Control what kind of buffering is used, when using chunked image frames. - * -1 = default (let the encoder decide) - * 0 = buffers everything, basically the same as non-streamed code path - (mainly for testing) - * 1 = buffers everything for images that are smaller than 2048 x 2048, and - * uses streaming input and output for larger images - * 2 = uses streaming input and output for all images that are larger than - * one group, i.e. 256 x 256 pixels by default - * 3 = currently same as 2 - * - * When using streaming input and output the encoder minimizes memory usage at - * the cost of compression density. Also note that images produced with - * streaming mode might not be progressively decodeable. - */ - JXL_ENC_FRAME_SETTING_BUFFERING = 34, - - /** Keep or discard Exif metadata boxes derived from a JPEG frame when using - * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1, - * this option cannot be set to 0. Even when Exif metadata is discarded, the - * orientation will still be applied. 0 = discard Exif metadata, 1 = keep Exif - * metadata (default). - */ - JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF = 35, - - /** Keep or discard XMP metadata boxes derived from a JPEG frame when using - * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1, - * this option cannot be set to 0. 0 = discard XMP metadata, 1 = keep XMP - * metadata (default). - */ - JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP = 36, - - /** Keep or discard JUMBF metadata boxes derived from a JPEG frame when using - * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * @ref JxlEncoderAddBox. 0 = discard JUMBF metadata, 1 = keep JUMBF metadata - * (default). - */ - JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF = 37, - - /** If this mode is disabled, the encoder will not make any image quality - * decisions that are computed based on the full image, but stored only once - * (e.g. the X quant multiplier in the frame header). Used mainly for testing - * equivalence of streaming and non-streaming code. - * 0 = disabled, 1 = enabled (default) - */ - JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS = 38, - - /** Disable perceptual optimizations. 0 = optimizations enabled (default), 1 = - * optimizations disabled. - */ - JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS = 39, - - /** Enum value not to be used as an option. This value is added to force the - * C compiler to have the enum to take a known size. - */ - JXL_ENC_FRAME_SETTING_FILL_ENUM = 65535, - -} JxlEncoderFrameSettingId; - -/** - * Creates an instance of @ref JxlEncoder and initializes it. - * - * @p memory_manager will be used for all the library dynamic allocations made - * from this instance. The parameter may be NULL, in which case the default - * allocator will be used. See jpegxl/memory_manager.h for details. - * - * @param memory_manager custom allocator function. It may be NULL. The memory - * manager will be copied internally. - * @return @c NULL if the instance can not be allocated or initialized - * @return pointer to initialized @ref JxlEncoder otherwise - */ -JXL_EXPORT JxlEncoder* JxlEncoderCreate(const JxlMemoryManager* memory_manager); - -/** - * Re-initializes a @ref JxlEncoder instance, so it can be re-used for encoding - * another image. All state and settings are reset as if the object was - * newly created with @ref JxlEncoderCreate, but the memory manager is kept. - * - * @param enc instance to be re-initialized. - */ -JXL_EXPORT void JxlEncoderReset(JxlEncoder* enc); - -/** - * Deinitializes and frees a @ref JxlEncoder instance. - * - * @param enc instance to be cleaned up and deallocated. - */ -JXL_EXPORT void JxlEncoderDestroy(JxlEncoder* enc); - -/** - * Sets the color management system (CMS) that will be used for color conversion - * (if applicable) during encoding. May only be set before starting encoding. If - * left unset, the default CMS implementation will be used. - * - * @param enc encoder object. - * @param cms structure representing a CMS implementation. See @ref - * JxlCmsInterface for more details. - */ -JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms); - -/** - * Set the parallel runner for multithreading. May only be set before starting - * encoding. - * - * @param enc encoder object. - * @param parallel_runner function pointer to runner for multithreading. It may - * be NULL to use the default, single-threaded, runner. A multithreaded - * runner should be set to reach fast performance. - * @param parallel_runner_opaque opaque pointer for parallel_runner. - * @return ::JXL_ENC_SUCCESS if the runner was set, ::JXL_ENC_ERROR - * otherwise (the previous runner remains set). - */ -JXL_EXPORT JxlEncoderStatus -JxlEncoderSetParallelRunner(JxlEncoder* enc, JxlParallelRunner parallel_runner, - void* parallel_runner_opaque); - -/** - * Get the (last) error code in case ::JXL_ENC_ERROR was returned. - * - * @param enc encoder object. - * @return the @ref JxlEncoderError that caused the (last) ::JXL_ENC_ERROR to - * be returned. - */ -JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc); - -/** - * Encodes a JPEG XL file using the available bytes. @p *avail_out indicates how - * many output bytes are available, and @p *next_out points to the input bytes. - * *avail_out will be decremented by the amount of bytes that have been - * processed by the encoder and *next_out will be incremented by the same - * amount, so *next_out will now point at the amount of *avail_out unprocessed - * bytes. - * - * The returned status indicates whether the encoder needs more output bytes. - * When the return value is not ::JXL_ENC_ERROR or ::JXL_ENC_SUCCESS, the - * encoding requires more @ref JxlEncoderProcessOutput calls to continue. - * - * The caller must guarantee that *avail_out >= 32 when calling - * @ref JxlEncoderProcessOutput; otherwise, ::JXL_ENC_NEED_MORE_OUTPUT will - * be returned. It is guaranteed that, if *avail_out >= 32, at least one byte of - * output will be written. - * - * This encodes the frames and/or boxes added so far. If the last frame or last - * box has been added, @ref JxlEncoderCloseInput, @ref JxlEncoderCloseFrames - * and/or @ref JxlEncoderCloseBoxes must be called before the next - * @ref JxlEncoderProcessOutput call, or the codestream won't be encoded - * correctly. - * - * @param enc encoder object. - * @param next_out pointer to next bytes to write to. - * @param avail_out amount of bytes available starting from *next_out. - * @return ::JXL_ENC_SUCCESS when encoding finished and all events handled. - * @return ::JXL_ENC_ERROR when encoding failed, e.g. invalid input. - * @return ::JXL_ENC_NEED_MORE_OUTPUT more output buffer is necessary. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, - uint8_t** next_out, - size_t* avail_out); - -/** - * Sets the frame information for this frame to the encoder. This includes - * animation information such as frame duration to store in the frame header. - * The frame header fields represent the frame as passed to the encoder, but not - * necessarily the exact values as they will be encoded file format: the encoder - * could change crop and blending options of a frame for more efficient encoding - * or introduce additional internal frames. Animation duration and time code - * information is not altered since those are immutable metadata of the frame. - * - * It is not required to use this function, however if have_animation is set - * to true in the basic info, then this function should be used to set the - * time duration of this individual frame. By default individual frames have a - * time duration of 0, making them form a composite still. See @ref - * JxlFrameHeader for more information. - * - * This information is stored in the @ref JxlEncoderFrameSettings and so is used - * for any frame encoded with these @ref JxlEncoderFrameSettings. It is ok to - * change between @ref JxlEncoderAddImageFrame calls, each added image frame - * will have the frame header that was set in the options at the time of calling - * @ref JxlEncoderAddImageFrame. - * - * The is_last and name_length fields of the @ref JxlFrameHeader are ignored, - * use - * @ref JxlEncoderCloseFrames to indicate last frame, and @ref - * JxlEncoderSetFrameName to indicate the name and its length instead. - * Calling this function will clear any name that was previously set with @ref - * JxlEncoderSetFrameName. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param frame_header frame header data to set. Object owned by the caller and - * does not need to be kept in memory, its information is copied internally. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus -JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings, - const JxlFrameHeader* frame_header); - -/** - * Sets blend info of an extra channel. The blend info of extra channels is set - * separately from that of the color channels, the color channels are set with - * @ref JxlEncoderSetFrameHeader. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param index index of the extra channel to use. - * @param blend_info blend info to set for the extra channel - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo( - JxlEncoderFrameSettings* frame_settings, size_t index, - const JxlBlendInfo* blend_info); - -/** - * Sets the name of the animation frame. This function is optional, frames are - * not required to have a name. This setting is a part of the frame header, and - * the same principles as for @ref JxlEncoderSetFrameHeader apply. The - * name_length field of @ref JxlFrameHeader is ignored by the encoder, this - * function determines the name length instead as the length in bytes of the C - * string. - * - * The maximum possible name length is 1071 bytes (excluding terminating null - * character). - * - * Calling @ref JxlEncoderSetFrameHeader clears any name that was - * previously set. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param frame_name name of the next frame to be encoded, as a UTF-8 encoded C - * string (zero terminated). Owned by the caller, and copied internally. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName( - JxlEncoderFrameSettings* frame_settings, const char* frame_name); - -/** - * Sets the bit depth of the input buffer. - * - * For float pixel formats, only the default @ref - JXL_BIT_DEPTH_FROM_PIXEL_FORMAT - * setting is allowed, while for unsigned pixel formats, - * ::JXL_BIT_DEPTH_FROM_CODESTREAM setting is also allowed. See the comment - on - * @ref JxlEncoderAddImageFrame for the effects of the bit depth setting. - - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param bit_depth the bit depth setting of the pixel input - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth( - JxlEncoderFrameSettings* frame_settings, const JxlBitDepth* bit_depth); - -/** - * Sets the buffer to read JPEG encoded bytes from for the next frame to encode. - * - * If @ref JxlEncoderSetBasicInfo has not yet been called, calling - * @ref JxlEncoderAddJPEGFrame will implicitly call it with the parameters of - * the added JPEG frame. - * - * If @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile has not - * yet been called, calling @ref JxlEncoderAddJPEGFrame will implicitly call it - * with the parameters of the added JPEG frame. - * - * If the encoder is set to store JPEG reconstruction metadata using @ref - * JxlEncoderStoreJPEGMetadata and a single JPEG frame is added, it will be - * possible to losslessly reconstruct the JPEG codestream. - * - * If this is the last frame, @ref JxlEncoderCloseInput or @ref - * JxlEncoderCloseFrames must be called before the next - * @ref JxlEncoderProcessOutput call. - * - * Note, this can only be used to add JPEG frames for lossless compression. To - * encode with lossy compression, the JPEG must be decoded manually and a pixel - * buffer added using JxlEncoderAddImageFrame. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param buffer bytes to read JPEG from. Owned by the caller and its contents - * are copied internally. - * @param size size of buffer in bytes. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus -JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings, - const uint8_t* buffer, size_t size); - -/** - * Sets the buffer to read pixels from for the next image to encode. Must call - * @ref JxlEncoderSetBasicInfo before @ref JxlEncoderAddImageFrame. - * - * Currently only some data types for pixel formats are supported: - * - ::JXL_TYPE_UINT8, with range 0..255 - * - ::JXL_TYPE_UINT16, with range 0..65535 - * - ::JXL_TYPE_FLOAT16, with nominal range 0..1 - * - ::JXL_TYPE_FLOAT, with nominal range 0..1 - * - * Note: the sample data type in pixel_format is allowed to be different from - * what is described in the @ref JxlBasicInfo. The type in pixel_format, - * together with an optional @ref JxlBitDepth parameter set by @ref - * JxlEncoderSetFrameBitDepth describes the format of the uncompressed pixel - * buffer. The bits_per_sample and exponent_bits_per_sample in the @ref - * JxlBasicInfo describes what will actually be encoded in the JPEG XL - * codestream. For example, to encode a 12-bit image, you would set - * bits_per_sample to 12, while the input frame buffer can be in the following - * formats: - * - if pixel format is in ::JXL_TYPE_UINT16 with default bit depth setting - * (i.e. ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT), input sample values are - * rescaled to 16-bit, i.e. multiplied by 65535/4095; - * - if pixel format is in ::JXL_TYPE_UINT16 with @ref - * JXL_BIT_DEPTH_FROM_CODESTREAM bit depth setting, input sample values are - * provided unscaled; - * - if pixel format is in ::JXL_TYPE_FLOAT, input sample values are - * rescaled to 0..1, i.e. multiplied by 1.f/4095.f. While it is allowed, it is - * obviously not recommended to use a pixel_format with lower precision than - * what is specified in the @ref JxlBasicInfo. - * - * We support interleaved channels as described by the @ref JxlPixelFormat - * "JxlPixelFormat": - * - single-channel data, e.g. grayscale - * - single-channel + alpha - * - trichromatic, e.g. RGB - * - trichromatic + alpha - * - * Extra channels not handled here need to be set by @ref - * JxlEncoderSetExtraChannelBuffer. - * If the image has alpha, and alpha is not passed here, it will implicitly be - * set to all-opaque (an alpha value of 1.0 everywhere). - * - * The pixels are assumed to be encoded in the original profile that is set with - * @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile. If none of - * these functions were used, the pixels are assumed to be nonlinear sRGB for - * integer data types (::JXL_TYPE_UINT8, ::JXL_TYPE_UINT16), and linear - * sRGB for floating point data types (::JXL_TYPE_FLOAT16, @ref - * JXL_TYPE_FLOAT). - * - * Sample values in floating-point pixel formats are allowed to be outside the - * nominal range, e.g. to represent out-of-sRGB-gamut colors in the - * uses_original_profile=false case. They are however not allowed to be NaN or - * +-infinity. - * - * If this is the last frame, @ref JxlEncoderCloseInput or @ref - * JxlEncoderCloseFrames must be called before the next - * @ref JxlEncoderProcessOutput call. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param pixel_format format for pixels. Object owned by the caller and its - * contents are copied internally. - * @param buffer buffer type to input the pixel data from. Owned by the caller - * and its contents are copied internally. - * @param size size of buffer in bytes. This size should match what is implied - * by the frame dimensions and the pixel format. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame( - const JxlEncoderFrameSettings* frame_settings, - const JxlPixelFormat* pixel_format, const void* buffer, size_t size); - -/** - * The @ref JxlEncoderOutputProcessor structure provides an interface for the - * encoder's output processing. Users of the library, who want to do streaming - * encoding, should implement the required callbacks for buffering, writing, - * seeking (if supported), and setting a finalized position during the encoding - * process. - * - * At a high level, the processor can be in one of two states: - * - With an active buffer: This indicates that a buffer has been acquired using - * `get_buffer` and encoded data can be written to it. - * - Without an active buffer: In this state, no data can be written. A new - * buffer must be acquired after releasing any previously active buffer. - * - * The library will not acquire more than one buffer at a given time. - * - * The state of the processor includes `position` and `finalized position`, - * which have the following meaning. - * - * - position: Represents the current position, in bytes, within the output - * stream where the encoded data will be written next. This position moves - * forward with each `release_buffer` call as data is written, and can also be - * adjusted through the optional seek callback, if provided. At this position - * the next write will occur. - * - * - finalized position: A position in the output stream that ensures all bytes - * before this point are finalized and won't be changed by later writes. - * - * All fields but `seek` are required, `seek` is optional and can be NULL. - */ -struct JxlEncoderOutputProcessor { - /** - * Required. - * An opaque pointer that the client can use to store custom data. - * This data will be passed to the associated callback functions. - */ - void* opaque; - - /** - * Required. - * Acquires a buffer at the current position into which the library will write - * the output data. - * - * If the `size` argument points to 0 and the returned value is NULL, this - * will be interpreted as asking the output writing to stop. In such a case, - * the library will return an error. The client is expected to set the size of - * the returned buffer based on the suggested `size` when this function is - * called. - * - * @param opaque user supplied parameters to the callback - * @param size points to a suggested buffer size when called; must be set to - * the size of the returned buffer once the function returns. - * @return a pointer to the acquired buffer or NULL to indicate a stop - * condition. - */ - void* (*get_buffer)(void* opaque, size_t* size); - - /** - * Required. - * Notifies the user of library that the current buffer's data has been - * written and can be released. This function should advance the current - * position of the buffer by `written_bytes` number of bytes. - * - * @param opaque user supplied parameters to the callback - * @param written_bytes the number of bytes written to the buffer. - */ - void (*release_buffer)(void* opaque, size_t written_bytes); - - /** - * Optional, can be NULL - * Seeks to a specific position in the output. This function is optional and - * can be set to NULL if the output doesn't support seeking. Can only be done - * when there is no buffer. Cannot be used to seek before the finalized - * position. - * - * @param opaque user supplied parameters to the callback - * @param position the position to seek to, in bytes. - */ - void (*seek)(void* opaque, uint64_t position); - - /** - * Required. - * Sets a finalized position on the output data, at a specific position. - * Seeking will never request a position before the finalized position. - * - * Will only be called if there is no active buffer. - * - * @param opaque user supplied parameters to the callback - * @param finalized_position the position, in bytes, where the finalized - * position should be set. - */ - void (*set_finalized_position)(void* opaque, uint64_t finalized_position); -}; - -/** - * Sets the output processor for the encoder. This processor determines how the - * encoder will handle buffering, writing, seeking (if supported), and - * setting a finalized position during the encoding process. - * - * This should not be used when using @ref JxlEncoderProcessOutput. - * - * @param enc encoder object. - * @param output_processor the struct containing the callbacks for managing - * output. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor( - JxlEncoder* enc, struct JxlEncoderOutputProcessor output_processor); - -/** - * Flushes any buffered input in the encoder, ensuring that all available input - * data has been processed and written to the output. - * - * This function can only be used after @ref JxlEncoderSetOutputProcessor. - * Before making the last call to @ref JxlEncoderFlushInput, users should call - * @ref JxlEncoderCloseInput to signal the end of input data. - * - * This should not be used when using @ref JxlEncoderProcessOutput. - * - * @param enc encoder object. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderFlushInput(JxlEncoder* enc); - -/** - * This struct provides callback functions to pass pixel data in a streaming - * manner instead of requiring the entire frame data in memory at once. - */ -struct JxlChunkedFrameInputSource { - /** - * A pointer to any user-defined data or state. This can be used to pass - * information to the callback functions. - */ - void* opaque; - - /** - * Get the pixel format that color channel data will be provided in. - * When called, `pixel_format` points to a suggested pixel format; if - * color channel data can be given in this pixel format, processing might - * be more efficient. - * - * This function will be called exactly once, before any call to - * get_color_channel_at. - * - * @param opaque user supplied parameters to the callback - * @param pixel_format format for pixels - */ - void (*get_color_channels_pixel_format)(void* opaque, - JxlPixelFormat* pixel_format); - - /** - * Callback to retrieve a rectangle of color channel data at a specific - * location. It is guaranteed that xpos and ypos are multiples of 8. xsize, - * ysize will be multiples of 8, unless the resulting rectangle would be out - * of image bounds. Moreover, xsize and ysize will be at most 2048. The - * returned data will be assumed to be in the format returned by the - * (preceding) call to get_color_channels_pixel_format, except the `align` - * parameter of the pixel format will be ignored. Instead, the `i`-th row will - * be assumed to start at position `return_value + i * *row_offset`, with the - * value of `*row_offset` decided by the callee. - * - * Note that multiple calls to `get_color_channel_data_at` may happen before a - * call to `release_buffer`. - * - * @param opaque user supplied parameters to the callback - * @param xpos horizontal position for the data. - * @param ypos vertical position for the data. - * @param xsize horizontal size of the requested rectangle of data. - * @param ysize vertical size of the requested rectangle of data. - * @param row_offset pointer to a the byte offset between consecutive rows of - * the retrieved pixel data. - * @return pointer to the retrieved pixel data. - */ - const void* (*get_color_channel_data_at)(void* opaque, size_t xpos, - size_t ypos, size_t xsize, - size_t ysize, size_t* row_offset); - - /** - * Get the pixel format that extra channel data will be provided in. - * When called, `pixel_format` points to a suggested pixel format; if - * extra channel data can be given in this pixel format, processing might - * be more efficient. - * - * This function will be called exactly once per index, before any call to - * get_extra_channel_data_at with that given index. - * - * @param opaque user supplied parameters to the callback - * @param ec_index zero-indexed index of the extra channel - * @param pixel_format format for extra channel data - */ - void (*get_extra_channel_pixel_format)(void* opaque, size_t ec_index, - JxlPixelFormat* pixel_format); - - /** - * Callback to retrieve a rectangle of extra channel `ec_index` data at a - * specific location. It is guaranteed that xpos and ypos are multiples of - * 8. xsize, ysize will be multiples of 8, unless the resulting rectangle - * would be out of image bounds. Moreover, xsize and ysize will be at most - * 2048. The returned data will be assumed to be in the format returned by the - * (preceding) call to get_extra_channels_pixel_format_at with the - * corresponding extra channel index `ec_index`, except the `align` parameter - * of the pixel format will be ignored. Instead, the `i`-th row will be - * assumed to start at position `return_value + i * *row_offset`, with the - * value of `*row_offset` decided by the callee. - * - * Note that multiple calls to `get_extra_channel_data_at` may happen before a - * call to `release_buffer`. - * - * @param opaque user supplied parameters to the callback - * @param xpos horizontal position for the data. - * @param ypos vertical position for the data. - * @param xsize horizontal size of the requested rectangle of data. - * @param ysize vertical size of the requested rectangle of data. - * @param row_offset pointer to a the byte offset between consecutive rows of - * the retrieved pixel data. - * @return pointer to the retrieved pixel data. - */ - const void* (*get_extra_channel_data_at)(void* opaque, size_t ec_index, - size_t xpos, size_t ypos, - size_t xsize, size_t ysize, - size_t* row_offset); - - /** - * Releases the buffer `buf` (obtained through a call to - * `get_color_channel_data_at` or `get_extra_channel_data_at`). This function - * will be called exactly once per call to `get_color_channel_data_at` or - * `get_extra_channel_data_at`. - * - * @param opaque user supplied parameters to the callback - * @param buf pointer returned by `get_color_channel_data_at` or - * `get_extra_channel_data_at` - */ - void (*release_buffer)(void* opaque, const void* buf); -}; - -/** - * @brief Adds a frame to the encoder using a chunked input source. - * - * This function gives a way to encode a frame by providing pixel data in a - * chunked or streaming manner, which can be especially useful when dealing with - * large images that may not fit entirely in memory or when trying to optimize - * memory usage. The input data is provided through callbacks defined in the - * @ref JxlChunkedFrameInputSource struct. Once the frame data has been - * completely retrieved, this function will flush the input and close it if it - * is the last frame. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param is_last_frame indicates if this is the last frame. - * @param chunked_frame_input struct providing callback methods for retrieving - * pixel data in chunks. - * - * @return Returns a status indicating the success or failure of adding the - * frame. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderAddChunkedFrame( - const JxlEncoderFrameSettings* frame_settings, JXL_BOOL is_last_frame, - struct JxlChunkedFrameInputSource chunked_frame_input); - -/** - * Sets the buffer to read pixels from for an extra channel at a given index. - * The index must be smaller than the num_extra_channels in the associated - * @ref JxlBasicInfo. Must call @ref JxlEncoderSetExtraChannelInfo before @ref - * JxlEncoderSetExtraChannelBuffer. - * - * TODO(firsching): mention what data types in pixel formats are supported. - * - * It is required to call this function for every extra channel, except for the - * alpha channel if that was already set through @ref JxlEncoderAddImageFrame. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param pixel_format format for pixels. Object owned by the caller and its - * contents are copied internally. The num_channels value is ignored, since the - * number of channels for an extra channel is always assumed to be one. - * @param buffer buffer type to input the pixel data from. Owned by the caller - * and its contents are copied internally. - * @param size size of buffer in bytes. This size should match what is implied - * by the frame dimensions and the pixel format. - * @param index index of the extra channel to use. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer( - const JxlEncoderFrameSettings* frame_settings, - const JxlPixelFormat* pixel_format, const void* buffer, size_t size, - uint32_t index); - -/** Adds a metadata box to the file format. @ref JxlEncoderProcessOutput must be - * used to effectively write the box to the output. @ref JxlEncoderUseBoxes must - * be enabled before using this function. - * - * Boxes allow inserting application-specific data and metadata (Exif, XML/XMP, - * JUMBF and user defined boxes). - * - * The box format follows ISO BMFF and shares features and box types with other - * image and video formats, including the Exif, XML and JUMBF boxes. The box - * format for JPEG XL is specified in ISO/IEC 18181-2. - * - * Boxes in general don't contain other boxes inside, except a JUMBF superbox. - * Boxes follow each other sequentially and are byte-aligned. If the container - * format is used, the JXL stream consists of concatenated boxes. - * It is also possible to use a direct codestream without boxes, but in that - * case metadata cannot be added. - * - * Each box generally has the following byte structure in the file: - * - 4 bytes: box size including box header (Big endian. If set to 0, an - * 8-byte 64-bit size follows instead). - * - 4 bytes: type, e.g. "JXL " for the signature box, "jxlc" for a codestream - * box. - * - N bytes: box contents. - * - * Only the box contents are provided to the contents argument of this function, - * the encoder encodes the size header itself. Most boxes are written - * automatically by the encoder as needed ("JXL ", "ftyp", "jxll", "jxlc", - * "jxlp", "jxli", "jbrd"), and this function only needs to be called to add - * optional metadata when encoding from pixels (using @ref - * JxlEncoderAddImageFrame). When recompressing JPEG files (using @ref - * JxlEncoderAddJPEGFrame), if the input JPEG contains EXIF, XMP or JUMBF - * metadata, the corresponding boxes are already added automatically. - * - * Box types are given by 4 characters. The following boxes can be added with - * this function: - * - "Exif": a box with EXIF metadata, can be added by libjxl users, or is - * automatically added when needed for JPEG reconstruction. The contents of - * this box must be prepended by a 4-byte tiff header offset, which may - * be 4 zero bytes in case the tiff header follows immediately. - * The EXIF metadata must be in sync with what is encoded in the JPEG XL - * codestream, specifically the image orientation. While this is not - * recommended in practice, in case of conflicting metadata, the JPEG XL - * codestream takes precedence. - * - "xml ": a box with XML data, in particular XMP metadata, can be added by - * libjxl users, or is automatically added when needed for JPEG reconstruction - * - "jumb": a JUMBF superbox, which can contain boxes with different types of - * metadata inside. This box type can be added by the encoder transparently, - * and other libraries to create and handle JUMBF content exist. - * - Application-specific boxes. Their typename should not begin with "jxl" or - * "JXL" or conflict with other existing typenames, and they should be - * registered with MP4RA (mp4ra.org). - * - * These boxes can be stored uncompressed or Brotli-compressed (using a "brob" - * box), depending on the compress_box parameter. - * - * @param enc encoder object. - * @param type the box type, e.g. "Exif" for EXIF metadata, "xml " for XMP or - * IPTC metadata, "jumb" for JUMBF metadata. - * @param contents the full contents of the box, for example EXIF - * data. ISO BMFF box header must not be included, only the contents. Owned by - * the caller and its contents are copied internally. - * @param size size of the box contents. - * @param compress_box Whether to compress this box as a "brob" box. Requires - * Brotli support. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error, such as - * when using this function without @ref JxlEncoderUseContainer, or adding a box - * type that would result in an invalid file format. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc, - const JxlBoxType type, - const uint8_t* contents, - size_t size, - JXL_BOOL compress_box); - -/** - * Indicates the intention to add metadata boxes. This allows @ref - * JxlEncoderAddBox to be used. When using this function, then it is required - * to use @ref JxlEncoderCloseBoxes at the end. - * - * By default the encoder assumes no metadata boxes will be added. - * - * This setting can only be set at the beginning, before encoding starts. - * - * @param enc encoder object. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderUseBoxes(JxlEncoder* enc); - -/** - * Declares that no further boxes will be added with @ref JxlEncoderAddBox. - * This function must be called after the last box is added so the encoder knows - * the stream will be finished. It is not necessary to use this function if - * @ref JxlEncoderUseBoxes is not used. Further frames may still be added. - * - * Must be called between @ref JxlEncoderAddBox of the last box - * and the next call to @ref JxlEncoderProcessOutput, or @ref - * JxlEncoderProcessOutput won't output the last box correctly. - * - * NOTE: if you don't need to close frames and boxes at separate times, you can - * use @ref JxlEncoderCloseInput instead to close both at once. - * - * @param enc encoder object. - */ -JXL_EXPORT void JxlEncoderCloseBoxes(JxlEncoder* enc); - -/** - * Declares that no frames will be added and @ref JxlEncoderAddImageFrame and - * @ref JxlEncoderAddJPEGFrame won't be called anymore. Further metadata boxes - * may still be added. This function or @ref JxlEncoderCloseInput must be called - * after adding the last frame and the next call to - * @ref JxlEncoderProcessOutput, or the frame won't be properly marked as last. - * - * NOTE: if you don't need to close frames and boxes at separate times, you can - * use @ref JxlEncoderCloseInput instead to close both at once. - * - * @param enc encoder object. - */ -JXL_EXPORT void JxlEncoderCloseFrames(JxlEncoder* enc); - -/** - * Closes any input to the encoder, equivalent to calling @ref - * JxlEncoderCloseFrames as well as calling @ref JxlEncoderCloseBoxes if needed. - * No further input of any kind may be given to the encoder, but further @ref - * JxlEncoderProcessOutput calls should be done to create the final output. - * - * The requirements of both @ref JxlEncoderCloseFrames and @ref - * JxlEncoderCloseBoxes apply to this function. Either this function or the - * other two must be called after the final frame and/or box, and the next - * @ref JxlEncoderProcessOutput call, or the codestream won't be encoded - * correctly. - * - * @param enc encoder object. - */ -JXL_EXPORT void JxlEncoderCloseInput(JxlEncoder* enc); - -/** - * Sets the original color encoding of the image encoded by this encoder. This - * is an alternative to @ref JxlEncoderSetICCProfile and only one of these two - * must be used. This one sets the color encoding as a @ref JxlColorEncoding, - * while the other sets it as ICC binary data. Must be called after @ref - * JxlEncoderSetBasicInfo. - * - * @param enc encoder object. - * @param color color encoding. Object owned by the caller and its contents are - * copied internally. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise - */ -JXL_EXPORT JxlEncoderStatus -JxlEncoderSetColorEncoding(JxlEncoder* enc, const JxlColorEncoding* color); - -/** - * Sets the original color encoding of the image encoded by this encoder as an - * ICC color profile. This is an alternative to @ref JxlEncoderSetColorEncoding - * and only one of these two must be used. This one sets the color encoding as - * ICC binary data, while the other defines it as a @ref JxlColorEncoding. Must - * be called after @ref JxlEncoderSetBasicInfo. - * - * @param enc encoder object. - * @param icc_profile bytes of the original ICC profile - * @param size size of the icc_profile buffer in bytes - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc, - const uint8_t* icc_profile, - size_t size); - -/** - * Initializes a @ref JxlBasicInfo struct to default values. - * For forwards-compatibility, this function has to be called before values - * are assigned to the struct fields. - * The default values correspond to an 8-bit RGB image, no alpha or any - * other extra channels. - * - * @param info global image metadata. Object owned by the caller. - */ -JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info); - -/** - * Initializes a @ref JxlFrameHeader struct to default values. - * For forwards-compatibility, this function has to be called before values - * are assigned to the struct fields. - * The default values correspond to a frame with no animation duration and the - * 'replace' blend mode. After using this function, For animation duration must - * be set, for composite still blend settings must be set. - * - * @param frame_header frame metadata. Object owned by the caller. - */ -JXL_EXPORT void JxlEncoderInitFrameHeader(JxlFrameHeader* frame_header); - -/** - * Initializes a @ref JxlBlendInfo struct to default values. - * For forwards-compatibility, this function has to be called before values - * are assigned to the struct fields. - * - * @param blend_info blending info. Object owned by the caller. - */ -JXL_EXPORT void JxlEncoderInitBlendInfo(JxlBlendInfo* blend_info); - -/** - * Sets the global metadata of the image encoded by this encoder. - * - * If the @ref JxlBasicInfo contains information of extra channels beyond an - * alpha channel, then @ref JxlEncoderSetExtraChannelInfo must be called between - * @ref JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to - * indicate extra channels, the value of `info.num_extra_channels` should be set - * to the number of extra channels, also counting the alpha channel if present. - * - * @param enc encoder object. - * @param info global image metadata. Object owned by the caller and its - * contents are copied internally. - * @return ::JXL_ENC_SUCCESS if the operation was successful, - * ::JXL_ENC_ERROR otherwise - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, - const JxlBasicInfo* info); - -/** - * Sets the upsampling method the decoder will use in case there are frames - * with ::JXL_ENC_FRAME_SETTING_RESAMPLING set. This is useful in combination - * with the ::JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED option, to control - * the type of upsampling that will be used. - * - * @param enc encoder object. - * @param factor upsampling factor to configure (1, 2, 4 or 8; for 1 this - * function has no effect at all) - * @param mode upsampling mode to use for this upsampling: - * -1: default (good for photographic images, no signaling overhead) - * 0: nearest neighbor (good for pixel art) - * 1: 'pixel dots' (same as NN for 2x, diamond-shaped 'pixel dots' for 4x/8x) - * @return ::JXL_ENC_SUCCESS if the operation was successful, - * ::JXL_ENC_ERROR otherwise - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetUpsamplingMode(JxlEncoder* enc, - int64_t factor, - int64_t mode); - -/** - * Initializes a @ref JxlExtraChannelInfo struct to default values. - * For forwards-compatibility, this function has to be called before values - * are assigned to the struct fields. - * The default values correspond to an 8-bit channel of the provided type. - * - * @param type type of the extra channel. - * @param info global extra channel metadata. Object owned by the caller and its - * contents are copied internally. - */ -JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type, - JxlExtraChannelInfo* info); - -/** - * Sets information for the extra channel at the given index. The index - * must be smaller than num_extra_channels in the associated @ref JxlBasicInfo. - * - * @param enc encoder object - * @param index index of the extra channel to set. - * @param info global extra channel metadata. Object owned by the caller and its - * contents are copied internally. - * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo( - JxlEncoder* enc, size_t index, const JxlExtraChannelInfo* info); - -/** - * Sets the name for the extra channel at the given index in UTF-8. The index - * must be smaller than the num_extra_channels in the associated @ref - * JxlBasicInfo. - * - * TODO(lode): remove size parameter for consistency with - * @ref JxlEncoderSetFrameName - * - * @param enc encoder object - * @param index index of the extra channel to set. - * @param name buffer with the name of the extra channel. - * @param size size of the name buffer in bytes, not counting the terminating - * character. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc, - size_t index, - const char* name, - size_t size); - -/** - * Sets a frame-specific option of integer type to the encoder options. - * The @ref JxlEncoderFrameSettingId argument determines which option is set. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param option ID of the option to set. - * @param value Integer value to set for this option. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or - * invalid integer value for the given option. If an error is returned, the - * state of the - * @ref JxlEncoderFrameSettings object is still valid and is the same as before - * this function was called. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption( - JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option, - int64_t value); - -/** - * Sets a frame-specific option of float type to the encoder options. - * The @ref JxlEncoderFrameSettingId argument determines which option is set. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param option ID of the option to set. - * @param value Float value to set for this option. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or - * invalid integer value for the given option. If an error is returned, the - * state of the - * @ref JxlEncoderFrameSettings object is still valid and is the same as before - * this function was called. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption( - JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option, - float value); - -/** Forces the encoder to use the box-based container format (BMFF) even - * when not necessary. - * - * When using @ref JxlEncoderUseBoxes, @ref JxlEncoderStoreJPEGMetadata or @ref - * JxlEncoderSetCodestreamLevel with level 10, the encoder will automatically - * also use the container format, it is not necessary to use - * @ref JxlEncoderUseContainer for those use cases. - * - * By default this setting is disabled. - * - * This setting can only be set at the beginning, before encoding starts. - * - * @param enc encoder object. - * @param use_container true if the encoder should always output the JPEG XL - * container format, false to only output it when necessary. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc, - JXL_BOOL use_container); - -/** - * Configure the encoder to store JPEG reconstruction metadata in the JPEG XL - * container. - * - * If this is set to true and a single JPEG frame is added, it will be - * possible to losslessly reconstruct the JPEG codestream. - * - * This setting can only be set at the beginning, before encoding starts. - * - * @param enc encoder object. - * @param store_jpeg_metadata true if the encoder should store JPEG metadata. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise. - */ -JXL_EXPORT JxlEncoderStatus -JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata); - -/** Sets the feature level of the JPEG XL codestream. Valid values are 5 and - * 10, or -1 (to choose automatically). Using the minimum required level, or - * level 5 in most cases, is recommended for compatibility with all decoders. - * - * Level 5: for end-user image delivery, this level is the most widely - * supported level by image decoders and the recommended level to use unless a - * level 10 feature is absolutely necessary. Supports a maximum resolution - * 268435456 pixels total with a maximum width or height of 262144 pixels, - * maximum 16-bit color channel depth, maximum 120 frames per second for - * animation, maximum ICC color profile size of 4 MiB, it allows all color - * models and extra channel types except CMYK and the JXL_CHANNEL_BLACK - * extra channel, and a maximum of 4 extra channels in addition to the 3 color - * channels. It also sets boundaries to certain internally used coding tools. - * - * Level 10: this level removes or increases the bounds of most of the level - * 5 limitations, allows CMYK color and up to 32 bits per color channel, but - * may be less widely supported. - * - * The default value is -1. This means the encoder will automatically choose - * between level 5 and level 10 based on what information is inside the @ref - * JxlBasicInfo structure. Do note that some level 10 features, particularly - * those used by animated JPEG XL codestreams, might require level 10, even - * though the @ref JxlBasicInfo only suggests level 5. In this case, the level - * must be explicitly set to 10, otherwise the encoder will return an error. - * The encoder will restrict internal encoding choices to those compatible with - * the level setting. - * - * This setting can only be set at the beginning, before encoding starts. - * - * @param enc encoder object. - * @param level the level value to set, must be -1, 5, or 10. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc, - int level); - -/** Returns the codestream level required to support the currently configured - * settings and basic info. This function can only be used at the beginning, - * before encoding starts, but after setting basic info. - * - * This does not support per-frame settings, only global configuration, such as - * the image dimensions, that are known at the time of writing the header of - * the JPEG XL file. - * - * If this returns 5, nothing needs to be done and the codestream can be - * compatible with any decoder. If this returns 10, @ref - * JxlEncoderSetCodestreamLevel has to be used to set the codestream level to - * 10, or the encoder can be configured differently to allow using the more - * compatible level 5. - * - * @param enc encoder object. - * @return -1 if no level can support the configuration (e.g. image dimensions - * larger than even level 10 supports), 5 if level 5 is supported, 10 if setting - * the codestream level to 10 is required. - * - */ -JXL_EXPORT int JxlEncoderGetRequiredCodestreamLevel(const JxlEncoder* enc); - -/** - * Enables lossless encoding. - * - * This is not an option like the others on itself, but rather while enabled it - * overrides a set of existing options (such as distance, modular mode and - * color transform) that enables bit-for-bit lossless encoding. - * - * When disabled, those options are not overridden, but since those options - * could still have been manually set to a combination that operates losslessly, - * using this function with lossless set to ::JXL_FALSE does not - * guarantee lossy encoding, though the default set of options is lossy. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param lossless whether to override options for lossless mode - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless( - JxlEncoderFrameSettings* frame_settings, JXL_BOOL lossless); - -/** - * Sets the distance level for lossy compression: target max butteraugli - * distance, lower = higher quality. Range: 0 .. 25. - * 0.0 = mathematically lossless (however, use @ref JxlEncoderSetFrameLossless - * instead to use true lossless, as setting distance to 0 alone is not the only - * requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default - * value: 1.0. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param distance the distance value to set. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameDistance( - JxlEncoderFrameSettings* frame_settings, float distance); - -/** - * Sets the distance level for lossy compression of extra channels. - * The distance is as in @ref JxlEncoderSetFrameDistance (lower = higher - * quality). If not set, or if set to the special value -1, the distance that - * was set with - * @ref JxlEncoderSetFrameDistance will be used. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param index index of the extra channel to set a distance value for. - * @param distance the distance value to set. - * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref - * JXL_ENC_ERROR otherwise. - */ -JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelDistance( - JxlEncoderFrameSettings* frame_settings, size_t index, float distance); - -/** - * Maps JPEG-style quality factor to distance. - * - * This function takes in input a JPEG-style quality factor `quality` and - * produces as output a `distance` value suitable to be used with @ref - * JxlEncoderSetFrameDistance and @ref JxlEncoderSetExtraChannelDistance. - * - * The `distance` value influences the level of compression, with lower values - * indicating higher quality: - * - 0.0 implies lossless compression (however, note that calling @ref - * JxlEncoderSetFrameLossless is required). - * - 1.0 represents a visually lossy compression, which is also the default - * setting. - * - * The `quality` parameter, ranging up to 100, is inversely related to - * 'distance': - * - A `quality` of 100.0 maps to a `distance` of 0.0 (lossless). - * - A `quality` of 90.0 corresponds to a `distance` of 1.0. - * - * Recommended Range: - * - `distance`: 0.5 to 3.0. - * - corresponding `quality`: approximately 96 to 68. - * - * Allowed Range: - * - `distance`: 0.0 to 25.0. - * - corresponding `quality`: 100.0 to 0.0. - * - * Note: the `quality` parameter has no consistent psychovisual meaning - * across different codecs and libraries. Using the mapping defined by @ref - * JxlEncoderDistanceFromQuality will result in a visual quality roughly - * equivalent to what would be obtained with `libjpeg-turbo` with the same - * `quality` parameter, but that is by no means guaranteed; do not assume that - * the same quality value will result in similar file sizes and image quality - * across different codecs. - */ -JXL_EXPORT float JxlEncoderDistanceFromQuality(float quality); - -/** - * Create a new set of encoder options, with all values initially copied from - * the @p source options, or set to default if @p source is NULL. - * - * The returned pointer is an opaque struct tied to the encoder and it will be - * deallocated by the encoder when @ref JxlEncoderDestroy() is called. For - * functions taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings, - * only @ref JxlEncoderFrameSettings created with this function for the same - * encoder instance can be used. - * - * @param enc encoder object. - * @param source source options to copy initial values from, or NULL to get - * defaults initialized to defaults. - * @return the opaque struct pointer identifying a new set of encoder options. - */ -JXL_EXPORT JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate( - JxlEncoder* enc, const JxlEncoderFrameSettings* source); - -/** - * Sets a color encoding to be sRGB. - * - * @param color_encoding color encoding instance. - * @param is_gray whether the color encoding should be gray scale or color. - */ -JXL_EXPORT void JxlColorEncodingSetToSRGB(JxlColorEncoding* color_encoding, - JXL_BOOL is_gray); - -/** - * Sets a color encoding to be linear sRGB. - * - * @param color_encoding color encoding instance. - * @param is_gray whether the color encoding should be gray scale or color. - */ -JXL_EXPORT void JxlColorEncodingSetToLinearSRGB( - JxlColorEncoding* color_encoding, JXL_BOOL is_gray); - -/** - * Enables usage of expert options. - * - * At the moment, the only expert option is setting an effort value of 11, - * which gives the best compression for pixel-lossless modes but is very slow. - * - * @param enc encoder object. - */ -JXL_EXPORT void JxlEncoderAllowExpertOptions(JxlEncoder* enc); - -/** - * Function type for @ref JxlEncoderSetDebugImageCallback. - * - * The callback may be called simultaneously by different threads when using a - * threaded parallel runner, on different debug images. - * - * @param opaque optional user data, as given to @ref - * JxlEncoderSetDebugImageCallback. - * @param label label of debug image, can be used in filenames - * @param xsize width of debug image - * @param ysize height of debug image - * @param color color encoding of debug image - * @param pixels pixel data of debug image as big-endian 16-bit unsigned - * samples. The memory is not owned by the user, and is only valid during the - * time the callback is running. - */ -typedef void (*JxlDebugImageCallback)(void* opaque, const char* label, - size_t xsize, size_t ysize, - const JxlColorEncoding* color, - const uint16_t* pixels); - -/** - * Sets the given debug image callback that will be used by the encoder to - * output various debug images during encoding. - * - * This only has any effect if the encoder was compiled with the appropriate - * debug build flags. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param callback used to return the debug image - * @param opaque user supplied parameter to the image callback - */ -JXL_EXPORT void JxlEncoderSetDebugImageCallback( - JxlEncoderFrameSettings* frame_settings, JxlDebugImageCallback callback, - void* opaque); - -/** - * Sets the given stats object for gathering various statistics during encoding. - * - * This only has any effect if the encoder was compiled with the appropriate - * debug build flags. - * - * @param frame_settings set of options and metadata for this frame. Also - * includes reference to the encoder object. - * @param stats object that can be used to query the gathered stats (created - * by @ref JxlEncoderStatsCreate) - */ -JXL_EXPORT void JxlEncoderCollectStats(JxlEncoderFrameSettings* frame_settings, - JxlEncoderStats* stats); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_ENCODE_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/encode_cxx.h b/third_party/jpeg-xl/lib/include/jxl/encode_cxx.h deleted file mode 100644 index 8e552357ac1c5..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/encode_cxx.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/// @addtogroup libjxl_cpp -///@{ -/// -/// @file encode_cxx.h -/// @brief C++ header-only helper for @ref encode.h. -/// -/// There's no binary library associated with the header since this is a header -/// only library. - -#ifndef JXL_ENCODE_CXX_H_ -#define JXL_ENCODE_CXX_H_ - -#include -#include - -#include - -#ifndef __cplusplus -#error "This a C++ only header. Use jxl/encode.h from C sources." -#endif - -/// Struct to call JxlEncoderDestroy from the JxlEncoderPtr unique_ptr. -struct JxlEncoderDestroyStruct { - /// Calls @ref JxlEncoderDestroy() on the passed encoder. - void operator()(JxlEncoder* encoder) { JxlEncoderDestroy(encoder); } -}; - -/// std::unique_ptr<> type that calls JxlEncoderDestroy() when releasing the -/// encoder. -/// -/// Use this helper type from C++ sources to ensure the encoder is destroyed and -/// their internal resources released. -typedef std::unique_ptr JxlEncoderPtr; - -/// Creates an instance of JxlEncoder into a JxlEncoderPtr and initializes it. -/// -/// This function returns a unique_ptr that will call JxlEncoderDestroy() when -/// releasing the pointer. See @ref JxlEncoderCreate for details on the -/// instance creation. -/// -/// @param memory_manager custom allocator function. It may be NULL. The memory -/// manager will be copied internally. -/// @return a @c NULL JxlEncoderPtr if the instance can not be allocated or -/// initialized -/// @return initialized JxlEncoderPtr instance otherwise. -static inline JxlEncoderPtr JxlEncoderMake( - const JxlMemoryManager* memory_manager) { - return JxlEncoderPtr(JxlEncoderCreate(memory_manager)); -} - -#endif // JXL_ENCODE_CXX_H_ - -/// @} diff --git a/third_party/jpeg-xl/lib/include/jxl/gain_map.h b/third_party/jpeg-xl/lib/include/jxl/gain_map.h deleted file mode 100644 index e1b314b56cbd9..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/gain_map.h +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_metadata - * @{ - * @file gain_map.h - * @brief Utility functions to manipulate jhgm (gain map) boxes. - */ - -#ifndef JXL_GAIN_MAP_H_ -#define JXL_GAIN_MAP_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Gain map bundle - * - * This structure is used to serialize gain map data to and from an input - * buffer. It holds pointers to sections within the buffer, and different parts - * of the gain map data such as metadata, ICC profile data, and the gain map - * itself. - * - * The pointers in this structure do not take ownership of the memory they point - * to. Instead, they reference specific locations within the provided buffer. It - * is the caller's responsibility to ensure that the buffer remains valid and is - * not deallocated as long as these pointers are in use. The structure should be - * considered as providing a view into the buffer, not as an owner of the data. - */ -typedef struct { - /** Version number of the gain map bundle. */ - uint8_t jhgm_version; - /** Size of the gain map metadata in bytes. */ - uint16_t gain_map_metadata_size; - /** Pointer to the gain map metadata, which is a binary - * blob following ISO 21496-1. This pointer references data within the input - * buffer. */ - const uint8_t* gain_map_metadata; - /** Indicates whether a color encoding is present. */ - JXL_BOOL has_color_encoding; - /** If has_color_encoding is true, this field contains the - * uncompressed color encoding data. */ - JxlColorEncoding color_encoding; - /** Size of the alternative ICC profile in bytes (compressed - * size). */ - uint32_t alt_icc_size; - /** Pointer to the compressed ICC profile. This pointer references - * data within the input buffer. */ - const uint8_t* alt_icc; - /** Size of the gain map in bytes. */ - uint32_t gain_map_size; - /** Pointer to the gain map data, which is a JPEG XL naked - * codestream. This pointer references data within the input buffer.*/ - const uint8_t* gain_map; -} JxlGainMapBundle; - -/** - * Calculates the total size required to serialize the gain map bundle into a - * binary buffer. This function accounts for all the necessary space to - * serialize fields such as gain map metadata, color encoding, compressed ICC - * profile data, and the gain map itself. - * - * @param[in] map_bundle Pointer to the JxlGainMapBundle containing all - * necessary data to compute the size. - * @param[out] bundle_size The size in bytes required to serialize the bundle. - * @return Whether setting the size was successful. - */ -JXL_EXPORT JXL_BOOL JxlGainMapGetBundleSize(const JxlGainMapBundle* map_bundle, - size_t* bundle_size); - -/** - * Serializes the gain map bundle into a preallocated buffer. The function - * ensures that all parts of the bundle such as metadata, color encoding, - * compressed ICC profile, and the gain map are correctly encoded into the - * buffer. First call `JxlGainMapGetBundleSize` to get the size needed for - * the buffer. - * - * @param[in] map_bundle Pointer to the `JxlGainMapBundle` to serialize. - * @param[out] output_buffer Pointer to the buffer where the serialized data - * will be written. - * @param[in] output_buffer_size The size of the output buffer in bytes. Must be - * large enough to hold the entire serialized data. - * @param[out] bytes_written The number of bytes written to the output buffer. - * @return Whether writing the bundle was successful. - */ -JXL_EXPORT JXL_BOOL JxlGainMapWriteBundle(const JxlGainMapBundle* map_bundle, - uint8_t* output_buffer, - size_t output_buffer_size, - size_t* bytes_written); - -/** - * Deserializes a gain map bundle from a provided buffer and populates a - * `JxlGainMapBundle` structure with the data extracted. This function assumes - * the buffer contains a valid serialized gain map bundle. After successful - * execution, the `JxlGainMapBundle` structure will reference three different - * sections within the buffer: - * - gain_map_metadata - * - alt_icc - * - gain_map - * These sections will be accompanied by their respective sizes. Users must - * ensure that the buffer remains valid as long as these pointers are in use. - * @param[in,out] map_bundle Pointer to a preallocated `JxlGainMapBundle` where - * the deserialized data will be stored. - * @param[in] input_buffer Pointer to the buffer containing the serialized gain - * map bundle data. - * @param[in] input_buffer_size The size of the input buffer in bytes. - * @param[out] bytes_read The number of bytes read from the input buffer. - * @return Whether reading the bundle was successful. - */ -JXL_EXPORT JXL_BOOL JxlGainMapReadBundle(JxlGainMapBundle* map_bundle, - const uint8_t* input_buffer, - size_t input_buffer_size, - size_t* bytes_read); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_GAIN_MAP_H_ */ - -/** @} */ diff --git a/third_party/jpeg-xl/lib/include/jxl/memory_manager.h b/third_party/jpeg-xl/lib/include/jxl/memory_manager.h deleted file mode 100644 index 05b4abe90b520..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/memory_manager.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_common - * @{ - * @file memory_manager.h - * @brief Abstraction functions used by JPEG XL to allocate memory. - */ - -#ifndef JXL_MEMORY_MANAGER_H_ -#define JXL_MEMORY_MANAGER_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Allocating function for a memory region of a given size. - * - * Allocates a contiguous memory region of size @p size bytes. The returned - * memory may not be aligned to a specific size or initialized at all. - * - * @param opaque custom memory manager handle provided by the caller. - * @param size in bytes of the requested memory region. - * @return @c NULL if the memory can not be allocated, - * @return pointer to the memory otherwise. - */ -typedef void* (*jpegxl_alloc_func)(void* opaque, size_t size); - -/** - * Deallocating function pointer type. - * - * This function @b MUST do nothing if @p address is @c NULL. - * - * @param opaque custom memory manager handle provided by the caller. - * @param address memory region pointer returned by ::jpegxl_alloc_func, or @c - * NULL. - */ -typedef void (*jpegxl_free_func)(void* opaque, void* address); - -/** - * Memory Manager struct. - * These functions, when provided by the caller, will be used to handle memory - * allocations. - */ -typedef struct JxlMemoryManagerStruct { - /** The opaque pointer that will be passed as the first parameter to all the - * functions in this struct. */ - void* opaque; - - /** Memory allocation function. This can be NULL if and only if also the - * free() member in this class is NULL. All dynamic memory will be allocated - * and freed with these functions if they are not NULL, otherwise with the - * standard malloc/free. */ - jpegxl_alloc_func alloc; - /** Free function matching the alloc() member. */ - jpegxl_free_func free; - - /* TODO(deymo): Add cache-aligned alloc/free functions here. */ -} JxlMemoryManager; - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_MEMORY_MANAGER_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h deleted file mode 100644 index 742f9598e90a5..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_threads - * @{ - */ -/** - * @file parallel_runner.h - */ - -/** API for running data operations in parallel in a multi-threaded environment. - * This module allows the JPEG XL caller to define their own way of creating and - * assigning threads. - * - * The JxlParallelRunner function type defines a parallel data processing - * runner that may be implemented by the caller to allow the library to process - * in multiple threads. The multi-threaded processing in this library only - * requires to run the same function over each number of a range, possibly - * running each call in a different thread. The JPEG XL caller is responsible - * for implementing this logic using the thread APIs available in their system. - * For convenience, a C++ implementation based on std::thread is provided in - * jpegxl/parallel_runner_thread.h (part of the jpegxl_threads library). - * - * Thread pools usually store small numbers of heterogeneous tasks in a queue. - * When tasks are identical or differ only by an integer input parameter, it is - * much faster to store just one function of an integer parameter and call it - * for each value. Conventional vector-of-tasks can be run in parallel using a - * lambda function adapter that simply calls task_funcs[task]. - * - * If no multi-threading is desired, a @c NULL value of JxlParallelRunner - * will use an internal implementation without multi-threading. - */ - -#ifndef JXL_PARALLEL_RUNNER_H_ -#define JXL_PARALLEL_RUNNER_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Return code used in the JxlParallel* functions as return value. A value - * of ::JXL_PARALLEL_RET_SUCCESS means success and any other value means error. - * The special value ::JXL_PARALLEL_RET_RUNNER_ERROR can be used by the runner - * to indicate any other error. - */ -typedef int JxlParallelRetCode; - -/** - * Code returned by the @ref JxlParallelRunInit function to indicate success. - */ -#define JXL_PARALLEL_RET_SUCCESS (0) - -/** - * Code returned by the @ref JxlParallelRunInit function to indicate a general - * error. - */ -#define JXL_PARALLEL_RET_RUNNER_ERROR (-1) - -/** - * Parallel run initialization callback. See @ref JxlParallelRunner for details. - * - * This function MUST be called by the JxlParallelRunner only once, on the - * same thread that called @ref JxlParallelRunner, before any parallel - * execution. The purpose of this call is to provide the maximum number of - * threads that the - * @ref JxlParallelRunner will use, which can be used by JPEG XL to allocate - * per-thread storage if needed. - * - * @param jpegxl_opaque the @p jpegxl_opaque handle provided to - * @ref JxlParallelRunner() must be passed here. - * @param num_threads the maximum number of threads. This value must be - * positive. - * @return 0 if the initialization process was successful. - * @return an error code if there was an error, which should be returned by - * @ref JxlParallelRunner(). - */ -typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque, - size_t num_threads); - -/** - * Parallel run data processing callback. See @ref JxlParallelRunner for - * details. - * - * This function MUST be called once for every number in the range [start_range, - * end_range) (including start_range but not including end_range) passing this - * number as the @p value. Calls for different value may be executed from - * different threads in parallel. - * - * @param jpegxl_opaque the @p jpegxl_opaque handle provided to - * @ref JxlParallelRunner() must be passed here. - * @param value the number in the range [start_range, end_range) of the call. - * @param thread_id the thread number where this function is being called from. - * This must be lower than the @p num_threads value passed to - * @ref JxlParallelRunInit. - */ -typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value, - size_t thread_id); - -/** - * JxlParallelRunner function type. A parallel runner implementation can be - * provided by a JPEG XL caller to allow running computations in multiple - * threads. This function must call the initialization function @p init in the - * same thread that called it and then call the passed @p func once for every - * number in the range [start_range, end_range) (including start_range but not - * including end_range) possibly from different multiple threads in parallel. - * - * The @ref JxlParallelRunner function does not need to be re-entrant. This - * means that the same @ref JxlParallelRunner function with the same - * runner_opaque provided parameter will not be called from the library from - * either @p init or - * @p func in the same decoder or encoder instance. However, a single decoding - * or encoding instance may call the provided @ref JxlParallelRunner multiple - * times for different parts of the decoding or encoding process. - * - * @return 0 if the @p init call succeeded (returned 0) and no other error - * occurred in the runner code. - * @return JXL_PARALLEL_RET_RUNNER_ERROR if an error occurred in the runner - * code, for example, setting up the threads. - * @return the return value of @p init() if non-zero. - */ -typedef JxlParallelRetCode (*JxlParallelRunner)( - void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, - JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); - -/* The following is an example of a @ref JxlParallelRunner that doesn't use any - * multi-threading. Note that this implementation doesn't store any state - * between multiple calls of the ExampleSequentialRunner function, so the - * runner_opaque value is not used. - - JxlParallelRetCode ExampleSequentialRunner(void* runner_opaque, - void* jpegxl_opaque, - JxlParallelRunInit init, - JxlParallelRunFunction func, - uint32_t start_range, - uint32_t end_range) { - // We only use one thread (the currently running thread). - JxlParallelRetCode init_ret = (*init)(jpegxl_opaque, 1); - if (init_ret != 0) return init_ret; - - // In case of other initialization error (for example when initializing the - // threads) one can return JXL_PARALLEL_RET_RUNNER_ERROR. - - for (uint32_t i = start_range; i < end_range; i++) { - // Every call is in the thread number 0. These don't need to be in any - // order. - (*func)(jpegxl_opaque, i, 0); - } - return JXL_PARALLEL_RET_SUCCESS; - } - */ - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_PARALLEL_RUNNER_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h deleted file mode 100644 index b0bf68091ea21..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_threads - * @{ - * @file resizable_parallel_runner.h - * @brief implementation using std::thread of a resizeable ::JxlParallelRunner. - */ - -/** Implementation of JxlParallelRunner than can be used to enable - * multithreading when using the JPEG XL library. This uses std::thread - * internally and related synchronization functions. The number of threads - * created can be changed after creation of the thread pool; the threads - * (including the main thread) are re-used for every - * ResizableParallelRunner::Runner call. Only one concurrent - * @ref JxlResizableParallelRunner call per instance is allowed at a time. - * - * This is a scalable, lower-overhead thread pool runner, especially suitable - * for data-parallel computations in the fork-join model, where clients need to - * know when all tasks have completed. - * - * Compared to the implementation in @ref thread_parallel_runner.h, this - * implementation is tuned for execution on lower-powered systems, including - * for example ARM CPUs with big.LITTLE computation models. - */ - -#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_H_ -#define JXL_RESIZABLE_PARALLEL_RUNNER_H_ - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner. - */ -JXL_THREADS_EXPORT JxlParallelRetCode JxlResizableParallelRunner( - void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, - JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); - -/** Creates the runner for @ref JxlResizableParallelRunner. Use as the opaque - * runner. The runner will execute tasks on the calling thread until - * @ref JxlResizableParallelRunnerSetThreads is called. - */ -JXL_THREADS_EXPORT void* JxlResizableParallelRunnerCreate( - const JxlMemoryManager* memory_manager); - -/** Changes the number of threads for @ref JxlResizableParallelRunner. - */ -JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads( - void* runner_opaque, size_t num_threads); - -/** Suggests a number of threads to use for an image of given size. - */ -JXL_THREADS_EXPORT uint32_t -JxlResizableParallelRunnerSuggestThreads(uint64_t xsize, uint64_t ysize); - -/** Destroys the runner created by @ref JxlResizableParallelRunnerCreate. - */ -JXL_THREADS_EXPORT void JxlResizableParallelRunnerDestroy(void* runner_opaque); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_RESIZABLE_PARALLEL_RUNNER_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner_cxx.h b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner_cxx.h deleted file mode 100644 index 344c96cc9ebf9..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner_cxx.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/// @addtogroup libjxl_cpp -/// @{ -/// -/// @file resizable_parallel_runner_cxx.h -/// @ingroup libjxl_threads -/// @brief C++ header-only helper for @ref resizable_parallel_runner.h. -/// -/// There's no binary library associated with the header since this is a header -/// only library. - -#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_ -#define JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_ - -#include -#include - -#include - -#ifndef __cplusplus -#error \ - "This a C++ only header. Use jxl/jxl_resizable_parallel_runner.h from C" \ - "sources." -#endif - -/// Struct to call JxlResizableParallelRunnerDestroy from the -/// JxlResizableParallelRunnerPtr unique_ptr. -struct JxlResizableParallelRunnerDestroyStruct { - /// Calls @ref JxlResizableParallelRunnerDestroy() on the passed runner. - void operator()(void* runner) { JxlResizableParallelRunnerDestroy(runner); } -}; - -/// std::unique_ptr<> type that calls JxlResizableParallelRunnerDestroy() when -/// releasing the runner. -/// -/// Use this helper type from C++ sources to ensure the runner is destroyed and -/// their internal resources released. -typedef std::unique_ptr - JxlResizableParallelRunnerPtr; - -/// Creates an instance of JxlResizableParallelRunner into a -/// JxlResizableParallelRunnerPtr and initializes it. -/// -/// This function returns a unique_ptr that will call -/// JxlResizableParallelRunnerDestroy() when releasing the pointer. See @ref -/// JxlResizableParallelRunnerCreate for details on the instance creation. -/// -/// @param memory_manager custom allocator function. It may be NULL. The memory -/// manager will be copied internally. -/// @return a @c NULL JxlResizableParallelRunnerPtr if the instance can not be -/// allocated or initialized -/// @return initialized JxlResizableParallelRunnerPtr instance otherwise. -static inline JxlResizableParallelRunnerPtr JxlResizableParallelRunnerMake( - const JxlMemoryManager* memory_manager) { - return JxlResizableParallelRunnerPtr( - JxlResizableParallelRunnerCreate(memory_manager)); -} - -#endif // JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_ - -/// @} diff --git a/third_party/jpeg-xl/lib/include/jxl/stats.h b/third_party/jpeg-xl/lib/include/jxl/stats.h deleted file mode 100644 index 35930b4da1547..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/stats.h +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_encoder - * @{ - * @file stats.h - * @brief API to collect various statistics from JXL encoder. - */ - -#ifndef JXL_STATS_H_ -#define JXL_STATS_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Opaque structure that holds the encoder statistics. - * - * Allocated and initialized with @ref JxlEncoderStatsCreate(). - * Cleaned up and deallocated with @ref JxlEncoderStatsDestroy(). - */ -typedef struct JxlEncoderStatsStruct JxlEncoderStats; - -/** - * Creates an instance of JxlEncoderStats and initializes it. - * - * @return pointer to initialized @ref JxlEncoderStats instance - */ -JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate(void); - -/** - * Deinitializes and frees JxlEncoderStats instance. - * - * @param stats instance to be cleaned up and deallocated. No-op if stats is - * null pointer. - */ -JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats); - -/** Data type for querying @ref JxlEncoderStats object - */ -typedef enum { - JXL_ENC_STAT_HEADER_BITS, - JXL_ENC_STAT_TOC_BITS, - JXL_ENC_STAT_DICTIONARY_BITS, - JXL_ENC_STAT_SPLINES_BITS, - JXL_ENC_STAT_NOISE_BITS, - JXL_ENC_STAT_QUANT_BITS, - JXL_ENC_STAT_MODULAR_TREE_BITS, - JXL_ENC_STAT_MODULAR_GLOBAL_BITS, - JXL_ENC_STAT_DC_BITS, - JXL_ENC_STAT_MODULAR_DC_GROUP_BITS, - JXL_ENC_STAT_CONTROL_FIELDS_BITS, - JXL_ENC_STAT_COEF_ORDER_BITS, - JXL_ENC_STAT_AC_HISTOGRAM_BITS, - JXL_ENC_STAT_AC_BITS, - JXL_ENC_STAT_MODULAR_AC_GROUP_BITS, - JXL_ENC_STAT_NUM_SMALL_BLOCKS, - JXL_ENC_STAT_NUM_DCT4X8_BLOCKS, - JXL_ENC_STAT_NUM_AFV_BLOCKS, - JXL_ENC_STAT_NUM_DCT8_BLOCKS, - JXL_ENC_STAT_NUM_DCT8X32_BLOCKS, - JXL_ENC_STAT_NUM_DCT16_BLOCKS, - JXL_ENC_STAT_NUM_DCT16X32_BLOCKS, - JXL_ENC_STAT_NUM_DCT32_BLOCKS, - JXL_ENC_STAT_NUM_DCT32X64_BLOCKS, - JXL_ENC_STAT_NUM_DCT64_BLOCKS, - JXL_ENC_STAT_NUM_BUTTERAUGLI_ITERS, - JXL_ENC_NUM_STATS, -} JxlEncoderStatsKey; - -/** Returns the value of the statistics corresponding the given key. - * - * @param stats object that was passed to the encoder with a - * @ref JxlEncoderCollectStats function - * @param key the particular statistics to query - * - * @return the value of the statistics - */ -JXL_EXPORT size_t JxlEncoderStatsGet(const JxlEncoderStats* stats, - JxlEncoderStatsKey key); - -/** Updates the values of the given stats object with that of an other. - * - * @param stats object whose values will be updated (usually added together) - * @param other stats object whose values will be merged with stats - */ -JXL_EXPORT void JxlEncoderStatsMerge(JxlEncoderStats* stats, - const JxlEncoderStats* other); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_STATS_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h deleted file mode 100644 index fbfe9e20749aa..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_threads - * @{ - * @file thread_parallel_runner.h - * @brief implementation using std::thread of a ::JxlParallelRunner. - */ - -/** Implementation of JxlParallelRunner than can be used to enable - * multithreading when using the JPEG XL library. This uses std::thread - * internally and related synchronization functions. The number of threads - * created is fixed at construction time and the threads are re-used for every - * ThreadParallelRunner::Runner call. Only one concurrent - * JxlThreadParallelRunner call per instance is allowed at a time. - * - * This is a scalable, lower-overhead thread pool runner, especially suitable - * for data-parallel computations in the fork-join model, where clients need to - * know when all tasks have completed. - * - * This thread pool can efficiently load-balance millions of tasks using an - * atomic counter, thus avoiding per-task virtual or system calls. With 48 - * hyperthreads and 1M tasks that add to an atomic counter, overall runtime is - * 10-20x higher when using std::async, and ~200x for a queue-based thread - */ - -#ifndef JXL_THREAD_PARALLEL_RUNNER_H_ -#define JXL_THREAD_PARALLEL_RUNNER_H_ - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner. - */ -JXL_THREADS_EXPORT JxlParallelRetCode JxlThreadParallelRunner( - void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, - JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); - -/** Creates the runner for @ref JxlThreadParallelRunner. Use as the opaque - * runner. - */ -JXL_THREADS_EXPORT void* JxlThreadParallelRunnerCreate( - const JxlMemoryManager* memory_manager, size_t num_worker_threads); - -/** Destroys the runner created by @ref JxlThreadParallelRunnerCreate. - */ -JXL_THREADS_EXPORT void JxlThreadParallelRunnerDestroy(void* runner_opaque); - -/** Returns a default num_worker_threads value for - * @ref JxlThreadParallelRunnerCreate. - */ -JXL_THREADS_EXPORT size_t JxlThreadParallelRunnerDefaultNumWorkerThreads(void); - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_THREAD_PARALLEL_RUNNER_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner_cxx.h b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner_cxx.h deleted file mode 100644 index 7f35ca00e2898..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner_cxx.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/// @addtogroup libjxl_cpp -/// @{ -/// -/// @file thread_parallel_runner_cxx.h -/// @brief C++ header-only helper for @ref thread_parallel_runner.h. -/// -/// There's no binary library associated with the header since this is a header -/// only library. - -#ifndef JXL_THREAD_PARALLEL_RUNNER_CXX_H_ -#define JXL_THREAD_PARALLEL_RUNNER_CXX_H_ - -#include -#include - -#include -#include - -#ifndef __cplusplus -#error \ - "This a C++ only header. Use jxl/jxl_thread_parallel_runner.h from C" \ - "sources." -#endif - -/// Struct to call JxlThreadParallelRunnerDestroy from the -/// JxlThreadParallelRunnerPtr unique_ptr. -struct JxlThreadParallelRunnerDestroyStruct { - /// Calls @ref JxlThreadParallelRunnerDestroy() on the passed runner. - void operator()(void* runner) { JxlThreadParallelRunnerDestroy(runner); } -}; - -/// std::unique_ptr<> type that calls JxlThreadParallelRunnerDestroy() when -/// releasing the runner. -/// -/// Use this helper type from C++ sources to ensure the runner is destroyed and -/// their internal resources released. -typedef std::unique_ptr - JxlThreadParallelRunnerPtr; - -/// Creates an instance of JxlThreadParallelRunner into a -/// JxlThreadParallelRunnerPtr and initializes it. -/// -/// This function returns a unique_ptr that will call -/// JxlThreadParallelRunnerDestroy() when releasing the pointer. See @ref -/// JxlThreadParallelRunnerCreate for details on the instance creation. -/// -/// @param memory_manager custom allocator function. It may be NULL. The memory -/// manager will be copied internally. -/// @param num_worker_threads the number of worker threads to create. -/// @return a @c NULL JxlThreadParallelRunnerPtr if the instance can not be -/// allocated or initialized -/// @return initialized JxlThreadParallelRunnerPtr instance otherwise. -static inline JxlThreadParallelRunnerPtr JxlThreadParallelRunnerMake( - const JxlMemoryManager* memory_manager, size_t num_worker_threads) { - return JxlThreadParallelRunnerPtr( - JxlThreadParallelRunnerCreate(memory_manager, num_worker_threads)); -} - -#endif // JXL_THREAD_PARALLEL_RUNNER_CXX_H_ - -/// @} diff --git a/third_party/jpeg-xl/lib/include/jxl/types.h b/third_party/jpeg-xl/lib/include/jxl/types.h deleted file mode 100644 index 1844375f68dac..0000000000000 --- a/third_party/jpeg-xl/lib/include/jxl/types.h +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright (c) the JPEG XL Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -/** @addtogroup libjxl_common - * @{ - * @file types.h - * @brief Data types for the JPEG XL API, for both encoding and decoding. - */ - -#ifndef JXL_TYPES_H_ -#define JXL_TYPES_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * A portable @c bool replacement. - * - * ::JXL_BOOL is a "documentation" type: actually it is @c int, but in API it - * denotes a type, whose only values are ::JXL_TRUE and ::JXL_FALSE. - */ -#define JXL_BOOL int -/** Portable @c true replacement. */ -#define JXL_TRUE 1 -/** Portable @c false replacement. */ -#define JXL_FALSE 0 -/** Converts of bool-like value to either ::JXL_TRUE or ::JXL_FALSE. */ -#define TO_JXL_BOOL(C) (!!(C) ? JXL_TRUE : JXL_FALSE) -/** Converts JXL_BOOL to C++ bool. */ -#define FROM_JXL_BOOL(C) (static_cast(C)) - -/** Data type for the sample values per channel per pixel. - */ -typedef enum { - /** Use 32-bit single-precision floating point values, with range 0.0-1.0 - * (within gamut, may go outside this range for wide color gamut). Floating - * point output, either ::JXL_TYPE_FLOAT or ::JXL_TYPE_FLOAT16, is recommended - * for HDR and wide gamut images when color profile conversion is required. */ - JXL_TYPE_FLOAT = 0, - - /** Use type uint8_t. May clip wide color gamut data. - */ - JXL_TYPE_UINT8 = 2, - - /** Use type uint16_t. May clip wide color gamut data. - */ - JXL_TYPE_UINT16 = 3, - - /** Use 16-bit IEEE 754 half-precision floating point values */ - JXL_TYPE_FLOAT16 = 5, -} JxlDataType; - -/** Ordering of multi-byte data. - */ -typedef enum { - /** Use the endianness of the system, either little endian or big endian, - * without forcing either specific endianness. Do not use if pixel data - * should be exported to a well defined format. - */ - JXL_NATIVE_ENDIAN = 0, - /** Force little endian */ - JXL_LITTLE_ENDIAN = 1, - /** Force big endian */ - JXL_BIG_ENDIAN = 2, -} JxlEndianness; - -/** Data type for the sample values per channel per pixel for the output buffer - * for pixels. This is not necessarily the same as the data type encoded in the - * codestream. The channels are interleaved per pixel. The pixels are - * organized row by row, left to right, top to bottom. - * TODO(lode): support different channel orders if needed (RGB, BGR, ...) - */ -typedef struct { - /** Amount of channels available in a pixel buffer. - * 1: single-channel data, e.g. grayscale or a single extra channel - * 2: single-channel + alpha - * 3: trichromatic, e.g. RGB - * 4: trichromatic + alpha - * TODO(lode): this needs finetuning. It is not yet defined how the user - * chooses output color space. CMYK+alpha needs 5 channels. - */ - uint32_t num_channels; - - /** Data type of each channel. - */ - JxlDataType data_type; - - /** Whether multi-byte data types are represented in big endian or little - * endian format. This applies to ::JXL_TYPE_UINT16 and ::JXL_TYPE_FLOAT. - */ - JxlEndianness endianness; - - /** Align scanlines to a multiple of align bytes, or 0 to require no - * alignment at all (which has the same effect as value 1) - */ - size_t align; -} JxlPixelFormat; - -/** Settings for the interpretation of UINT input and output buffers. - * (buffers using a FLOAT data type are not affected by this) - */ -typedef enum { - /** This is the default setting, where the encoder expects the input pixels - * to use the full range of the pixel format data type (e.g. for UINT16, the - * input range is 0 .. 65535 and the value 65535 is mapped to 1.0 when - * converting to float), and the decoder uses the full range to output - * pixels. If the bit depth in the basic info is different from this, the - * encoder expects the values to be rescaled accordingly (e.g. multiplied by - * 65535/4095 for a 12-bit image using UINT16 input data type). */ - JXL_BIT_DEPTH_FROM_PIXEL_FORMAT = 0, - - /** If this setting is selected, the encoder expects the input pixels to be - * in the range defined by the bits_per_sample value of the basic info (e.g. - * for 12-bit images using UINT16 input data types, the allowed range is - * 0 .. 4095 and the value 4095 is mapped to 1.0 when converting to float), - * and the decoder outputs pixels in this range. */ - JXL_BIT_DEPTH_FROM_CODESTREAM = 1, - - /** This setting can only be used in the decoder to select a custom range for - * pixel output */ - JXL_BIT_DEPTH_CUSTOM = 2, -} JxlBitDepthType; - -/** Data type for describing the interpretation of the input and output buffers - * in terms of the range of allowed input and output pixel values. */ -typedef struct { - /** Bit depth setting, see comment on @ref JxlBitDepthType */ - JxlBitDepthType type; - - /** Custom bits per sample */ - uint32_t bits_per_sample; - - /** Custom exponent bits per sample */ - uint32_t exponent_bits_per_sample; -} JxlBitDepth; - -/** Data type holding the 4-character type name of an ISOBMFF box. - */ -typedef char JxlBoxType[4]; - -#ifdef __cplusplus -} -#endif - -#endif /* JXL_TYPES_H_ */ - -/** @}*/ diff --git a/third_party/jpeg-xl/lib/jpegli.cmake b/third_party/jpeg-xl/lib/jpegli.cmake deleted file mode 100644 index a471c8b2abd8c..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli.cmake +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include(jxl_lists.cmake) - -set(JPEGLI_INTERNAL_LIBS - hwy - Threads::Threads - ${ATOMICS_LIBRARIES} -) - -# JPEGLIB setup -set(BITS_IN_JSAMPLE 8) -set(MEM_SRCDST_SUPPORTED 1) - -if(JPEGLI_LIBJPEG_LIBRARY_SOVERSION STREQUAL "62") - set(JPEG_LIB_VERSION 62) -elseif(JPEGLI_LIBJPEG_LIBRARY_SOVERSION STREQUAL "7") - set(JPEG_LIB_VERSION 70) -elseif(JPEGLI_LIBJPEG_LIBRARY_SOVERSION STREQUAL "8") - set(JPEG_LIB_VERSION 80) -endif() - -configure_file( - ../third_party/libjpeg-turbo/jconfig.h.in include/jpegli/jconfig.h) -configure_file( - ../third_party/libjpeg-turbo/jpeglib.h include/jpegli/jpeglib.h COPYONLY) -configure_file( - ../third_party/libjpeg-turbo/jmorecfg.h include/jpegli/jmorecfg.h COPYONLY) - -add_library(jpegli-static STATIC EXCLUDE_FROM_ALL "${JPEGXL_INTERNAL_JPEGLI_SOURCES}") -target_compile_options(jpegli-static PRIVATE "${JPEGXL_INTERNAL_FLAGS}") -target_compile_options(jpegli-static PUBLIC ${JPEGXL_COVERAGE_FLAGS}) -set_property(TARGET jpegli-static PROPERTY POSITION_INDEPENDENT_CODE ON) -target_include_directories(jpegli-static PRIVATE - "$" - "$" - "$" - "${JXL_HWY_INCLUDE_DIRS}" -) -target_include_directories(jpegli-static PUBLIC - "$" -) -target_link_libraries(jpegli-static PUBLIC ${JPEGLI_INTERNAL_LIBS}) - -# -# Tests for jpegli-static -# - -find_package(JPEG) -if(JPEG_FOUND AND BUILD_TESTING) -# TODO(eustas): merge into jxl_tests.cmake? - -add_library(jpegli_libjpeg_util-obj OBJECT - ${JPEGXL_INTERNAL_JPEGLI_LIBJPEG_HELPER_FILES} -) -target_include_directories(jpegli_libjpeg_util-obj PRIVATE - "${PROJECT_SOURCE_DIR}" - "${JPEG_INCLUDE_DIRS}" -) -target_compile_options(jpegli_libjpeg_util-obj PRIVATE - "${JPEGXL_INTERNAL_FLAGS}" "${JPEGXL_COVERAGE_FLAGS}") - -# Individual test binaries: -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests) -foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_JPEGLI_TESTS) - # The TESTNAME is the name without the extension or directory. - get_filename_component(TESTNAME ${TESTFILE} NAME_WE) - add_executable(${TESTNAME} ${TESTFILE} - $ - ${JPEGXL_INTERNAL_JPEGLI_TESTLIB_FILES} - ) - target_compile_options(${TESTNAME} PRIVATE - ${JPEGXL_INTERNAL_FLAGS} - # Add coverage flags to the test binary so code in the private headers of - # the library is also instrumented when running tests that execute it. - ${JPEGXL_COVERAGE_FLAGS} - ) - target_compile_definitions(${TESTNAME} PRIVATE - -DTEST_DATA_PATH="${JPEGXL_TEST_DATA_PATH}") - target_include_directories(${TESTNAME} PRIVATE - "${PROJECT_SOURCE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_BINARY_DIR}/include" - ) - target_link_libraries(${TESTNAME} - hwy - jpegli-static - GTest::GTest - GTest::Main - ${JPEG_LIBRARIES} - ) - set_target_properties(${TESTNAME} PROPERTIES LINK_FLAGS "${JPEGXL_COVERAGE_LINK_FLAGS}") - # Output test targets in the test directory. - set_target_properties(${TESTNAME} PROPERTIES PREFIX "tests/") - if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set_target_properties(${TESTNAME} PROPERTIES COMPILE_FLAGS "-Wno-error") - endif () - # 240 seconds because some build types (e.g. coverage) can be quite slow. - gtest_discover_tests(${TESTNAME} DISCOVERY_TIMEOUT 240) -endforeach () -endif() - -# -# Build libjpeg.so that links to libjpeg-static -# - -if (JPEGXL_ENABLE_JPEGLI_LIBJPEG AND NOT APPLE AND NOT WIN32 AND NOT EMSCRIPTEN) -add_library(jpegli-libjpeg-obj OBJECT "${JPEGXL_INTERNAL_JPEGLI_WRAPPER_SOURCES}") -target_compile_options(jpegli-libjpeg-obj PRIVATE ${JPEGXL_INTERNAL_FLAGS}) -target_compile_options(jpegli-libjpeg-obj PUBLIC ${JPEGXL_COVERAGE_FLAGS}) -set_property(TARGET jpegli-libjpeg-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_include_directories(jpegli-libjpeg-obj PRIVATE - "$" - "$" -) -target_compile_definitions(jpegli-libjpeg-obj PUBLIC - ${JPEGLI_LIBJPEG_OBJ_COMPILE_DEFINITIONS} -) -set(JPEGLI_LIBJPEG_INTERNAL_OBJECTS $) - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/jpegli) -add_library(jpeg SHARED ${JPEGLI_LIBJPEG_INTERNAL_OBJECTS}) -target_link_libraries(jpeg PUBLIC ${JPEGXL_COVERAGE_FLAGS}) -target_link_libraries(jpeg PRIVATE jpegli-static) -set_target_properties(jpeg PROPERTIES - VERSION ${JPEGLI_LIBJPEG_LIBRARY_VERSION} - SOVERSION ${JPEGLI_LIBJPEG_LIBRARY_SOVERSION} - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/jpegli" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/jpegli") - -# Add a jpeg.version file as a version script to tag symbols with the -# appropriate version number. -set_target_properties(jpeg PROPERTIES - LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version.${JPEGLI_LIBJPEG_LIBRARY_SOVERSION}) -set_property(TARGET jpeg APPEND_STRING PROPERTY - LINK_FLAGS " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version.${JPEGLI_LIBJPEG_LIBRARY_SOVERSION}") - -if (JPEGXL_INSTALL_JPEGLI_LIBJPEG) - install(TARGETS jpeg - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install( - DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/jpegli/" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -endif() - -# This hides the default visibility symbols from static libraries bundled into -# the shared library. In particular this prevents exposing symbols from hwy -# in the shared library. -if(LINKER_SUPPORT_EXCLUDE_LIBS) - set_property(TARGET jpeg APPEND_STRING PROPERTY - LINK_FLAGS " ${LINKER_EXCLUDE_LIBS_FLAG}") -endif() -endif() diff --git a/third_party/jpeg-xl/lib/jpegli/README.md b/third_party/jpeg-xl/lib/jpegli/README.md deleted file mode 100644 index 7241d1eaabfd9..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/README.md +++ /dev/null @@ -1,52 +0,0 @@ -:warning: **Important Update:** Development continues at https://github.com/google/jpegli - - -# Improved JPEG encoder and decoder implementation - -This subdirectory contains a JPEG encoder and decoder implementation that is -API and ABI compatible with libjpeg62. - -## Building - -When building the parent libjxl project, two binaries, `tools/cjpegli` and -`tools/djpegli` will be built, as well as a -`lib/jpegli/libjpeg.so.62.3.0` shared library that can be used as a drop-in -replacement for the system library with the same name. - -## Encoder improvements - -Improvements and new features used by the encoder include: - -* Support for 16-bit unsigned and 32-bit floating point input buffers. - -* Color space conversions, chroma subsampling and DCT are all done in floating - point precision, the conversion to integers happens first when producing - the final quantized DCT coefficients. - -* The desired quality can be indicated by a distance parameter that is - analogous to the distance parameter of JPEG XL. The quantization tables - are chosen based on the distance and the chroma subsampling mode, with - different positions in the quantization matrix scaling differently, and the - red and blue chrominance channels have separate quantization tables. - -* Adaptive dead-zone quantization. On noisy parts of the image, quantization - thresholds for zero coefficients are higher than on smoother parts of the - image. - -* Support for more efficient compression of JPEGs with an ICC profile - representing the XYB colorspace. These JPEGs will not be converted to the - YCbCr colorspace, but specialized quantization tables will be chosen for - the original X, Y, B channels. - -## Decoder improvements - -* Support for 16-bit unsigned and 32-bit floating point output buffers. - -* Non-zero DCT coefficients are dequantized to the expectation value of their - respective quantization intervals assuming a Laplacian distribution of the - original unquantized DCT coefficients. - -* After dequantization, inverse DCT, chroma upsampling and color space - conversions are all done in floating point precision, the conversion to - integer samples happens only in the final output phase (unless output to - floating point was requested). diff --git a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc deleted file mode 100644 index 11367ee66083f..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/adaptive_quantization.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/adaptive_quantization.cc" -#include -#include - -#include "lib/jpegli/encode_internal.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { -namespace { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::AbsDiff; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::And; -using hwy::HWY_NAMESPACE::Div; -using hwy::HWY_NAMESPACE::Floor; -using hwy::HWY_NAMESPACE::GetLane; -using hwy::HWY_NAMESPACE::Max; -using hwy::HWY_NAMESPACE::Min; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::NegMulAdd; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::ShiftLeft; -using hwy::HWY_NAMESPACE::ShiftRight; -using hwy::HWY_NAMESPACE::Sqrt; -using hwy::HWY_NAMESPACE::Sub; -using hwy::HWY_NAMESPACE::ZeroIfNegative; - -constexpr float kInputScaling = 1.0f / 255.0f; - -// Primary template: default to actual division. -template -struct FastDivision { - HWY_INLINE V operator()(const V n, const V d) const { return n / d; } -}; -// Partial specialization for float vectors. -template -struct FastDivision { - // One Newton-Raphson iteration. - static HWY_INLINE V ReciprocalNR(const V x) { - const auto rcp = ApproximateReciprocal(x); - const auto sum = Add(rcp, rcp); - const auto x_rcp = Mul(x, rcp); - return NegMulAdd(x_rcp, rcp, sum); - } - - V operator()(const V n, const V d) const { -#if JXL_TRUE // Faster on SKX - return Div(n, d); -#else - return n * ReciprocalNR(d); -#endif - } -}; - -// Approximates smooth functions via rational polynomials (i.e. dividing two -// polynomials). Evaluates polynomials via Horner's scheme, which is faster than -// Clenshaw recurrence for Chebyshev polynomials. LoadDup128 allows us to -// specify constants (replicated 4x) independently of the lane count. -template -HWY_INLINE HWY_MAYBE_UNUSED V EvalRationalPolynomial(const D d, const V x, - const T (&p)[NP], - const T (&q)[NQ]) { - constexpr size_t kDegP = NP / 4 - 1; - constexpr size_t kDegQ = NQ / 4 - 1; - auto yp = LoadDup128(d, &p[kDegP * 4]); - auto yq = LoadDup128(d, &q[kDegQ * 4]); - // We use pointer arithmetic to refer to &p[(kDegP - n) * 4] to avoid a - // compiler warning that the index is out of bounds since we are already - // checking that it is not out of bounds with (kDegP >= n) and the access - // will be optimized away. Similarly with q and kDegQ. - HWY_FENCE; - if (kDegP >= 1) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 1) * 4))); - if (kDegQ >= 1) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 1) * 4))); - HWY_FENCE; - if (kDegP >= 2) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 2) * 4))); - if (kDegQ >= 2) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 2) * 4))); - HWY_FENCE; - if (kDegP >= 3) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 3) * 4))); - if (kDegQ >= 3) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 3) * 4))); - HWY_FENCE; - if (kDegP >= 4) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 4) * 4))); - if (kDegQ >= 4) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 4) * 4))); - HWY_FENCE; - if (kDegP >= 5) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 5) * 4))); - if (kDegQ >= 5) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 5) * 4))); - HWY_FENCE; - if (kDegP >= 6) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 6) * 4))); - if (kDegQ >= 6) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 6) * 4))); - HWY_FENCE; - if (kDegP >= 7) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 7) * 4))); - if (kDegQ >= 7) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 7) * 4))); - - return FastDivision()(yp, yq); -} - -// Computes base-2 logarithm like std::log2. Undefined if negative / NaN. -// L1 error ~3.9E-6 -template -V FastLog2f(const DF df, V x) { - // 2,2 rational polynomial approximation of std::log1p(x) / std::log(2). - HWY_ALIGN const float p[4 * (2 + 1)] = {HWY_REP4(-1.8503833400518310E-06f), - HWY_REP4(1.4287160470083755E+00f), - HWY_REP4(7.4245873327820566E-01f)}; - HWY_ALIGN const float q[4 * (2 + 1)] = {HWY_REP4(9.9032814277590719E-01f), - HWY_REP4(1.0096718572241148E+00f), - HWY_REP4(1.7409343003366853E-01f)}; - - const Rebind di; - const auto x_bits = BitCast(di, x); - - // Range reduction to [-1/3, 1/3] - 3 integer, 2 float ops - const auto exp_bits = Sub(x_bits, Set(di, 0x3f2aaaab)); // = 2/3 - // Shifted exponent = log2; also used to clear mantissa. - const auto exp_shifted = ShiftRight<23>(exp_bits); - const auto mantissa = BitCast(df, Sub(x_bits, ShiftLeft<23>(exp_shifted))); - const auto exp_val = ConvertTo(df, exp_shifted); - return Add(EvalRationalPolynomial(df, Sub(mantissa, Set(df, 1.0f)), p, q), - exp_val); -} - -// max relative error ~3e-7 -template -V FastPow2f(const DF df, V x) { - const Rebind di; - auto floorx = Floor(x); - auto exp = - BitCast(df, ShiftLeft<23>(Add(ConvertTo(di, floorx), Set(di, 127)))); - auto frac = Sub(x, floorx); - auto num = Add(frac, Set(df, 1.01749063e+01)); - num = MulAdd(num, frac, Set(df, 4.88687798e+01)); - num = MulAdd(num, frac, Set(df, 9.85506591e+01)); - num = Mul(num, exp); - auto den = MulAdd(frac, Set(df, 2.10242958e-01), Set(df, -2.22328856e-02)); - den = MulAdd(den, frac, Set(df, -1.94414990e+01)); - den = MulAdd(den, frac, Set(df, 9.85506633e+01)); - return Div(num, den); -} - -inline float FastPow2f(float f) { - HWY_CAPPED(float, 1) D; - return GetLane(FastPow2f(D, Set(D, f))); -} - -// The following functions modulate an exponent (out_val) and return the updated -// value. Their descriptor is limited to 8 lanes for 8x8 blocks. - -template -V ComputeMask(const D d, const V out_val) { - const auto kBase = Set(d, -0.74174993f); - const auto kMul4 = Set(d, 3.2353257320940401f); - const auto kMul2 = Set(d, 12.906028311180409f); - const auto kOffset2 = Set(d, 305.04035728311436f); - const auto kMul3 = Set(d, 5.0220313103171232f); - const auto kOffset3 = Set(d, 2.1925739705298404f); - const auto kOffset4 = Mul(Set(d, 0.25f), kOffset3); - const auto kMul0 = Set(d, 0.74760422233706747f); - const auto k1 = Set(d, 1.0f); - - // Avoid division by zero. - const auto v1 = Max(Mul(out_val, kMul0), Set(d, 1e-3f)); - const auto v2 = Div(k1, Add(v1, kOffset2)); - const auto v3 = Div(k1, MulAdd(v1, v1, kOffset3)); - const auto v4 = Div(k1, MulAdd(v1, v1, kOffset4)); - // TODO(jyrki): - // A log or two here could make sense. In butteraugli we have effectively - // log(log(x + C)) for this kind of use, as a single log is used in - // saturating visual masking and here the modulation values are exponential, - // another log would counter that. - return Add(kBase, MulAdd(kMul4, v4, MulAdd(kMul2, v2, Mul(kMul3, v3)))); -} - -// mul and mul2 represent a scaling difference between jxl and butteraugli. -const float kSGmul = 226.0480446705883f; -const float kSGmul2 = 1.0f / 73.377132366608819f; -const float kLog2 = 0.693147181f; -// Includes correction factor for std::log -> log2. -const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2; -const float kSGVOffset = 7.14672470003f; - -template -V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { - // The opsin space in jxl is the cubic root of photons, i.e., v * v * v - // is related to the number of photons. - // - // SimpleGamma(v * v * v) is the psychovisual space in butteraugli. - // This ratio allows quantization to move from jxl's opsin space to - // butteraugli's log-gamma space. - static const float kEpsilon = 1e-2; - static const float kNumOffset = kEpsilon / kInputScaling / kInputScaling; - static const float kNumMul = kSGRetMul * 3 * kSGmul; - static const float kVOffset = (kSGVOffset * kLog2 + kEpsilon) / kInputScaling; - static const float kDenMul = kLog2 * kSGmul * kInputScaling * kInputScaling; - - v = ZeroIfNegative(v); - const auto num_mul = Set(d, kNumMul); - const auto num_offset = Set(d, kNumOffset); - const auto den_offset = Set(d, kVOffset); - const auto den_mul = Set(d, kDenMul); - - const auto v2 = Mul(v, v); - - const auto num = MulAdd(num_mul, v2, num_offset); - const auto den = MulAdd(Mul(den_mul, v), v2, den_offset); - return invert ? Div(num, den) : Div(den, num); -} - -template -float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) { - using DScalar = HWY_CAPPED(float, 1); - auto vscalar = Load(DScalar(), &v); - return GetLane( - RatioOfDerivativesOfCubicRootToSimpleGamma(DScalar(), vscalar)); -} - -// TODO(veluca): this function computes an approximation of the derivative of -// SimpleGamma with (f(x+eps)-f(x))/eps. Consider two-sided approximation or -// exact derivatives. For reference, SimpleGamma was: -/* -template -V SimpleGamma(const D d, V v) { - // A simple HDR compatible gamma function. - const auto mul = Set(d, kSGmul); - const auto kRetMul = Set(d, kSGRetMul); - const auto kRetAdd = Set(d, kSGmul2 * -20.2789020414f); - const auto kVOffset = Set(d, kSGVOffset); - - v *= mul; - - // This should happen rarely, but may lead to a NaN, which is rather - // undesirable. Since negative photons don't exist we solve the NaNs by - // clamping here. - // TODO(veluca): with FastLog2f, this no longer leads to NaNs. - v = ZeroIfNegative(v); - return kRetMul * FastLog2f(d, v + kVOffset) + kRetAdd; -} -*/ - -template -V GammaModulation(const D d, const size_t x, const size_t y, - const RowBuffer& input, const V out_val) { - static const float kBias = 0.16f / kInputScaling; - static const float kScale = kInputScaling / 64.0f; - auto overall_ratio = Zero(d); - const auto bias = Set(d, kBias); - const auto scale = Set(d, kScale); - const float* const JXL_RESTRICT block_start = input.Row(y) + x; - for (size_t dy = 0; dy < 8; ++dy) { - const float* const JXL_RESTRICT row_in = block_start + dy * input.stride(); - for (size_t dx = 0; dx < 8; dx += Lanes(d)) { - const auto iny = Add(Load(d, row_in + dx), bias); - const auto ratio_g = - RatioOfDerivativesOfCubicRootToSimpleGamma(d, iny); - overall_ratio = Add(overall_ratio, ratio_g); - } - } - overall_ratio = Mul(SumOfLanes(d, overall_ratio), scale); - // ideally -1.0, but likely optimal correction adds some entropy, so slightly - // less than that. - // ln(2) constant folded in because we want std::log but have FastLog2f. - const auto kGamma = Set(d, -0.15526878023684174f * 0.693147180559945f); - return MulAdd(kGamma, FastLog2f(d, overall_ratio), out_val); -} - -// Change precision in 8x8 blocks that have high frequency content. -template -V HfModulation(const D d, const size_t x, const size_t y, - const RowBuffer& input, const V out_val) { - // Zero out the invalid differences for the rightmost value per row. - const Rebind du; - HWY_ALIGN constexpr uint32_t kMaskRight[8] = {~0u, ~0u, ~0u, ~0u, - ~0u, ~0u, ~0u, 0}; - - auto sum = Zero(d); // sum of absolute differences with right and below - static const float kSumCoeff = -2.0052193233688884f * kInputScaling / 112.0; - auto sumcoeff = Set(d, kSumCoeff); - - const float* const JXL_RESTRICT block_start = input.Row(y) + x; - for (size_t dy = 0; dy < 8; ++dy) { - const float* JXL_RESTRICT row_in = block_start + dy * input.stride(); - const float* JXL_RESTRICT row_in_next = - dy == 7 ? row_in : row_in + input.stride(); - - for (size_t dx = 0; dx < 8; dx += Lanes(d)) { - const auto p = Load(d, row_in + dx); - const auto pr = LoadU(d, row_in + dx + 1); - const auto mask = BitCast(d, Load(du, kMaskRight + dx)); - sum = Add(sum, And(mask, AbsDiff(p, pr))); - const auto pd = Load(d, row_in_next + dx); - sum = Add(sum, AbsDiff(p, pd)); - } - } - - sum = SumOfLanes(d, sum); - return MulAdd(sum, sumcoeff, out_val); -} - -void PerBlockModulations(const float y_quant_01, const RowBuffer& input, - const size_t yb0, const size_t yblen, - RowBuffer* aq_map) { - static const float kAcQuant = 0.841f; - float base_level = 0.48f * kAcQuant; - float kDampenRampStart = 9.0f; - float kDampenRampEnd = 65.0f; - float dampen = 1.0f; - if (y_quant_01 >= kDampenRampStart) { - dampen = 1.0f - ((y_quant_01 - kDampenRampStart) / - (kDampenRampEnd - kDampenRampStart)); - if (dampen < 0) { - dampen = 0; - } - } - const float mul = kAcQuant * dampen; - const float add = (1.0f - dampen) * base_level; - for (size_t iy = 0; iy < yblen; iy++) { - const size_t yb = yb0 + iy; - const size_t y = yb * 8; - float* const JXL_RESTRICT row_out = aq_map->Row(yb); - const HWY_CAPPED(float, 8) df; - for (size_t ix = 0; ix < aq_map->xsize(); ix++) { - size_t x = ix * 8; - auto out_val = Set(df, row_out[ix]); - out_val = ComputeMask(df, out_val); - out_val = HfModulation(df, x, y, input, out_val); - out_val = GammaModulation(df, x, y, input, out_val); - // We want multiplicative quantization field, so everything - // until this point has been modulating the exponent. - row_out[ix] = FastPow2f(GetLane(out_val) * 1.442695041f) * mul + add; - } - } -} - -template -V MaskingSqrt(const D d, V v) { - static const float kLogOffset = 28; - static const float kMul = 211.50759899638012f; - const auto mul_v = Set(d, kMul * 1e8); - const auto offset_v = Set(d, kLogOffset); - return Mul(Set(d, 0.25f), Sqrt(MulAdd(v, Sqrt(mul_v), offset_v))); -} - -template -void Sort4(V& min0, V& min1, V& min2, V& min3) { - const auto tmp0 = Min(min0, min1); - const auto tmp1 = Max(min0, min1); - const auto tmp2 = Min(min2, min3); - const auto tmp3 = Max(min2, min3); - const auto tmp4 = Max(tmp0, tmp2); - const auto tmp5 = Min(tmp1, tmp3); - min0 = Min(tmp0, tmp2); - min1 = Min(tmp4, tmp5); - min2 = Max(tmp4, tmp5); - min3 = Max(tmp1, tmp3); -} - -template -void UpdateMin4(const V v, V& min0, V& min1, V& min2, V& min3) { - const auto tmp0 = Max(min0, v); - const auto tmp1 = Max(min1, tmp0); - const auto tmp2 = Max(min2, tmp1); - min0 = Min(min0, v); - min1 = Min(min1, tmp0); - min2 = Min(min2, tmp1); - min3 = Min(min3, tmp2); -} - -// Computes a linear combination of the 4 lowest values of the 3x3 neighborhood -// of each pixel. Output is downsampled 2x. -void FuzzyErosion(const RowBuffer& pre_erosion, const size_t yb0, - const size_t yblen, RowBuffer* tmp, - RowBuffer* aq_map) { - int xsize_blocks = aq_map->xsize(); - int xsize = pre_erosion.xsize(); - HWY_FULL(float) d; - const auto mul0 = Set(d, 0.125f); - const auto mul1 = Set(d, 0.075f); - const auto mul2 = Set(d, 0.06f); - const auto mul3 = Set(d, 0.05f); - for (size_t iy = 0; iy < 2 * yblen; ++iy) { - size_t y = 2 * yb0 + iy; - const float* JXL_RESTRICT rowt = pre_erosion.Row(y - 1); - const float* JXL_RESTRICT rowm = pre_erosion.Row(y); - const float* JXL_RESTRICT rowb = pre_erosion.Row(y + 1); - float* row_out = tmp->Row(y); - for (int x = 0; x < xsize; x += Lanes(d)) { - int xm1 = x - 1; - int xp1 = x + 1; - auto min0 = LoadU(d, rowm + x); - auto min1 = LoadU(d, rowm + xm1); - auto min2 = LoadU(d, rowm + xp1); - auto min3 = LoadU(d, rowt + xm1); - Sort4(min0, min1, min2, min3); - UpdateMin4(LoadU(d, rowt + x), min0, min1, min2, min3); - UpdateMin4(LoadU(d, rowt + xp1), min0, min1, min2, min3); - UpdateMin4(LoadU(d, rowb + xm1), min0, min1, min2, min3); - UpdateMin4(LoadU(d, rowb + x), min0, min1, min2, min3); - UpdateMin4(LoadU(d, rowb + xp1), min0, min1, min2, min3); - const auto v = Add(Add(Mul(mul0, min0), Mul(mul1, min1)), - Add(Mul(mul2, min2), Mul(mul3, min3))); - Store(v, d, row_out + x); - } - if (iy % 2 == 1) { - const float* JXL_RESTRICT row_out0 = tmp->Row(y - 1); - float* JXL_RESTRICT aq_out = aq_map->Row(yb0 + iy / 2); - for (int bx = 0, x = 0; bx < xsize_blocks; ++bx, x += 2) { - aq_out[bx] = - (row_out[x] + row_out[x + 1] + row_out0[x] + row_out0[x + 1]); - } - } - } -} - -void ComputePreErosion(const RowBuffer& input, const size_t xsize, - const size_t y0, const size_t ylen, int border, - float* diff_buffer, RowBuffer* pre_erosion) { - const size_t xsize_out = xsize / 4; - const size_t y0_out = y0 / 4; - - // The XYB gamma is 3.0 to be able to decode faster with two muls. - // Butteraugli's gamma is matching the gamma of human eye, around 2.6. - // We approximate the gamma difference by adding one cubic root into - // the adaptive quantization. This gives us a total gamma of 2.6666 - // for quantization uses. - static const float match_gamma_offset = 0.019 / kInputScaling; - - const HWY_CAPPED(float, 8) df; - - static const float limit = 0.2f; - // Computes image (padded to multiple of 8x8) of local pixel differences. - // Subsample both directions by 4. - for (size_t iy = 0; iy < ylen; ++iy) { - size_t y = y0 + iy; - const float* row_in = input.Row(y); - const float* row_in1 = input.Row(y + 1); - const float* row_in2 = input.Row(y - 1); - float* JXL_RESTRICT row_out = diff_buffer; - const auto match_gamma_offset_v = Set(df, match_gamma_offset); - const auto quarter = Set(df, 0.25f); - for (size_t x = 0; x < xsize; x += Lanes(df)) { - const auto in = LoadU(df, row_in + x); - const auto in_r = LoadU(df, row_in + x + 1); - const auto in_l = LoadU(df, row_in + x - 1); - const auto in_t = LoadU(df, row_in2 + x); - const auto in_b = LoadU(df, row_in1 + x); - const auto base = Mul(quarter, Add(Add(in_r, in_l), Add(in_t, in_b))); - const auto gammacv = - RatioOfDerivativesOfCubicRootToSimpleGamma( - df, Add(in, match_gamma_offset_v)); - auto diff = Mul(gammacv, Sub(in, base)); - diff = Mul(diff, diff); - diff = Min(diff, Set(df, limit)); - diff = MaskingSqrt(df, diff); - if ((iy & 3) != 0) { - diff = Add(diff, LoadU(df, row_out + x)); - } - StoreU(diff, df, row_out + x); - } - if (iy % 4 == 3) { - size_t y_out = y0_out + iy / 4; - float* row_d_out = pre_erosion->Row(y_out); - for (size_t x = 0; x < xsize_out; x++) { - row_d_out[x] = (row_out[x * 4] + row_out[x * 4 + 1] + - row_out[x * 4 + 2] + row_out[x * 4 + 3]) * - 0.25f; - } - pre_erosion->PadRow(y_out, xsize_out, border); - } - } -} - -} // namespace - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { -HWY_EXPORT(ComputePreErosion); -HWY_EXPORT(FuzzyErosion); -HWY_EXPORT(PerBlockModulations); - -namespace { - -constexpr int kPreErosionBorder = 1; - -} // namespace - -void ComputeAdaptiveQuantField(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - if (!m->use_adaptive_quantization) { - return; - } - int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0; - jpeg_component_info* y_comp = &cinfo->comp_info[y_channel]; - int y_quant_01 = cinfo->quant_tbl_ptrs[y_comp->quant_tbl_no]->quantval[1]; - if (m->next_iMCU_row == 0) { - m->input_buffer[y_channel].CopyRow(-1, 0, 1); - } - if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) { - size_t last_row = m->ysize_blocks * DCTSIZE - 1; - m->input_buffer[y_channel].CopyRow(last_row + 1, last_row, 1); - } - const RowBuffer& input = m->input_buffer[y_channel]; - const size_t xsize_blocks = y_comp->width_in_blocks; - const size_t xsize = xsize_blocks * DCTSIZE; - const size_t yb0 = m->next_iMCU_row * cinfo->max_v_samp_factor; - const size_t yblen = cinfo->max_v_samp_factor; - size_t y0 = yb0 * DCTSIZE; - size_t ylen = cinfo->max_v_samp_factor * DCTSIZE; - if (y0 == 0) { - ylen += 4; - } else { - y0 += 4; - } - if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) { - ylen -= 4; - } - HWY_DYNAMIC_DISPATCH(ComputePreErosion) - (input, xsize, y0, ylen, kPreErosionBorder, m->diff_buffer, &m->pre_erosion); - if (y0 == 0) { - m->pre_erosion.CopyRow(-1, 0, kPreErosionBorder); - } - if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) { - size_t last_row = m->ysize_blocks * 2 - 1; - m->pre_erosion.CopyRow(last_row + 1, last_row, kPreErosionBorder); - } - HWY_DYNAMIC_DISPATCH(FuzzyErosion) - (m->pre_erosion, yb0, yblen, &m->fuzzy_erosion_tmp, &m->quant_field); - HWY_DYNAMIC_DISPATCH(PerBlockModulations) - (y_quant_01, input, yb0, yblen, &m->quant_field); - for (int y = 0; y < cinfo->max_v_samp_factor; ++y) { - float* row = m->quant_field.Row(yb0 + y); - for (size_t x = 0; x < xsize_blocks; ++x) { - row[x] = std::max(0.0f, (0.6f / row[x]) - 1.0f); - } - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.h b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.h deleted file mode 100644 index d8537e85df2a0..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ADAPTIVE_QUANTIZATION_H_ -#define LIB_JPEGLI_ADAPTIVE_QUANTIZATION_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void ComputeAdaptiveQuantField(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_ADAPTIVE_QUANTIZATION_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/bit_writer.cc b/third_party/jpeg-xl/lib/jpegli/bit_writer.cc deleted file mode 100644 index 9788f35b8d405..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/bit_writer.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/bit_writer.h" - -#include "lib/jpegli/encode_internal.h" - -namespace jpegli { - -void JpegBitWriterInit(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - JpegBitWriter* bw = &m->bw; - size_t buffer_size = m->blocks_per_iMCU_row * (DCTSIZE2 * 16 + 8) + (1 << 16); - bw->cinfo = cinfo; - bw->data = Allocate(cinfo, buffer_size, JPOOL_IMAGE); - bw->len = buffer_size; - bw->pos = 0; - bw->output_pos = 0; - bw->put_buffer = 0; - bw->free_bits = 64; - bw->healthy = true; -} - -bool EmptyBitWriterBuffer(JpegBitWriter* bw) { - while (bw->output_pos < bw->pos) { - j_compress_ptr cinfo = bw->cinfo; - if (cinfo->dest->free_in_buffer == 0 && - !(*cinfo->dest->empty_output_buffer)(cinfo)) { - return false; - } - size_t buflen = bw->pos - bw->output_pos; - size_t copylen = std::min(cinfo->dest->free_in_buffer, buflen); - memcpy(cinfo->dest->next_output_byte, bw->data + bw->output_pos, copylen); - bw->output_pos += copylen; - cinfo->dest->free_in_buffer -= copylen; - cinfo->dest->next_output_byte += copylen; - } - bw->output_pos = bw->pos = 0; - return true; -} - -void JumpToByteBoundary(JpegBitWriter* bw) { - size_t n_bits = bw->free_bits & 7u; - if (n_bits > 0) { - WriteBits(bw, n_bits, (1u << n_bits) - 1); - } - bw->put_buffer <<= bw->free_bits; - while (bw->free_bits <= 56) { - int c = (bw->put_buffer >> 56) & 0xFF; - EmitByte(bw, c); - bw->put_buffer <<= 8; - bw->free_bits += 8; - } - bw->put_buffer = 0; - bw->free_bits = 64; -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/bit_writer.h b/third_party/jpeg-xl/lib/jpegli/bit_writer.h deleted file mode 100644 index 3adf1eaca1f24..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/bit_writer.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_BIT_WRITER_H_ -#define LIB_JPEGLI_BIT_WRITER_H_ - -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jpegli { - -// Handles the packing of bits into output bytes. -struct JpegBitWriter { - j_compress_ptr cinfo; - uint8_t* data; - size_t len; - size_t pos; - size_t output_pos; - uint64_t put_buffer; - int free_bits; - bool healthy; -}; - -void JpegBitWriterInit(j_compress_ptr cinfo); - -bool EmptyBitWriterBuffer(JpegBitWriter* bw); - -void JumpToByteBoundary(JpegBitWriter* bw); - -// Returns non-zero if and only if x has a zero byte, i.e. one of -// x & 0xff, x & 0xff00, ..., x & 0xff00000000000000 is zero. -static JXL_INLINE uint64_t HasZeroByte(uint64_t x) { - return (x - 0x0101010101010101ULL) & ~x & 0x8080808080808080ULL; -} - -/** - * Writes the given byte to the output, writes an extra zero if byte is 0xFF. - * - * This method is "careless" - caller must make sure that there is enough - * space in the output buffer. Emits up to 2 bytes to buffer. - */ -static JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) { - bw->data[bw->pos++] = byte; - if (byte == 0xFF) bw->data[bw->pos++] = 0; -} - -static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw) { - // At this point we are ready to emit the bytes of put_buffer to the output. - // The JPEG format requires that after every 0xff byte in the entropy - // coded section, there is a zero byte, therefore we first check if any of - // the bytes of put_buffer is 0xFF. - if (HasZeroByte(~bw->put_buffer)) { - // We have a 0xFF byte somewhere, examine each byte and append a zero - // byte if necessary. - EmitByte(bw, (bw->put_buffer >> 56) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 48) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 40) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 32) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 24) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 16) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 8) & 0xFF); - EmitByte(bw, (bw->put_buffer >> 0) & 0xFF); - } else { - // We don't have any 0xFF bytes, output all 8 bytes without checking. - StoreBE64(bw->put_buffer, bw->data + bw->pos); - bw->pos += 8; - } -} - -static JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) { - // This is an optimization; if everything goes well, - // then |nbits| is positive; if non-existing Huffman symbol is going to be - // encoded, its length should be zero; later encoder could check the - // "health" of JpegBitWriter. - if (nbits == 0) { - bw->healthy = false; - return; - } - bw->free_bits -= nbits; - if (bw->free_bits < 0) { - bw->put_buffer <<= (bw->free_bits + nbits); - bw->put_buffer |= (bits >> -bw->free_bits); - DischargeBitBuffer(bw); - bw->free_bits += 64; - bw->put_buffer = nbits; - } - bw->put_buffer <<= nbits; - bw->put_buffer |= bits; -} - -} // namespace jpegli -#endif // LIB_JPEGLI_BIT_WRITER_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.cc b/third_party/jpeg-xl/lib/jpegli/bitstream.cc deleted file mode 100644 index 4dbeb738bba2d..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/bitstream.cc +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/bitstream.h" - -#include - -#include "lib/jpegli/bit_writer.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" - -namespace jpegli { - -void WriteOutput(j_compress_ptr cinfo, const uint8_t* buf, size_t bufsize) { - size_t pos = 0; - while (pos < bufsize) { - if (cinfo->dest->free_in_buffer == 0 && - !(*cinfo->dest->empty_output_buffer)(cinfo)) { - JPEGLI_ERROR("Destination suspension is not supported in markers."); - } - size_t len = std::min(cinfo->dest->free_in_buffer, bufsize - pos); - memcpy(cinfo->dest->next_output_byte, buf + pos, len); - pos += len; - cinfo->dest->free_in_buffer -= len; - cinfo->dest->next_output_byte += len; - } -} - -void WriteOutput(j_compress_ptr cinfo, const std::vector& bytes) { - WriteOutput(cinfo, bytes.data(), bytes.size()); -} - -void WriteOutput(j_compress_ptr cinfo, std::initializer_list bytes) { - WriteOutput(cinfo, bytes.begin(), bytes.size()); -} - -void EncodeAPP0(j_compress_ptr cinfo) { - WriteOutput(cinfo, - {0xff, 0xe0, 0, 16, 'J', 'F', 'I', 'F', '\0', - cinfo->JFIF_major_version, cinfo->JFIF_minor_version, - cinfo->density_unit, static_cast(cinfo->X_density >> 8), - static_cast(cinfo->X_density & 0xff), - static_cast(cinfo->Y_density >> 8), - static_cast(cinfo->Y_density & 0xff), 0, 0}); -} - -void EncodeAPP14(j_compress_ptr cinfo) { - uint8_t color_transform = cinfo->jpeg_color_space == JCS_YCbCr ? 1 - : cinfo->jpeg_color_space == JCS_YCCK ? 2 - : 0; - WriteOutput(cinfo, {0xff, 0xee, 0, 14, 'A', 'd', 'o', 'b', 'e', 0, 100, 0, 0, - 0, 0, color_transform}); -} - -void WriteFileHeader(j_compress_ptr cinfo) { - WriteOutput(cinfo, {0xFF, 0xD8}); // SOI - if (cinfo->write_JFIF_header) { - EncodeAPP0(cinfo); - } - if (cinfo->write_Adobe_marker) { - EncodeAPP14(cinfo); - } -} - -bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) { - uint8_t data[4 + NUM_QUANT_TBLS * (1 + 2 * DCTSIZE2)]; // 520 bytes - size_t pos = 0; - data[pos++] = 0xFF; - data[pos++] = 0xDB; - pos += 2; // Length will be filled in later. - - int send_table[NUM_QUANT_TBLS] = {}; - if (write_all_tables) { - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - if (cinfo->quant_tbl_ptrs[i]) send_table[i] = 1; - } - } else { - for (int c = 0; c < cinfo->num_components; ++c) { - send_table[cinfo->comp_info[c].quant_tbl_no] = 1; - } - } - - bool is_baseline = true; - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - if (!send_table[i]) continue; - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[i]; - if (quant_table == nullptr) { - JPEGLI_ERROR("Missing quant table %d", i); - } - int precision = 0; - for (UINT16 q : quant_table->quantval) { - if (q > 255) { - precision = 1; - is_baseline = false; - } - } - if (quant_table->sent_table) { - continue; - } - data[pos++] = (precision << 4) + i; - for (size_t j = 0; j < DCTSIZE2; ++j) { - int val_idx = kJPEGNaturalOrder[j]; - int val = quant_table->quantval[val_idx]; - if (val == 0) { - JPEGLI_ERROR("Invalid quantval 0."); - } - if (precision) { - data[pos++] = val >> 8; - } - data[pos++] = val & 0xFFu; - } - quant_table->sent_table = TRUE; - } - if (pos > 4) { - data[2] = (pos - 2) >> 8u; - data[3] = (pos - 2) & 0xFFu; - WriteOutput(cinfo, data, pos); - } - return is_baseline; -} - -void EncodeSOF(j_compress_ptr cinfo, bool is_baseline) { - if (cinfo->data_precision != kJpegPrecision) { - JPEGLI_ERROR("Unsupported data precision %d", cinfo->data_precision); - } - const uint8_t marker = cinfo->progressive_mode ? 0xc2 - : is_baseline ? 0xc0 - : 0xc1; - const size_t n_comps = cinfo->num_components; - const size_t marker_len = 8 + 3 * n_comps; - std::vector data(marker_len + 2); - size_t pos = 0; - data[pos++] = 0xFF; - data[pos++] = marker; - data[pos++] = marker_len >> 8u; - data[pos++] = marker_len & 0xFFu; - data[pos++] = kJpegPrecision; - data[pos++] = cinfo->image_height >> 8u; - data[pos++] = cinfo->image_height & 0xFFu; - data[pos++] = cinfo->image_width >> 8u; - data[pos++] = cinfo->image_width & 0xFFu; - data[pos++] = n_comps; - for (size_t i = 0; i < n_comps; ++i) { - jpeg_component_info* comp = &cinfo->comp_info[i]; - data[pos++] = comp->component_id; - data[pos++] = ((comp->h_samp_factor << 4u) | (comp->v_samp_factor)); - const uint32_t quant_idx = comp->quant_tbl_no; - if (cinfo->quant_tbl_ptrs[quant_idx] == nullptr) { - JPEGLI_ERROR("Invalid component quant table index %u.", quant_idx); - } - data[pos++] = quant_idx; - } - WriteOutput(cinfo, data); -} - -void WriteFrameHeader(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - bool is_baseline = EncodeDQT(cinfo, /*write_all_tables=*/false); - if (cinfo->progressive_mode || cinfo->arith_code || - cinfo->data_precision != 8) { - is_baseline = false; - } - for (size_t i = 0; i < m->num_huffman_tables; ++i) { - int slot_id = m->slot_id_map[i]; - if (slot_id > 0x11 || (slot_id > 0x01 && slot_id < 0x10)) { - is_baseline = false; - } - } - EncodeSOF(cinfo, is_baseline); -} - -void EncodeDRI(j_compress_ptr cinfo) { - WriteOutput(cinfo, {0xFF, 0xDD, 0, 4, - static_cast(cinfo->restart_interval >> 8), - static_cast(cinfo->restart_interval & 0xFF)}); -} - -void EncodeDHT(j_compress_ptr cinfo, size_t offset, size_t num) { - jpeg_comp_master* m = cinfo->master; - size_t marker_len = 2; - for (size_t i = 0; i < num; ++i) { - const JHUFF_TBL& table = m->huffman_tables[offset + i]; - if (table.sent_table) continue; - marker_len += kJpegHuffmanMaxBitLength + 1; - for (size_t j = 0; j <= kJpegHuffmanMaxBitLength; ++j) { - marker_len += table.bits[j]; - } - } - std::vector data(marker_len + 2); - size_t pos = 0; - data[pos++] = 0xFF; - data[pos++] = 0xC4; - data[pos++] = marker_len >> 8u; - data[pos++] = marker_len & 0xFFu; - for (size_t i = 0; i < num; ++i) { - const JHUFF_TBL& table = m->huffman_tables[offset + i]; - if (table.sent_table) continue; - size_t total_count = 0; - for (size_t i = 0; i <= kJpegHuffmanMaxBitLength; ++i) { - total_count += table.bits[i]; - } - data[pos++] = m->slot_id_map[offset + i]; - for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) { - data[pos++] = table.bits[i]; - } - for (size_t i = 0; i < total_count; ++i) { - data[pos++] = table.huffval[i]; - } - } - if (marker_len > 2) { - WriteOutput(cinfo, data); - } -} - -void EncodeSOS(j_compress_ptr cinfo, int scan_index) { - jpeg_comp_master* m = cinfo->master; - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - const size_t marker_len = 6 + 2 * scan_info->comps_in_scan; - std::vector data(marker_len + 2); - size_t pos = 0; - data[pos++] = 0xFF; - data[pos++] = 0xDA; - data[pos++] = marker_len >> 8u; - data[pos++] = marker_len & 0xFFu; - data[pos++] = scan_info->comps_in_scan; - for (int i = 0; i < scan_info->comps_in_scan; ++i) { - int comp_idx = scan_info->component_index[i]; - data[pos++] = cinfo->comp_info[comp_idx].component_id; - int dc_slot_id = m->slot_id_map[m->context_map[comp_idx]]; - int ac_context = m->ac_ctx_offset[scan_index] + i; - int ac_slot_id = m->slot_id_map[m->context_map[ac_context]]; - data[pos++] = (dc_slot_id << 4u) + (ac_slot_id - 16); - } - data[pos++] = scan_info->Ss; - data[pos++] = scan_info->Se; - data[pos++] = ((scan_info->Ah << 4u) | (scan_info->Al)); - WriteOutput(cinfo, data); -} - -void WriteScanHeader(j_compress_ptr cinfo, int scan_index) { - jpeg_comp_master* m = cinfo->master; - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - cinfo->restart_interval = m->scan_token_info[scan_index].restart_interval; - if (cinfo->restart_interval != m->last_restart_interval) { - EncodeDRI(cinfo); - m->last_restart_interval = cinfo->restart_interval; - } - size_t num_dht = 0; - if (scan_index == 0) { - // For the first scan we emit all DC and at most 4 AC Huffman codes. - for (size_t i = 0, num_ac = 0; i < m->num_huffman_tables; ++i) { - if (m->slot_id_map[i] >= 16 && num_ac++ >= 4) break; - ++num_dht; - } - } else if (scan_info->Ss > 0) { - // For multi-scan sequential and progressive DC scans we have already - // emitted all Huffman codes that we need before the first scan. For - // progressive AC scans we only need at most one new Huffman code. - if (m->context_map[m->ac_ctx_offset[scan_index]] == m->next_dht_index) { - num_dht = 1; - } - } - if (num_dht > 0) { - EncodeDHT(cinfo, m->next_dht_index, num_dht); - m->next_dht_index += num_dht; - } - EncodeSOS(cinfo, scan_index); -} - -void WriteBlock(const int32_t* JXL_RESTRICT symbols, - const int32_t* JXL_RESTRICT extra_bits, const int num_nonzeros, - const bool emit_eob, - const HuffmanCodeTable* JXL_RESTRICT dc_code, - const HuffmanCodeTable* JXL_RESTRICT ac_code, - JpegBitWriter* JXL_RESTRICT bw) { - int symbol = symbols[0]; - WriteBits(bw, dc_code->depth[symbol], dc_code->code[symbol] | extra_bits[0]); - for (int i = 1; i < num_nonzeros; ++i) { - symbol = symbols[i]; - if (symbol > 255) { - WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]); - symbol -= 256; - if (symbol > 255) { - WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]); - symbol -= 256; - if (symbol > 255) { - WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]); - symbol -= 256; - } - } - } - WriteBits(bw, ac_code->depth[symbol], - ac_code->code[symbol] | extra_bits[i]); - } - if (emit_eob) { - WriteBits(bw, ac_code->depth[0], ac_code->code[0]); - } -} - -namespace { - -JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) { - bw->data[bw->pos++] = 0xFF; - bw->data[bw->pos++] = marker; -} - -void WriteTokens(j_compress_ptr cinfo, int scan_index, JpegBitWriter* bw) { - jpeg_comp_master* m = cinfo->master; - HuffmanCodeTable* coding_tables = &m->coding_tables[0]; - int next_restart_marker = 0; - const ScanTokenInfo& sti = m->scan_token_info[scan_index]; - size_t num_token_arrays = m->cur_token_array + 1; - size_t total_tokens = 0; - size_t restart_idx = 0; - size_t next_restart = sti.restarts[restart_idx]; - uint8_t* context_map = m->context_map; - for (size_t i = 0; i < num_token_arrays; ++i) { - Token* tokens = m->token_arrays[i].tokens; - size_t num_tokens = m->token_arrays[i].num_tokens; - if (sti.token_offset < total_tokens + num_tokens && - total_tokens < sti.token_offset + sti.num_tokens) { - size_t start_ix = - total_tokens < sti.token_offset ? sti.token_offset - total_tokens : 0; - size_t end_ix = std::min(sti.token_offset + sti.num_tokens - total_tokens, - num_tokens); - size_t cycle_len = bw->len / 8; - size_t next_cycle = cycle_len; - for (size_t i = start_ix; i < end_ix; ++i) { - if (total_tokens + i == next_restart) { - JumpToByteBoundary(bw); - EmitMarker(bw, 0xD0 + next_restart_marker); - next_restart_marker += 1; - next_restart_marker &= 0x7; - next_restart = sti.restarts[++restart_idx]; - } - Token t = tokens[i]; - const HuffmanCodeTable* code = &coding_tables[context_map[t.context]]; - WriteBits(bw, code->depth[t.symbol], code->code[t.symbol] | t.bits); - if (--next_cycle == 0) { - if (!EmptyBitWriterBuffer(bw)) { - JPEGLI_ERROR( - "Output suspension is not supported in " - "finish_compress"); - } - next_cycle = cycle_len; - } - } - } - total_tokens += num_tokens; - } -} - -void WriteACRefinementTokens(j_compress_ptr cinfo, int scan_index, - JpegBitWriter* bw) { - jpeg_comp_master* m = cinfo->master; - const ScanTokenInfo& sti = m->scan_token_info[scan_index]; - const uint8_t context = m->ac_ctx_offset[scan_index]; - const HuffmanCodeTable* code = &m->coding_tables[m->context_map[context]]; - size_t cycle_len = bw->len / 64; - size_t next_cycle = cycle_len; - size_t refbit_idx = 0; - size_t eobrun_idx = 0; - size_t restart_idx = 0; - size_t next_restart = sti.restarts[restart_idx]; - int next_restart_marker = 0; - for (size_t i = 0; i < sti.num_tokens; ++i) { - if (i == next_restart) { - JumpToByteBoundary(bw); - EmitMarker(bw, 0xD0 + next_restart_marker); - next_restart_marker += 1; - next_restart_marker &= 0x7; - next_restart = sti.restarts[++restart_idx]; - } - RefToken t = sti.tokens[i]; - int symbol = t.symbol & 253; - uint16_t bits = 0; - if ((symbol & 1) == 0) { - int r = symbol >> 4; - if (r > 0 && r < 15) { - bits = sti.eobruns[eobrun_idx++]; - } - } else { - bits = (t.symbol >> 1) & 1; - } - WriteBits(bw, code->depth[symbol], code->code[symbol] | bits); - for (int j = 0; j < t.refbits; ++j) { - WriteBits(bw, 1, sti.refbits[refbit_idx++]); - } - if (--next_cycle == 0) { - if (!EmptyBitWriterBuffer(bw)) { - JPEGLI_ERROR("Output suspension is not supported in finish_compress"); - } - next_cycle = cycle_len; - } - } -} - -void WriteDCRefinementBits(j_compress_ptr cinfo, int scan_index, - JpegBitWriter* bw) { - jpeg_comp_master* m = cinfo->master; - const ScanTokenInfo& sti = m->scan_token_info[scan_index]; - size_t restart_idx = 0; - size_t next_restart = sti.restarts[restart_idx]; - int next_restart_marker = 0; - size_t cycle_len = bw->len * 4; - size_t next_cycle = cycle_len; - size_t refbit_idx = 0; - for (size_t i = 0; i < sti.num_tokens; ++i) { - if (i == next_restart) { - JumpToByteBoundary(bw); - EmitMarker(bw, 0xD0 + next_restart_marker); - next_restart_marker += 1; - next_restart_marker &= 0x7; - next_restart = sti.restarts[++restart_idx]; - } - WriteBits(bw, 1, sti.refbits[refbit_idx++]); - if (--next_cycle == 0) { - if (!EmptyBitWriterBuffer(bw)) { - JPEGLI_ERROR( - "Output suspension is not supported in " - "finish_compress"); - } - next_cycle = cycle_len; - } - } -} - -} // namespace - -void WriteScanData(j_compress_ptr cinfo, int scan_index) { - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - JpegBitWriter* bw = &cinfo->master->bw; - if (scan_info->Ah == 0) { - WriteTokens(cinfo, scan_index, bw); - } else if (scan_info->Ss > 0) { - WriteACRefinementTokens(cinfo, scan_index, bw); - } else { - WriteDCRefinementBits(cinfo, scan_index, bw); - } - if (!bw->healthy) { - JPEGLI_ERROR("Unknown Huffman coded symbol found in scan %d", scan_index); - } - JumpToByteBoundary(bw); - if (!EmptyBitWriterBuffer(bw)) { - JPEGLI_ERROR("Output suspension is not supported in finish_compress"); - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.h b/third_party/jpeg-xl/lib/jpegli/bitstream.h deleted file mode 100644 index bed441aefe438..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/bitstream.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_BITSTREAM_H_ -#define LIB_JPEGLI_BITSTREAM_H_ - -#include -#include - -#include "lib/jpegli/encode_internal.h" - -namespace jpegli { - -void WriteOutput(j_compress_ptr cinfo, const uint8_t* buf, size_t bufsize); -void WriteOutput(j_compress_ptr cinfo, const std::vector& bytes); -void WriteOutput(j_compress_ptr cinfo, std::initializer_list bytes); - -void EncodeAPP0(j_compress_ptr cinfo); -void EncodeAPP14(j_compress_ptr cinfo); -void WriteFileHeader(j_compress_ptr cinfo); - -// Returns true of only baseline 8-bit tables are used. -bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables); -void EncodeSOF(j_compress_ptr cinfo, bool is_baseline); -void WriteFrameHeader(j_compress_ptr cinfo); - -void EncodeDRI(j_compress_ptr cinfo); -void EncodeDHT(j_compress_ptr cinfo, size_t offset, size_t num); -void EncodeSOS(j_compress_ptr cinfo, int scan_index); -void WriteScanHeader(j_compress_ptr cinfo, int scan_index); - -void WriteBlock(const int32_t* JXL_RESTRICT symbols, - const int32_t* JXL_RESTRICT extra_bits, int num_nonzeros, - bool emit_eob, const HuffmanCodeTable* JXL_RESTRICT dc_code, - const HuffmanCodeTable* JXL_RESTRICT ac_code, - JpegBitWriter* JXL_RESTRICT bw); -void WriteScanData(j_compress_ptr cinfo, int scan_index); - -} // namespace jpegli - -#endif // LIB_JPEGLI_BITSTREAM_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc b/third_party/jpeg-xl/lib/jpegli/color_quantize.cc deleted file mode 100644 index d5b0097f3d066..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/color_quantize.h" - -#include -#include -#include - -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/error.h" - -namespace jpegli { - -namespace { - -constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3}; -constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1}; - -int Pow(int a, int b) { - int r = 1; - for (int i = 0; i < b; ++i) { - r *= a; - } - return r; -} - -int ComponentOrder(j_decompress_ptr cinfo, int i) { - if (cinfo->out_color_components == 3) { - return i < 2 ? 1 - i : i; - } - return i; -} - -int GetColorComponent(int i, int N) { - return (i * 255 + (N - 1) / 2) / (N - 1); -} - -} // namespace - -void ChooseColorMap1Pass(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - int components = cinfo->out_color_components; - int desired = std::min(cinfo->desired_number_of_colors, 256); - int num = 1; - while (Pow(num + 1, components) <= desired) { - ++num; - } - if (num == 1) { - JPEGLI_ERROR("Too few colors (%d) in requested colormap", desired); - } - int actual = Pow(num, components); - for (int i = 0; i < components; ++i) { - m->num_colors_[i] = num; - } - while (actual < desired) { - int total = actual; - for (int i = 0; i < components; ++i) { - int c = ComponentOrder(cinfo, i); - int new_total = (actual / m->num_colors_[c]) * (m->num_colors_[c] + 1); - if (new_total <= desired) { - ++m->num_colors_[c]; - actual = new_total; - } - } - if (actual == total) { - break; - } - } - cinfo->actual_number_of_colors = actual; - cinfo->colormap = (*cinfo->mem->alloc_sarray)( - reinterpret_cast(cinfo), JPOOL_IMAGE, actual, components); - int next_color[kMaxComponents] = {0}; - for (int i = 0; i < actual; ++i) { - for (int c = 0; c < components; ++c) { - cinfo->colormap[c][i] = - GetColorComponent(next_color[c], m->num_colors_[c]); - } - int c = components - 1; - while (c > 0 && next_color[c] + 1 == m->num_colors_[c]) { - next_color[c--] = 0; - } - ++next_color[c]; - } - if (!m->colormap_lut_) { - m->colormap_lut_ = Allocate(cinfo, components * 256, JPOOL_IMAGE); - } - int stride = actual; - for (int c = 0; c < components; ++c) { - int N = m->num_colors_[c]; - stride /= N; - for (int i = 0; i < 256; ++i) { - int index = ((2 * i - 1) * (N - 1) + 254) / 510; - m->colormap_lut_[c * 256 + i] = index * stride; - } - } -} - -namespace { - -// 2^13 priority levels for the PQ seems to be a good compromise between -// accuracy, running time and stack space usage. -const int kMaxPriority = 1 << 13; -const int kMaxLevel = 3; - -// This function is used in the multi-resolution grid to be able to compute -// the keys for the different resolutions by just shifting the first key. -inline int InterlaceBitsRGB(uint8_t r, uint8_t g, uint8_t b) { - int z = 0; - for (int i = 0; i < 7; ++i) { - z += (r >> 5) & 4; - z += (g >> 6) & 2; - z += (b >> 7); - z <<= 3; - r <<= 1; - g <<= 1; - b <<= 1; - } - z += (r >> 5) & 4; - z += (g >> 6) & 2; - z += (b >> 7); - return z; -} - -// This function will compute the actual priorities of the colors based on -// the current distance from the palette, the population count and the signals -// from the multi-resolution grid. -inline int Priority(int d, int n, const int* density, const int* radius) { - int p = d * n; - for (int level = 0; level < kMaxLevel; ++level) { - if (d > radius[level]) { - p += density[level] * (d - radius[level]); - } - } - return std::min(kMaxPriority - 1, p >> 4); -} - -inline int ColorIntQuadDistanceRGB(uint8_t r1, uint8_t g1, uint8_t b1, - uint8_t r2, uint8_t g2, uint8_t b2) { - // weights for the intensity calculation - static constexpr int ired = 2; - static constexpr int igreen = 5; - static constexpr int iblue = 1; - // normalization factor for the intensity calculation (2^ishift) - static constexpr int ishift = 3; - const int rd = r1 - r2; - const int gd = g1 - g2; - const int bd = b1 - b2; - const int id = ired * rd + igreen * gd + iblue * bd; - return rd * rd + gd * gd + bd * bd + ((id * id) >> (2 * ishift)); -} - -inline int ScaleQuadDistanceRGB(int d) { - return static_cast(std::lround(sqrt(d * 0.25))); -} - -// The function updates the minimal distances, the clustering and the -// quantization error after the insertion of the new color into the palette. -void AddToRGBPalette(const uint8_t* red, const uint8_t* green, - const uint8_t* blue, - const int* count, // histogram of colors - const int index, // index of color to be added - const int k, // size of current palette - const int n, // number of colors - int* dist, // array of distances from palette - int* cluster, // mapping of color indices to palette - int* center, // the inverse mapping - int64_t* error) { // measure of the quantization error - center[k] = index; - cluster[index] = k; - *error -= - static_cast(dist[index]) * static_cast(count[index]); - dist[index] = 0; - for (int j = 0; j < n; ++j) { - if (dist[j] > 0) { - const int d = ColorIntQuadDistanceRGB( - red[index], green[index], blue[index], red[j], green[j], blue[j]); - if (d < dist[j]) { - *error += static_cast((d - dist[j])) * - static_cast(count[j]); - dist[j] = d; - cluster[j] = k; - } - } - } -} - -struct RGBPixelHasher { - // A quick but good-enough hash to get 24 bits of RGB into the lower 12 bits. - size_t operator()(uint32_t a) const { return (a ^ (a >> 12)) * 0x9e3779b9; } -}; - -struct WangHasher { - // Thomas Wang's Hash. Nearly perfect and still quite fast. Above (for - // pixels) we use a simpler hash because the number of hash calls is - // proportional to the number of pixels and that hash dominates; we want the - // cost to be minimal and we start with a large table. We can use a better - // hash for the histogram since the number of hash calls is proportional to - // the number of unique colors in the image, which is hopefully much smaller. - // Note that the difference is slight; e.g. replacing RGBPixelHasher with - // WangHasher only slows things down by 5% on an Opteron. - size_t operator()(uint32_t a) const { - a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); - a = a ^ (a >> 4); - a = a * 0x27d4eb2d; - a = a ^ (a >> 15); - return a; - } -}; - -// Build an index of all the different colors in the input -// image. To do this we map the 24 bit RGB representation of the colors -// to a unique integer index assigned to the different colors in order of -// appearance in the image. Return the number of unique colors found. -// The colors are pre-quantized to 3 * 6 bits precision. -int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels, - int* const count, uint8_t* const red, - uint8_t* const green, uint8_t* const blue) { - // Impossible because rgb are in the low 24 bits, and the upper 8 bits is 0. - const uint32_t impossible_pixel_value = 0x10000000; - std::unordered_map index_map(1 << 12); - std::unordered_map::iterator index_map_lookup; - const uint8_t* imagep = &image[0]; - uint32_t prev_pixel = impossible_pixel_value; - int index = 0; - int n = 0; - for (int i = 0; i < num_pixels; ++i) { - uint8_t r = ((*imagep++) & 0xfc) + 2; - uint8_t g = ((*imagep++) & 0xfc) + 2; - uint8_t b = ((*imagep++) & 0xfc) + 2; - uint32_t pixel = (b << 16) | (g << 8) | r; - if (pixel != prev_pixel) { - prev_pixel = pixel; - index_map_lookup = index_map.find(pixel); - if (index_map_lookup != index_map.end()) { - index = index_map_lookup->second; - } else { - index_map[pixel] = index = n++; - red[index] = r; - green[index] = g; - blue[index] = b; - } - } - ++count[index]; - } - return n; -} - -} // namespace - -void ChooseColorMap2Pass(j_decompress_ptr cinfo) { - if (cinfo->out_color_space != JCS_RGB) { - JPEGLI_ERROR("Two-pass quantizer must use RGB output color space."); - } - jpeg_decomp_master* m = cinfo->master; - const size_t num_pixels = cinfo->output_width * cinfo->output_height; - const int max_color_count = std::max(num_pixels, 1u << 18); - const int max_palette_size = cinfo->desired_number_of_colors; - std::unique_ptr red(new uint8_t[max_color_count]); - std::unique_ptr green(new uint8_t[max_color_count]); - std::unique_ptr blue(new uint8_t[max_color_count]); - std::vector count(max_color_count, 0); - // number of colors - int n = BuildRGBColorIndex(m->pixels_, num_pixels, count.data(), &red[0], - &green[0], &blue[0]); - - std::vector dist(n, std::numeric_limits::max()); - std::vector cluster(n); - std::vector in_palette(n, false); - int center[256]; - int k = 0; // palette size - const int count_threshold = (num_pixels * 4) / max_palette_size; - static constexpr int kAveragePixelErrorThreshold = 1; - const int64_t error_threshold = num_pixels * kAveragePixelErrorThreshold; - int64_t error = 0; // quantization error - - int max_count = 0; - int winner = 0; - for (int i = 0; i < n; ++i) { - if (count[i] > max_count) { - max_count = count[i]; - winner = i; - } - if (!in_palette[i] && count[i] > count_threshold) { - AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n, - dist.data(), cluster.data(), ¢er[0], &error); - in_palette[i] = true; - } - } - if (k == 0) { - AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), winner, k++, n, - dist.data(), cluster.data(), ¢er[0], &error); - in_palette[winner] = true; - } - - // Calculation of the multi-resolution density grid. - std::vector density(n * kMaxLevel); - std::vector radius(n * kMaxLevel); - std::unordered_map histogram[kMaxLevel]; - for (int level = 0; level < kMaxLevel; ++level) { - // This value is never used because key = InterlaceBitsRGB(...) >> 6 - } - - for (int i = 0; i < n; ++i) { - if (!in_palette[i]) { - const int key = InterlaceBitsRGB(red[i], green[i], blue[i]) >> 6; - for (int level = 0; level < kMaxLevel; ++level) { - histogram[level][key >> (3 * level)] += count[i]; - } - } - } - for (int i = 0; i < n; ++i) { - if (!in_palette[i]) { - for (int level = 0; level < kMaxLevel; ++level) { - const int mask = (4 << level) - 1; - const int rd = std::max(red[i] & mask, mask - (red[i] & mask)); - const int gd = std::max(green[i] & mask, mask - (green[i] & mask)); - const int bd = std::max(blue[i] & mask, mask - (blue[i] & mask)); - radius[i * kMaxLevel + level] = - ScaleQuadDistanceRGB(ColorIntQuadDistanceRGB(0, 0, 0, rd, gd, bd)); - } - const int key = InterlaceBitsRGB(red[i], green[i], blue[i]) >> 6; - if (kMaxLevel > 0) { - density[i * kMaxLevel] = histogram[0][key] - count[i]; - } - for (int level = 1; level < kMaxLevel; ++level) { - density[i * kMaxLevel + level] = - (histogram[level][key >> (3 * level)] - - histogram[level - 1][key >> (3 * level - 3)]); - } - } - } - - // Calculate the initial error now that the palette has been initialized. - error = 0; - for (int i = 0; i < n; ++i) { - error += static_cast(dist[i]) * static_cast(count[i]); - } - - std::unique_ptr[]> bucket_array( - new std::vector[kMaxPriority]); - int top_priority = -1; - for (int i = 0; i < n; ++i) { - if (!in_palette[i]) { - int priority = Priority(ScaleQuadDistanceRGB(dist[i]), count[i], - &density[i * kMaxLevel], &radius[i * kMaxLevel]); - bucket_array[priority].push_back(i); - top_priority = std::max(priority, top_priority); - } - } - double error_accum = 0; - while (top_priority >= 0 && k < max_palette_size) { - if (error < error_threshold) { - error_accum += std::min(error_threshold, error_threshold - error); - if (error_accum >= 10 * error_threshold) { - break; - } - } - int i = bucket_array[top_priority].back(); - int priority = Priority(ScaleQuadDistanceRGB(dist[i]), count[i], - &density[i * kMaxLevel], &radius[i * kMaxLevel]); - if (priority < top_priority) { - bucket_array[priority].push_back(i); - } else { - AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n, - dist.data(), cluster.data(), ¢er[0], &error); - } - bucket_array[top_priority].pop_back(); - while (top_priority >= 0 && bucket_array[top_priority].empty()) { - --top_priority; - } - } - - cinfo->actual_number_of_colors = k; - cinfo->colormap = (*cinfo->mem->alloc_sarray)( - reinterpret_cast(cinfo), JPOOL_IMAGE, k, 3); - for (int i = 0; i < k; ++i) { - int index = center[i]; - cinfo->colormap[0][i] = red[index]; - cinfo->colormap[1][i] = green[index]; - cinfo->colormap[2][i] = blue[index]; - } -} - -namespace { - -void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, const int cell[], - std::vector* candidates) { - int cell_min[kMaxComponents]; - int cell_max[kMaxComponents]; - int cell_center[kMaxComponents]; - for (int c = 0; c < ncomp; ++c) { - cell_min[c] = cell[c] << (8 - kNumColorCellBits[c]); - cell_max[c] = cell_min[c] + (1 << (8 - kNumColorCellBits[c])) - 1; - cell_center[c] = (cell_min[c] + cell_max[c]) >> 1; - } - int min_maxdist = std::numeric_limits::max(); - int mindist[256]; - for (int i = 0; i < cinfo->actual_number_of_colors; ++i) { - int dmin = 0; - int dmax = 0; - for (int c = 0; c < ncomp; ++c) { - int palette_c = cinfo->colormap[c][i]; - int dminc = 0; - int dmaxc; - if (palette_c < cell_min[c]) { - dminc = cell_min[c] - palette_c; - dmaxc = cell_max[c] - palette_c; - } else if (palette_c > cell_max[c]) { - dminc = palette_c - cell_max[c]; - dmaxc = palette_c - cell_min[c]; - } else if (palette_c > cell_center[c]) { - dmaxc = palette_c - cell_min[c]; - } else { - dmaxc = cell_max[c] - palette_c; - } - dminc *= kCompW[c]; - dmaxc *= kCompW[c]; - dmin += dminc * dminc; - dmax += dmaxc * dmaxc; - } - mindist[i] = dmin; - min_maxdist = std::min(dmax, min_maxdist); - } - for (int i = 0; i < cinfo->actual_number_of_colors; ++i) { - if (mindist[i] < min_maxdist) { - candidates->push_back(i); - } - } -} - -} // namespace - -void CreateInverseColorMap(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - int ncomp = cinfo->out_color_components; - JPEGLI_CHECK(ncomp > 0); - JPEGLI_CHECK(ncomp <= kMaxComponents); - int num_cells = 1; - for (int c = 0; c < ncomp; ++c) { - num_cells *= (1 << kNumColorCellBits[c]); - } - m->candidate_lists_.resize(num_cells); - - int next_cell[kMaxComponents] = {0}; - for (int i = 0; i < num_cells; ++i) { - m->candidate_lists_[i].clear(); - FindCandidatesForCell(cinfo, ncomp, next_cell, &m->candidate_lists_[i]); - int c = ncomp - 1; - while (c > 0 && next_cell[c] + 1 == (1 << kNumColorCellBits[c])) { - next_cell[c--] = 0; - } - ++next_cell[c]; - } - m->regenerate_inverse_colormap_ = false; -} - -int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel) { - jpeg_decomp_master* m = cinfo->master; - int num_channels = cinfo->out_color_components; - int index = 0; - if (m->quant_mode_ == 1) { - for (int c = 0; c < num_channels; ++c) { - index += m->colormap_lut_[c * 256 + pixel[c]]; - } - } else { - size_t cell_idx = 0; - size_t stride = 1; - for (int c = num_channels - 1; c >= 0; --c) { - cell_idx += (pixel[c] >> (8 - kNumColorCellBits[c])) * stride; - stride <<= kNumColorCellBits[c]; - } - JPEGLI_CHECK(cell_idx < m->candidate_lists_.size()); - int mindist = std::numeric_limits::max(); - const auto& candidates = m->candidate_lists_[cell_idx]; - for (uint8_t i : candidates) { - int dist = 0; - for (int c = 0; c < num_channels; ++c) { - int d = (cinfo->colormap[c][i] - pixel[c]) * kCompW[c]; - dist += d * d; - } - if (dist < mindist) { - mindist = dist; - index = i; - } - } - } - JPEGLI_CHECK(index < cinfo->actual_number_of_colors); - return index; -} - -void CreateOrderedDitherTables(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - static constexpr size_t kDitherSize = 4; - static constexpr size_t kDitherMask = kDitherSize - 1; - static constexpr float kBaseDitherMatrix[] = { - 0, 8, 2, 10, // - 12, 4, 14, 6, // - 3, 11, 1, 9, // - 15, 7, 13, 5, // - }; - m->dither_size_ = kDitherSize; - m->dither_mask_ = kDitherMask; - size_t ncells = m->dither_size_ * m->dither_size_; - for (int c = 0; c < cinfo->out_color_components; ++c) { - float spread = 1.0f / (m->num_colors_[c] - 1); - float mul = spread / ncells; - float offset = 0.5f * spread; - if (m->dither_[c] == nullptr) { - m->dither_[c] = Allocate(cinfo, ncells, JPOOL_IMAGE_ALIGNED); - } - for (size_t idx = 0; idx < ncells; ++idx) { - m->dither_[c][idx] = kBaseDitherMatrix[idx] * mul - offset; - } - } -} - -void InitFSDitherState(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - for (int c = 0; c < cinfo->out_color_components; ++c) { - if (m->error_row_[c] == nullptr) { - m->error_row_[c] = - Allocate(cinfo, cinfo->output_width, JPOOL_IMAGE_ALIGNED); - m->error_row_[c + kMaxComponents] = - Allocate(cinfo, cinfo->output_width, JPOOL_IMAGE_ALIGNED); - } - memset(m->error_row_[c], 0.0, cinfo->output_width * sizeof(float)); - memset(m->error_row_[c + kMaxComponents], 0.0, - cinfo->output_width * sizeof(float)); - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.h b/third_party/jpeg-xl/lib/jpegli/color_quantize.h deleted file mode 100644 index 92b922f7564cf..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/color_quantize.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_COLOR_QUANTIZE_H_ -#define LIB_JPEGLI_COLOR_QUANTIZE_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void ChooseColorMap1Pass(j_decompress_ptr cinfo); - -void ChooseColorMap2Pass(j_decompress_ptr cinfo); - -void CreateInverseColorMap(j_decompress_ptr cinfo); - -void CreateOrderedDitherTables(j_decompress_ptr cinfo); - -void InitFSDitherState(j_decompress_ptr cinfo); - -int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel); - -} // namespace jpegli - -#endif // LIB_JPEGLI_COLOR_QUANTIZE_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/color_transform.cc b/third_party/jpeg-xl/lib/jpegli/color_transform.cc deleted file mode 100644 index ec906bedce414..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/color_transform.cc +++ /dev/null @@ -1,547 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/color_transform.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/color_transform.cc" -#include -#include - -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jxl/base/compiler_specific.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::Div; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::Sub; - -template -void YCbCrToExtRGB(float* row[kMaxComponents], size_t xsize) { - const HWY_CAPPED(float, 8) df; - const float* row_y = row[0]; - const float* row_cb = row[1]; - const float* row_cr = row[2]; - float* row_r = row[kRed]; - float* row_g = row[kGreen]; - float* row_b = row[kBlue]; - float* row_a = row[kAlpha]; - - // Full-range BT.601 as defined by JFIF Clause 7: - // https://www.itu.int/rec/T-REC-T.871-201105-I/en - const auto crcr = Set(df, 1.402f); - const auto cgcb = Set(df, -0.114f * 1.772f / 0.587f); - const auto cgcr = Set(df, -0.299f * 1.402f / 0.587f); - const auto cbcb = Set(df, 1.772f); - const auto alpha_opaque = Set(df, 127.0f / 255.0f); - - for (size_t x = 0; x < xsize; x += Lanes(df)) { - const auto y_vec = Load(df, row_y + x); - const auto cb_vec = Load(df, row_cb + x); - const auto cr_vec = Load(df, row_cr + x); - const auto r_vec = MulAdd(crcr, cr_vec, y_vec); - const auto g_vec = MulAdd(cgcr, cr_vec, MulAdd(cgcb, cb_vec, y_vec)); - const auto b_vec = MulAdd(cbcb, cb_vec, y_vec); - Store(r_vec, df, row_r + x); - Store(g_vec, df, row_g + x); - Store(b_vec, df, row_b + x); - if (kAlpha >= 0) { - Store(alpha_opaque, df, row_a + x); - } - } -} - -void YCbCrToRGB(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<0, 1, 2, -1>(row, xsize); -} - -void YCbCrToBGR(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<2, 1, 0, -1>(row, xsize); -} - -void YCbCrToRGBA(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<0, 1, 2, 3>(row, xsize); -} - -void YCbCrToBGRA(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<2, 1, 0, 3>(row, xsize); -} - -void YCbCrToARGB(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<1, 2, 3, 0>(row, xsize); -} - -void YCbCrToABGR(float* row[kMaxComponents], size_t xsize) { - YCbCrToExtRGB<3, 2, 1, 0>(row, xsize); -} - -void YCCKToCMYK(float* row[kMaxComponents], size_t xsize) { - const HWY_CAPPED(float, 8) df; - float* JXL_RESTRICT row0 = row[0]; - float* JXL_RESTRICT row1 = row[1]; - float* JXL_RESTRICT row2 = row[2]; - YCbCrToRGB(row, xsize); - const auto offset = Set(df, -1.0f / 255.0f); - for (size_t x = 0; x < xsize; x += Lanes(df)) { - Store(Sub(offset, Load(df, row0 + x)), df, row0 + x); - Store(Sub(offset, Load(df, row1 + x)), df, row1 + x); - Store(Sub(offset, Load(df, row2 + x)), df, row2 + x); - } -} - -template -void ExtRGBToYCbCr(float* row[kMaxComponents], size_t xsize) { - const HWY_CAPPED(float, 8) df; - const float* row_r = row[kRed]; - const float* row_g = row[kGreen]; - const float* row_b = row[kBlue]; - float* row_y = row[0]; - float* row_cb = row[1]; - float* row_cr = row[2]; - // Full-range BT.601 as defined by JFIF Clause 7: - // https://www.itu.int/rec/T-REC-T.871-201105-I/en - const auto c128 = Set(df, 128.0f); - const auto kR = Set(df, 0.299f); // NTSC luma - const auto kG = Set(df, 0.587f); - const auto kB = Set(df, 0.114f); - const auto kAmpR = Set(df, 0.701f); - const auto kAmpB = Set(df, 0.886f); - const auto kDiffR = Add(kAmpR, kR); - const auto kDiffB = Add(kAmpB, kB); - const auto kNormR = Div(Set(df, 1.0f), (Add(kAmpR, Add(kG, kB)))); - const auto kNormB = Div(Set(df, 1.0f), (Add(kR, Add(kG, kAmpB)))); - - for (size_t x = 0; x < xsize; x += Lanes(df)) { - const auto r = Load(df, row_r + x); - const auto g = Load(df, row_g + x); - const auto b = Load(df, row_b + x); - const auto r_base = Mul(r, kR); - const auto r_diff = Mul(r, kDiffR); - const auto g_base = Mul(g, kG); - const auto b_base = Mul(b, kB); - const auto b_diff = Mul(b, kDiffB); - const auto y_base = Add(r_base, Add(g_base, b_base)); - const auto cb_vec = MulAdd(Sub(b_diff, y_base), kNormB, c128); - const auto cr_vec = MulAdd(Sub(r_diff, y_base), kNormR, c128); - Store(y_base, df, row_y + x); - Store(cb_vec, df, row_cb + x); - Store(cr_vec, df, row_cr + x); - } -} - -void RGBToYCbCr(float* row[kMaxComponents], size_t xsize) { - ExtRGBToYCbCr<0, 1, 2>(row, xsize); -} - -void BGRToYCbCr(float* row[kMaxComponents], size_t xsize) { - ExtRGBToYCbCr<2, 1, 0>(row, xsize); -} - -void ARGBToYCbCr(float* row[kMaxComponents], size_t xsize) { - ExtRGBToYCbCr<1, 2, 3>(row, xsize); -} - -void ABGRToYCbCr(float* row[kMaxComponents], size_t xsize) { - ExtRGBToYCbCr<3, 2, 1>(row, xsize); -} - -void CMYKToYCCK(float* row[kMaxComponents], size_t xsize) { - const HWY_CAPPED(float, 8) df; - float* JXL_RESTRICT row0 = row[0]; - float* JXL_RESTRICT row1 = row[1]; - float* JXL_RESTRICT row2 = row[2]; - const auto unity = Set(df, 255.0f); - for (size_t x = 0; x < xsize; x += Lanes(df)) { - Store(Sub(unity, Load(df, row0 + x)), df, row0 + x); - Store(Sub(unity, Load(df, row1 + x)), df, row1 + x); - Store(Sub(unity, Load(df, row2 + x)), df, row2 + x); - } - RGBToYCbCr(row, xsize); -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -HWY_EXPORT(CMYKToYCCK); -HWY_EXPORT(YCCKToCMYK); -HWY_EXPORT(YCbCrToRGB); -HWY_EXPORT(YCbCrToBGR); -HWY_EXPORT(YCbCrToRGBA); -HWY_EXPORT(YCbCrToBGRA); -HWY_EXPORT(YCbCrToARGB); -HWY_EXPORT(YCbCrToABGR); -HWY_EXPORT(RGBToYCbCr); -HWY_EXPORT(BGRToYCbCr); -HWY_EXPORT(ARGBToYCbCr); -HWY_EXPORT(ABGRToYCbCr); - -bool CheckColorSpaceComponents(int num_components, J_COLOR_SPACE colorspace) { - switch (colorspace) { - case JCS_GRAYSCALE: - return num_components == 1; - case JCS_RGB: - case JCS_YCbCr: -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - case JCS_EXT_BGR: -#endif - return num_components == 3; - case JCS_CMYK: - case JCS_YCCK: -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGBX: - case JCS_EXT_BGRX: - case JCS_EXT_XBGR: - case JCS_EXT_XRGB: -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_EXT_ABGR: - case JCS_EXT_ARGB: -#endif - return num_components == 4; - default: - // Unrecognized colorspaces can have any number of channels, since no - // color transform will be performed on them. - return true; - } -} - -void NullTransform(float* row[kMaxComponents], size_t len) {} - -void FillAlpha(float* row, size_t len) { - static const float kAlpha = 127.0f / 255.0f; - for (size_t i = 0; i < len; ++i) { - row[i] = kAlpha; - } -} - -// Works for BGR as well. -void GrayscaleToRGB(float* row[kMaxComponents], size_t len) { - memcpy(row[1], row[0], len * sizeof(row[1][0])); - memcpy(row[2], row[0], len * sizeof(row[2][0])); -} - -// Works for BGRA as well. -void GrayscaleToRGBA(float* row[kMaxComponents], size_t len) { - memcpy(row[1], row[0], len * sizeof(row[1][0])); - memcpy(row[2], row[0], len * sizeof(row[2][0])); - FillAlpha(row[3], len); -} - -// Works for ABGR as well. -void GrayscaleToARGB(float* row[kMaxComponents], size_t len) { - memcpy(row[1], row[0], len * sizeof(row[1][0])); - memcpy(row[2], row[0], len * sizeof(row[2][0])); - memcpy(row[3], row[0], len * sizeof(row[1][0])); - FillAlpha(row[0], len); -} - -void GrayscaleToYCbCr(float* row[kMaxComponents], size_t len) { - memset(row[1], 0, len * sizeof(row[1][0])); - memset(row[2], 0, len * sizeof(row[2][0])); -} - -void RGBToBGR(float* row[kMaxComponents], size_t len) { - for (size_t i = 0; i < len; ++i) { - std::swap(row[0][i], row[2][i]); - } -} - -void RGBToRGBA(float* row[kMaxComponents], size_t len) { - FillAlpha(row[3], len); -} - -void RGBToBGRA(float* row[kMaxComponents], size_t len) { - static const float kAlpha = 127.0f / 255.0f; - for (size_t i = 0; i < len; ++i) { - std::swap(row[0][i], row[2][i]); - row[3][i] = kAlpha; - } -} - -void RGBToARGB(float* row[kMaxComponents], size_t len) { - memcpy(row[3], row[2], len * sizeof(row[1][0])); - memcpy(row[2], row[1], len * sizeof(row[2][0])); - memcpy(row[1], row[0], len * sizeof(row[1][0])); - FillAlpha(row[0], len); -} - -void RGBToABGR(float* row[kMaxComponents], size_t len) { - static const float kAlpha = 127.0f / 255.0f; - for (size_t i = 0; i < len; ++i) { - std::swap(row[1][i], row[2][i]); - row[3][i] = row[0][i]; - row[0][i] = kAlpha; - } -} - -void ChooseColorTransform(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - if (!CheckColorSpaceComponents(cinfo->input_components, - cinfo->in_color_space)) { - JPEGLI_ERROR("Invalid number of input components %d for colorspace %d", - cinfo->input_components, cinfo->in_color_space); - } - if (!CheckColorSpaceComponents(cinfo->num_components, - cinfo->jpeg_color_space)) { - JPEGLI_ERROR("Invalid number of components %d for colorspace %d", - cinfo->num_components, cinfo->jpeg_color_space); - } - if (cinfo->jpeg_color_space == cinfo->in_color_space) { - if (cinfo->num_components != cinfo->input_components) { - JPEGLI_ERROR("Input/output components mismatch: %d vs %d", - cinfo->input_components, cinfo->num_components); - } - // No color transform requested. - m->color_transform = NullTransform; - return; - } - - if (cinfo->in_color_space == JCS_RGB && m->xyb_mode) { - JPEGLI_ERROR("Color transform on XYB colorspace is not supported."); - } - - m->color_transform = nullptr; - if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - if (cinfo->in_color_space == JCS_RGB) { - m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr); - } else if (cinfo->in_color_space == JCS_YCbCr || - cinfo->in_color_space == JCS_YCCK) { - // Since the first luminance channel is the grayscale version of the - // image, nothing to do here - m->color_transform = NullTransform; - } - } else if (cinfo->jpeg_color_space == JCS_RGB) { - if (cinfo->in_color_space == JCS_GRAYSCALE) { - m->color_transform = GrayscaleToRGB; - } - } else if (cinfo->jpeg_color_space == JCS_YCbCr) { - if (cinfo->in_color_space == JCS_RGB) { - m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr); - } else if (cinfo->in_color_space == JCS_GRAYSCALE) { - m->color_transform = GrayscaleToYCbCr; - } - } else if (cinfo->jpeg_color_space == JCS_YCCK) { - if (cinfo->in_color_space == JCS_CMYK) { - m->color_transform = HWY_DYNAMIC_DISPATCH(CMYKToYCCK); - } - } - - if (cinfo->jpeg_color_space == JCS_GRAYSCALE || - cinfo->jpeg_color_space == JCS_YCbCr) { - switch (cinfo->in_color_space) { -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - case JCS_EXT_RGBX: - m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr); - break; - case JCS_EXT_BGR: - case JCS_EXT_BGRX: - m->color_transform = HWY_DYNAMIC_DISPATCH(BGRToYCbCr); - break; - case JCS_EXT_XRGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(ARGBToYCbCr); - break; - case JCS_EXT_XBGR: - m->color_transform = HWY_DYNAMIC_DISPATCH(ABGRToYCbCr); - break; -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr); - break; - case JCS_EXT_BGRA: - m->color_transform = HWY_DYNAMIC_DISPATCH(BGRToYCbCr); - break; - case JCS_EXT_ARGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(ARGBToYCbCr); - break; - case JCS_EXT_ABGR: - m->color_transform = HWY_DYNAMIC_DISPATCH(ABGRToYCbCr); - break; -#endif - default:; // Nothing to do. - } - } - - if (m->color_transform == nullptr) { - // TODO(szabadka) Support more color transforms. - JPEGLI_ERROR("Unsupported color transform %d -> %d", cinfo->in_color_space, - cinfo->jpeg_color_space); - } -} - -void ChooseColorTransform(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (!CheckColorSpaceComponents(cinfo->out_color_components, - cinfo->out_color_space)) { - JPEGLI_ERROR("Invalid number of output components %d for colorspace %d", - cinfo->out_color_components, cinfo->out_color_space); - } - if (!CheckColorSpaceComponents(cinfo->num_components, - cinfo->jpeg_color_space)) { - JPEGLI_ERROR("Invalid number of components %d for colorspace %d", - cinfo->num_components, cinfo->jpeg_color_space); - } - if (cinfo->jpeg_color_space == cinfo->out_color_space) { - if (cinfo->num_components != cinfo->out_color_components) { - JPEGLI_ERROR("Input/output components mismatch: %d vs %d", - cinfo->num_components, cinfo->out_color_components); - } - // No color transform requested. - m->color_transform = NullTransform; - return; - } - - m->color_transform = nullptr; - if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - switch (cinfo->out_color_space) { - case JCS_RGB: - m->color_transform = GrayscaleToRGB; - break; -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - case JCS_EXT_BGR: - m->color_transform = GrayscaleToRGB; - break; - case JCS_EXT_RGBX: - case JCS_EXT_BGRX: - m->color_transform = GrayscaleToRGBA; - break; - case JCS_EXT_XRGB: - case JCS_EXT_XBGR: - m->color_transform = GrayscaleToARGB; - break; -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - m->color_transform = GrayscaleToRGBA; - break; - case JCS_EXT_ARGB: - case JCS_EXT_ABGR: - m->color_transform = GrayscaleToARGB; - break; -#endif - default: - m->color_transform = nullptr; - } - } else if (cinfo->jpeg_color_space == JCS_RGB) { - switch (cinfo->out_color_space) { - case JCS_GRAYSCALE: - m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr); - break; -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - m->color_transform = NullTransform; - break; - case JCS_EXT_BGR: - m->color_transform = RGBToBGR; - break; - case JCS_EXT_RGBX: - m->color_transform = RGBToRGBA; - break; - case JCS_EXT_BGRX: - m->color_transform = RGBToBGRA; - break; - case JCS_EXT_XRGB: - m->color_transform = RGBToARGB; - break; - case JCS_EXT_XBGR: - m->color_transform = RGBToABGR; - break; -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - m->color_transform = RGBToRGBA; - break; - case JCS_EXT_BGRA: - m->color_transform = RGBToBGRA; - break; - case JCS_EXT_ARGB: - m->color_transform = RGBToARGB; - break; - case JCS_EXT_ABGR: - m->color_transform = RGBToABGR; - break; -#endif - default: - m->color_transform = nullptr; - } - } else if (cinfo->jpeg_color_space == JCS_YCbCr) { - switch (cinfo->out_color_space) { - case JCS_GRAYSCALE: - m->color_transform = NullTransform; - break; - case JCS_RGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGB); - break; -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGB); - break; - case JCS_EXT_BGR: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToBGR); - break; - case JCS_EXT_RGBX: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGBA); - break; - case JCS_EXT_BGRX: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToBGRA); - break; - case JCS_EXT_XRGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToARGB); - break; - case JCS_EXT_XBGR: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToABGR); - break; -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGBA); - break; - case JCS_EXT_BGRA: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToBGRA); - break; - case JCS_EXT_ARGB: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToARGB); - break; - case JCS_EXT_ABGR: - m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToABGR); - break; -#endif - default: - m->color_transform = nullptr; - } - } else if (cinfo->jpeg_color_space == JCS_YCCK) { - if (cinfo->out_color_space == JCS_CMYK) { - m->color_transform = HWY_DYNAMIC_DISPATCH(YCCKToCMYK); - } - } - - if (m->color_transform == nullptr) { - // TODO(szabadka) Support more color transforms. - JPEGLI_ERROR("Unsupported color transform %d -> %d", - cinfo->jpeg_color_space, cinfo->out_color_space); - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/color_transform.h b/third_party/jpeg-xl/lib/jpegli/color_transform.h deleted file mode 100644 index 8d58f8849a4e9..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/color_transform.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_COLOR_TRANSFORM_H_ -#define LIB_JPEGLI_COLOR_TRANSFORM_H_ - -#include "lib/jpegli/common.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jpegli { - -void ChooseColorTransform(j_compress_ptr cinfo); - -void ChooseColorTransform(j_decompress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_COLOR_TRANSFORM_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/common.cc b/third_party/jpeg-xl/lib/jpegli/common.cc deleted file mode 100644 index 5f34372f3e534..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/common.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/common.h" - -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/memory_manager.h" - -void jpegli_abort(j_common_ptr cinfo) { - if (cinfo->mem == nullptr) return; - for (int pool_id = 0; pool_id < JPOOL_NUMPOOLS; ++pool_id) { - if (pool_id == JPOOL_PERMANENT) continue; - (*cinfo->mem->free_pool)(cinfo, pool_id); - } - if (cinfo->is_decompressor) { - cinfo->global_state = jpegli::kDecStart; - } else { - cinfo->global_state = jpegli::kEncStart; - } -} - -void jpegli_destroy(j_common_ptr cinfo) { - if (cinfo->mem == nullptr) return; - (*cinfo->mem->self_destruct)(cinfo); - if (cinfo->is_decompressor) { - cinfo->global_state = jpegli::kDecNull; - delete reinterpret_cast(cinfo)->master; - } else { - cinfo->global_state = jpegli::kEncNull; - } -} - -JQUANT_TBL* jpegli_alloc_quant_table(j_common_ptr cinfo) { - JQUANT_TBL* table = jpegli::Allocate(cinfo, 1); - table->sent_table = FALSE; - return table; -} - -JHUFF_TBL* jpegli_alloc_huff_table(j_common_ptr cinfo) { - JHUFF_TBL* table = jpegli::Allocate(cinfo, 1); - table->sent_table = FALSE; - return table; -} - -int jpegli_bytes_per_sample(JpegliDataType data_type) { - switch (data_type) { - case JPEGLI_TYPE_UINT8: - return 1; - case JPEGLI_TYPE_UINT16: - return 2; - case JPEGLI_TYPE_FLOAT: - return 4; - default: - return 0; - } -} diff --git a/third_party/jpeg-xl/lib/jpegli/common.h b/third_party/jpeg-xl/lib/jpegli/common.h deleted file mode 100644 index 731a2e9d3f361..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/common.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file contains the C API of the common encoder/decoder part of libjpegli -// library, which is based on the C API of libjpeg, with the function names -// changed from jpeg_* to jpegli_*, while compressor and decompressor object -// definitions are included directly from jpeglib.h -// -// Applications can use the libjpegli library in one of the following ways: -// -// (1) Include jpegli/encode.h and/or jpegli/decode.h, update the function -// names of the API and link against libjpegli. -// -// (2) Leave the application code unchanged, but replace the libjpeg.so library -// with the one built by this project that is API- and ABI-compatible with -// libjpeg-turbo's version of libjpeg.so. - -#ifndef LIB_JPEGLI_COMMON_H_ -#define LIB_JPEGLI_COMMON_H_ - -#include "lib/jxl/base/include_jpeglib.h" // NOLINT - -#ifdef __cplusplus -extern "C" { -#endif - -struct jpeg_error_mgr* jpegli_std_error(struct jpeg_error_mgr* err); - -void jpegli_abort(j_common_ptr cinfo); - -void jpegli_destroy(j_common_ptr cinfo); - -JQUANT_TBL* jpegli_alloc_quant_table(j_common_ptr cinfo); - -JHUFF_TBL* jpegli_alloc_huff_table(j_common_ptr cinfo); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // LIB_JPEGLI_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/common_internal.h b/third_party/jpeg-xl/lib/jpegli/common_internal.h deleted file mode 100644 index bd0a86d3b0864..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/common_internal.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_COMMON_INTERNAL_H_ -#define LIB_JPEGLI_COMMON_INTERNAL_H_ - -#include -#include -#include - -// Suppress any -Wdeprecated-declarations warning that might be emitted by -// GCC or Clang by std::stable_sort in C++17 or later mode -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) -#pragma GCC push_options -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC pop_options -#endif - -#include - -#include "lib/jpegli/memory_manager.h" -#include "lib/jpegli/simd.h" -#include "lib/jxl/base/compiler_specific.h" // for ssize_t - -namespace jpegli { - -enum State { - kDecNull, - kDecStart, - kDecInHeader, - kDecHeaderDone, - kDecProcessMarkers, - kDecProcessScan, - kEncNull, - kEncStart, - kEncHeader, - kEncReadImage, - kEncWriteCoeffs, -}; - -template -constexpr inline T1 DivCeil(T1 a, T2 b) { - return (a + b - 1) / b; -} - -template -constexpr inline T1 RoundUpTo(T1 a, T2 b) { - return DivCeil(a, b) * b; -} - -constexpr size_t kDCTBlockSize = 64; -// This is set to the same value as MAX_COMPS_IN_SCAN, because that is the -// maximum number of channels the libjpeg-turbo decoder can decode. -constexpr int kMaxComponents = 4; -constexpr int kMaxQuantTables = 4; -constexpr int kJpegPrecision = 8; -constexpr int kMaxHuffmanTables = 4; -constexpr size_t kJpegHuffmanMaxBitLength = 16; -constexpr int kJpegHuffmanAlphabetSize = 256; -constexpr int kJpegDCAlphabetSize = 12; -constexpr int kMaxDHTMarkers = 512; -constexpr int kMaxDimPixels = 65535; -constexpr uint8_t kApp1 = 0xE1; -constexpr uint8_t kApp2 = 0xE2; -const uint8_t kIccProfileTag[12] = "ICC_PROFILE"; -const uint8_t kExifTag[6] = "Exif\0"; -const uint8_t kXMPTag[29] = "http://ns.adobe.com/xap/1.0/"; - -/* clang-format off */ -constexpr uint32_t kJPEGNaturalOrder[80] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // extra entries for safety in decoder - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63 -}; - -constexpr uint32_t kJPEGZigZagOrder[64] = { - 0, 1, 5, 6, 14, 15, 27, 28, - 2, 4, 7, 13, 16, 26, 29, 42, - 3, 8, 12, 17, 25, 30, 41, 43, - 9, 11, 18, 24, 31, 40, 44, 53, - 10, 19, 23, 32, 39, 45, 52, 54, - 20, 22, 33, 38, 46, 51, 55, 60, - 21, 34, 37, 47, 50, 56, 59, 61, - 35, 36, 48, 49, 57, 58, 62, 63 -}; -/* clang-format on */ - -template -class RowBuffer { - public: - template - void Allocate(CInfoType cinfo, size_t num_rows, size_t rowsize) { - static_assert(sizeof(T) == 4); - size_t vec_size = std::max(VectorSize(), sizeof(T)); - size_t alignment = std::max(HWY_ALIGNMENT, vec_size); - size_t min_memstride = alignment + rowsize * sizeof(T) + vec_size; - size_t memstride = RoundUpTo(min_memstride, alignment); - xsize_ = rowsize; - ysize_ = num_rows; - stride_ = memstride / sizeof(T); - offset_ = alignment / sizeof(T); - data_ = ::jpegli::Allocate(cinfo, ysize_ * stride_, JPOOL_IMAGE_ALIGNED); - } - - T* Row(ssize_t y) const { - return &data_[((ysize_ + y) % ysize_) * stride_ + offset_]; - } - - size_t xsize() const { return xsize_; }; - size_t ysize() const { return ysize_; }; - size_t stride() const { return stride_; } - - void PadRow(size_t y, size_t from, int border) { - float* row = Row(y); - for (int offset = -border; offset < 0; ++offset) { - row[offset] = row[0]; - } - float last_val = row[from - 1]; - for (size_t x = from; x < xsize_ + border; ++x) { - row[x] = last_val; - } - } - - void CopyRow(ssize_t dst_row, ssize_t src_row, int border) { - memcpy(Row(dst_row) - border, Row(src_row) - border, - (xsize_ + 2 * border) * sizeof(T)); - } - - void FillRow(ssize_t y, T val, size_t len) { - T* row = Row(y); - for (size_t x = 0; x < len; ++x) { - row[x] = val; - } - } - - private: - size_t xsize_; - size_t ysize_; - size_t stride_; - size_t offset_; - T* data_; -}; - -} // namespace jpegli - -#endif // LIB_JPEGLI_COMMON_INTERNAL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/dct-inl.h b/third_party/jpeg-xl/lib/jpegli/dct-inl.h deleted file mode 100644 index fb54a152d59c3..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/dct-inl.h +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#if defined(LIB_JPEGLI_DCT_INL_H_) == defined(HWY_TARGET_TOGGLE) -#ifdef LIB_JPEGLI_DCT_INL_H_ -#undef LIB_JPEGLI_DCT_INL_H_ -#else -#define LIB_JPEGLI_DCT_INL_H_ -#endif - -#include "lib/jpegli/transpose-inl.h" -#include "lib/jxl/base/compiler_specific.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { -namespace { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Abs; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::DemoteTo; -using hwy::HWY_NAMESPACE::Ge; -using hwy::HWY_NAMESPACE::IfThenElseZero; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::Round; -using hwy::HWY_NAMESPACE::Sub; -using hwy::HWY_NAMESPACE::Vec; - -using D = HWY_FULL(float); -using DI = HWY_FULL(int32_t); - -template -void AddReverse(const float* JXL_RESTRICT a_in1, - const float* JXL_RESTRICT a_in2, float* JXL_RESTRICT a_out) { - HWY_CAPPED(float, 8) d8; - for (size_t i = 0; i < N; i++) { - auto in1 = Load(d8, a_in1 + i * 8); - auto in2 = Load(d8, a_in2 + (N - i - 1) * 8); - Store(Add(in1, in2), d8, a_out + i * 8); - } -} - -template -void SubReverse(const float* JXL_RESTRICT a_in1, - const float* JXL_RESTRICT a_in2, float* JXL_RESTRICT a_out) { - HWY_CAPPED(float, 8) d8; - for (size_t i = 0; i < N; i++) { - auto in1 = Load(d8, a_in1 + i * 8); - auto in2 = Load(d8, a_in2 + (N - i - 1) * 8); - Store(Sub(in1, in2), d8, a_out + i * 8); - } -} - -template -void B(float* JXL_RESTRICT coeff) { - HWY_CAPPED(float, 8) d8; - constexpr float kSqrt2 = 1.41421356237f; - auto sqrt2 = Set(d8, kSqrt2); - auto in1 = Load(d8, coeff); - auto in2 = Load(d8, coeff + 8); - Store(MulAdd(in1, sqrt2, in2), d8, coeff); - for (size_t i = 1; i + 1 < N; i++) { - auto in1 = Load(d8, coeff + i * 8); - auto in2 = Load(d8, coeff + (i + 1) * 8); - Store(Add(in1, in2), d8, coeff + i * 8); - } -} - -// Ideally optimized away by compiler (except the multiply). -template -void InverseEvenOdd(const float* JXL_RESTRICT a_in, float* JXL_RESTRICT a_out) { - HWY_CAPPED(float, 8) d8; - for (size_t i = 0; i < N / 2; i++) { - auto in1 = Load(d8, a_in + i * 8); - Store(in1, d8, a_out + 2 * i * 8); - } - for (size_t i = N / 2; i < N; i++) { - auto in1 = Load(d8, a_in + i * 8); - Store(in1, d8, a_out + (2 * (i - N / 2) + 1) * 8); - } -} - -// Constants for DCT implementation. Generated by the following snippet: -// for i in range(N // 2): -// print(1.0 / (2 * math.cos((i + 0.5) * math.pi / N)), end=", ") -template -struct WcMultipliers; - -template <> -struct WcMultipliers<4> { - static constexpr float kMultipliers[] = { - 0.541196100146197, - 1.3065629648763764, - }; -}; - -template <> -struct WcMultipliers<8> { - static constexpr float kMultipliers[] = { - 0.5097955791041592, - 0.6013448869350453, - 0.8999762231364156, - 2.5629154477415055, - }; -}; - -#if JXL_CXX_LANG < JXL_CXX_17 -constexpr float WcMultipliers<4>::kMultipliers[]; -constexpr float WcMultipliers<8>::kMultipliers[]; -#endif - -// Invoked on full vector. -template -void Multiply(float* JXL_RESTRICT coeff) { - HWY_CAPPED(float, 8) d8; - for (size_t i = 0; i < N / 2; i++) { - auto in1 = Load(d8, coeff + (N / 2 + i) * 8); - auto mul = Set(d8, WcMultipliers::kMultipliers[i]); - Store(Mul(in1, mul), d8, coeff + (N / 2 + i) * 8); - } -} - -void LoadFromBlock(const float* JXL_RESTRICT pixels, size_t pixels_stride, - size_t off, float* JXL_RESTRICT coeff) { - HWY_CAPPED(float, 8) d8; - for (size_t i = 0; i < 8; i++) { - Store(LoadU(d8, pixels + i * pixels_stride + off), d8, coeff + i * 8); - } -} - -void StoreToBlockAndScale(const float* JXL_RESTRICT coeff, float* output, - size_t off) { - HWY_CAPPED(float, 8) d8; - auto mul = Set(d8, 1.0f / 8); - for (size_t i = 0; i < 8; i++) { - StoreU(Mul(mul, Load(d8, coeff + i * 8)), d8, output + i * 8 + off); - } -} - -template -struct DCT1DImpl; - -template <> -struct DCT1DImpl<1> { - JXL_INLINE void operator()(float* JXL_RESTRICT mem) {} -}; - -template <> -struct DCT1DImpl<2> { - JXL_INLINE void operator()(float* JXL_RESTRICT mem) { - HWY_CAPPED(float, 8) d8; - auto in1 = Load(d8, mem); - auto in2 = Load(d8, mem + 8); - Store(Add(in1, in2), d8, mem); - Store(Sub(in1, in2), d8, mem + 8); - } -}; - -template -struct DCT1DImpl { - void operator()(float* JXL_RESTRICT mem) { - HWY_ALIGN float tmp[N * 8]; - AddReverse(mem, mem + N * 4, tmp); - DCT1DImpl()(tmp); - SubReverse(mem, mem + N * 4, tmp + N * 4); - Multiply(tmp); - DCT1DImpl()(tmp + N * 4); - B(tmp + N * 4); - InverseEvenOdd(tmp, mem); - } -}; - -void DCT1D(const float* JXL_RESTRICT pixels, size_t pixels_stride, - float* JXL_RESTRICT output) { - HWY_CAPPED(float, 8) d8; - HWY_ALIGN float tmp[64]; - for (size_t i = 0; i < 8; i += Lanes(d8)) { - // TODO(veluca): consider removing the temporary memory here (as is done in - // IDCT), if it turns out that some compilers don't optimize away the loads - // and this is performance-critical. - LoadFromBlock(pixels, pixels_stride, i, tmp); - DCT1DImpl<8>()(tmp); - StoreToBlockAndScale(tmp, output, i); - } -} - -JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels( - const float* JXL_RESTRICT pixels, size_t pixels_stride, - float* JXL_RESTRICT coefficients, float* JXL_RESTRICT scratch_space) { - DCT1D(pixels, pixels_stride, scratch_space); - Transpose8x8Block(scratch_space, coefficients); - DCT1D(coefficients, 8, scratch_space); - Transpose8x8Block(scratch_space, coefficients); -} - -JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec& ival, - int16_t* out) { - Rebind di16; - Store(DemoteTo(di16, ival), di16, out); -} - -JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec& ival, - int32_t* out) { - DI di; - Store(ival, di, out); -} - -template -void QuantizeBlock(const float* dct, const float* qmc, float aq_strength, - const float* zero_bias_offset, const float* zero_bias_mul, - T* block) { - D d; - DI di; - const auto aq_mul = Set(d, aq_strength); - for (size_t k = 0; k < DCTSIZE2; k += Lanes(d)) { - const auto val = Load(d, dct + k); - const auto q = Load(d, qmc + k); - const auto qval = Mul(val, q); - const auto zb_offset = Load(d, zero_bias_offset + k); - const auto zb_mul = Load(d, zero_bias_mul + k); - const auto threshold = Add(zb_offset, Mul(zb_mul, aq_mul)); - const auto nzero_mask = Ge(Abs(qval), threshold); - const auto ival = ConvertTo(di, IfThenElseZero(nzero_mask, Round(qval))); - StoreQuantizedValue(ival, block + k); - } -} - -template -void ComputeCoefficientBlock(const float* JXL_RESTRICT pixels, size_t stride, - const float* JXL_RESTRICT qmc, - int16_t last_dc_coeff, float aq_strength, - const float* zero_bias_offset, - const float* zero_bias_mul, - float* JXL_RESTRICT tmp, T* block) { - float* JXL_RESTRICT dct = tmp; - float* JXL_RESTRICT scratch_space = tmp + DCTSIZE2; - TransformFromPixels(pixels, stride, dct, scratch_space); - QuantizeBlock(dct, qmc, aq_strength, zero_bias_offset, zero_bias_mul, block); - // Center DC values around zero. - static constexpr float kDCBias = 128.0f; - const float dc = (dct[0] - kDCBias) * qmc[0]; - float dc_threshold = zero_bias_offset[0] + aq_strength * zero_bias_mul[0]; - if (std::abs(dc - last_dc_coeff) < dc_threshold) { - block[0] = last_dc_coeff; - } else { - block[0] = std::round(dc); - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); -#endif // LIB_JPEGLI_DCT_INL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/decode.cc b/third_party/jpeg-xl/lib/jpegli/decode.cc deleted file mode 100644 index 832f0df87681b..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode.cc +++ /dev/null @@ -1,1060 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/decode.h" - -#include - -#include - -#include "lib/jpegli/color_quantize.h" -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/decode_marker.h" -#include "lib/jpegli/decode_scan.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jpegli/render.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { - -void InitializeImage(j_decompress_ptr cinfo) { - cinfo->restart_interval = 0; - cinfo->saw_JFIF_marker = FALSE; - cinfo->JFIF_major_version = 1; - cinfo->JFIF_minor_version = 1; - cinfo->density_unit = 0; - cinfo->X_density = 1; - cinfo->Y_density = 1; - cinfo->saw_Adobe_marker = FALSE; - cinfo->Adobe_transform = 0; - cinfo->CCIR601_sampling = FALSE; // not used - cinfo->marker_list = nullptr; - cinfo->comp_info = nullptr; - cinfo->input_scan_number = 0; - cinfo->input_iMCU_row = 0; - cinfo->output_scan_number = 0; - cinfo->output_iMCU_row = 0; - cinfo->output_scanline = 0; - cinfo->unread_marker = 0; - cinfo->coef_bits = nullptr; - // We set all these to zero since we don't yet support arithmetic coding. - memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L)); - memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U)); - memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K)); - // Initialize the private fields. - jpeg_decomp_master* m = cinfo->master; - m->input_buffer_.clear(); - m->input_buffer_pos_ = 0; - m->codestream_bits_ahead_ = 0; - m->is_multiscan_ = false; - m->found_soi_ = false; - m->found_dri_ = false; - m->found_sof_ = false; - m->found_sos_ = false; - m->found_eoi_ = false; - m->icc_index_ = 0; - m->icc_total_ = 0; - m->icc_profile_.clear(); - memset(m->dc_huff_lut_, 0, sizeof(m->dc_huff_lut_)); - memset(m->ac_huff_lut_, 0, sizeof(m->ac_huff_lut_)); - // Initialize the values to an invalid symbol so that we can recognize it - // when reading the bit stream using a Huffman code with space > 0. - for (size_t i = 0; i < kAllHuffLutSize; ++i) { - m->dc_huff_lut_[i].bits = 0; - m->dc_huff_lut_[i].value = 0xffff; - m->ac_huff_lut_[i].bits = 0; - m->ac_huff_lut_[i].value = 0xffff; - } - m->colormap_lut_ = nullptr; - m->pixels_ = nullptr; - m->scanlines_ = nullptr; - m->regenerate_inverse_colormap_ = true; - for (int i = 0; i < kMaxComponents; ++i) { - m->dither_[i] = nullptr; - m->error_row_[i] = nullptr; - } - m->output_passes_done_ = 0; - m->xoffset_ = 0; - m->dequant_ = nullptr; -} - -void InitializeDecompressParams(j_decompress_ptr cinfo) { - cinfo->jpeg_color_space = JCS_UNKNOWN; - cinfo->out_color_space = JCS_UNKNOWN; - cinfo->scale_num = 1; - cinfo->scale_denom = 1; - cinfo->output_gamma = 0.0f; - cinfo->buffered_image = FALSE; - cinfo->raw_data_out = FALSE; - cinfo->dct_method = JDCT_DEFAULT; - cinfo->do_fancy_upsampling = TRUE; - cinfo->do_block_smoothing = TRUE; - cinfo->quantize_colors = FALSE; - cinfo->dither_mode = JDITHER_FS; - cinfo->two_pass_quantize = TRUE; - cinfo->desired_number_of_colors = 256; - cinfo->enable_1pass_quant = FALSE; - cinfo->enable_external_quant = FALSE; - cinfo->enable_2pass_quant = FALSE; - cinfo->actual_number_of_colors = 0; - cinfo->colormap = nullptr; -} - -void InitProgressMonitor(j_decompress_ptr cinfo, bool coef_only) { - if (!cinfo->progress) return; - jpeg_decomp_master* m = cinfo->master; - int nc = cinfo->num_components; - int estimated_num_scans = - cinfo->progressive_mode ? 2 + 3 * nc : (m->is_multiscan_ ? nc : 1); - cinfo->progress->pass_limit = cinfo->total_iMCU_rows * estimated_num_scans; - cinfo->progress->pass_counter = 0; - if (coef_only) { - cinfo->progress->total_passes = 1; - } else { - int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0; - bool two_pass_quant = FROM_JXL_BOOL(cinfo->quantize_colors) && - (cinfo->colormap != nullptr) && - FROM_JXL_BOOL(cinfo->two_pass_quantize) && - FROM_JXL_BOOL(cinfo->enable_2pass_quant); - cinfo->progress->total_passes = input_passes + (two_pass_quant ? 2 : 1); - } - cinfo->progress->completed_passes = 0; -} - -void InitProgressMonitorForOutput(j_decompress_ptr cinfo) { - if (!cinfo->progress) return; - jpeg_decomp_master* m = cinfo->master; - int passes_per_output = cinfo->enable_2pass_quant ? 2 : 1; - int output_passes_left = cinfo->buffered_image && !m->found_eoi_ ? 2 : 1; - cinfo->progress->total_passes = - m->output_passes_done_ + passes_per_output * output_passes_left; - cinfo->progress->completed_passes = m->output_passes_done_; -} - -void ProgressMonitorInputPass(j_decompress_ptr cinfo) { - if (!cinfo->progress) return; - cinfo->progress->pass_counter = - ((cinfo->input_scan_number - 1) * cinfo->total_iMCU_rows + - cinfo->input_iMCU_row); - if (cinfo->progress->pass_counter > cinfo->progress->pass_limit) { - cinfo->progress->pass_limit = - cinfo->input_scan_number * cinfo->total_iMCU_rows; - } - (*cinfo->progress->progress_monitor)(reinterpret_cast(cinfo)); -} - -void ProgressMonitorOutputPass(j_decompress_ptr cinfo) { - if (!cinfo->progress) return; - jpeg_decomp_master* m = cinfo->master; - int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0; - cinfo->progress->pass_counter = cinfo->output_scanline; - cinfo->progress->pass_limit = cinfo->output_height; - cinfo->progress->completed_passes = input_passes + m->output_passes_done_; - (*cinfo->progress->progress_monitor)(reinterpret_cast(cinfo)); -} - -void BuildHuffmanLookupTable(j_decompress_ptr cinfo, JHUFF_TBL* table, - HuffmanTableEntry* huff_lut) { - uint32_t counts[kJpegHuffmanMaxBitLength + 1] = {}; - counts[0] = 0; - int total_count = 0; - int space = 1 << kJpegHuffmanMaxBitLength; - int max_depth = 1; - for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) { - int count = table->bits[i]; - if (count != 0) { - max_depth = i; - } - counts[i] = count; - total_count += count; - space -= count * (1 << (kJpegHuffmanMaxBitLength - i)); - } - uint32_t values[kJpegHuffmanAlphabetSize + 1] = {}; - uint8_t values_seen[256] = {0}; - for (int i = 0; i < total_count; ++i) { - int value = table->huffval[i]; - if (values_seen[value]) { - JPEGLI_ERROR("Duplicate Huffman code value %d", value); - } - values_seen[value] = 1; - values[i] = value; - } - // Add an invalid symbol that will have the all 1 code. - ++counts[max_depth]; - values[total_count] = kJpegHuffmanAlphabetSize; - space -= (1 << (kJpegHuffmanMaxBitLength - max_depth)); - if (space < 0) { - JPEGLI_ERROR("Invalid Huffman code lengths."); - } else if (space > 0 && huff_lut[0].value != 0xffff) { - // Re-initialize the values to an invalid symbol so that we can recognize - // it when reading the bit stream using a Huffman code with space > 0. - for (int i = 0; i < kJpegHuffmanLutSize; ++i) { - huff_lut[i].bits = 0; - huff_lut[i].value = 0xffff; - } - } - BuildJpegHuffmanTable(&counts[0], &values[0], huff_lut); -} - -void PrepareForScan(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - int comp_idx = cinfo->cur_comp_info[i]->component_index; - int* prev_coef_bits = cinfo->coef_bits[comp_idx + cinfo->num_components]; - for (int k = std::min(cinfo->Ss, 1); k <= std::max(cinfo->Se, 9); k++) { - prev_coef_bits[k] = - (cinfo->input_scan_number > 0) ? cinfo->coef_bits[comp_idx][k] : 0; - } - for (int k = cinfo->Ss; k <= cinfo->Se; ++k) { - cinfo->coef_bits[comp_idx][k] = cinfo->Al; - } - } - AddStandardHuffmanTables(reinterpret_cast(cinfo), - /*is_dc=*/false); - AddStandardHuffmanTables(reinterpret_cast(cinfo), - /*is_dc=*/true); - // Check that all the Huffman tables needed for this scan are defined and - // build derived lookup tables. - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - if (cinfo->Ss == 0) { - int dc_tbl_idx = cinfo->cur_comp_info[i]->dc_tbl_no; - JHUFF_TBL* table = cinfo->dc_huff_tbl_ptrs[dc_tbl_idx]; - HuffmanTableEntry* huff_lut = - &m->dc_huff_lut_[dc_tbl_idx * kJpegHuffmanLutSize]; - if (!table) { - JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx); - } - BuildHuffmanLookupTable(cinfo, table, huff_lut); - } - if (cinfo->Se > 0) { - int ac_tbl_idx = cinfo->cur_comp_info[i]->ac_tbl_no; - JHUFF_TBL* table = cinfo->ac_huff_tbl_ptrs[ac_tbl_idx]; - HuffmanTableEntry* huff_lut = - &m->ac_huff_lut_[ac_tbl_idx * kJpegHuffmanLutSize]; - if (!table) { - JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx); - } - BuildHuffmanLookupTable(cinfo, table, huff_lut); - } - } - // Copy quantization tables into comp_info. - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - jpeg_component_info* comp = cinfo->cur_comp_info[i]; - int quant_tbl_idx = comp->quant_tbl_no; - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_tbl_idx]; - if (!quant_table) { - JPEGLI_ERROR("Quantization table with index %d not found", quant_tbl_idx); - } - if (comp->quant_table == nullptr) { - comp->quant_table = Allocate(cinfo, 1, JPOOL_IMAGE); - memcpy(comp->quant_table, quant_table, sizeof(JQUANT_TBL)); - } - } - if (cinfo->comps_in_scan == 1) { - const auto& comp = *cinfo->cur_comp_info[0]; - cinfo->MCUs_per_row = DivCeil(cinfo->image_width * comp.h_samp_factor, - cinfo->max_h_samp_factor * DCTSIZE); - cinfo->MCU_rows_in_scan = DivCeil(cinfo->image_height * comp.v_samp_factor, - cinfo->max_v_samp_factor * DCTSIZE); - m->mcu_rows_per_iMCU_row_ = cinfo->cur_comp_info[0]->v_samp_factor; - } else { - cinfo->MCU_rows_in_scan = cinfo->total_iMCU_rows; - cinfo->MCUs_per_row = m->iMCU_cols_; - m->mcu_rows_per_iMCU_row_ = 1; - size_t mcu_size = 0; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - jpeg_component_info* comp = cinfo->cur_comp_info[i]; - mcu_size += comp->h_samp_factor * comp->v_samp_factor; - } - if (mcu_size > D_MAX_BLOCKS_IN_MCU) { - JPEGLI_ERROR("MCU size too big"); - } - } - memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_)); - m->restarts_to_go_ = cinfo->restart_interval; - m->next_restart_marker_ = 0; - m->eobrun_ = -1; - m->scan_mcu_row_ = 0; - m->scan_mcu_col_ = 0; - m->codestream_bits_ahead_ = 0; - ++cinfo->input_scan_number; - cinfo->input_iMCU_row = 0; - PrepareForiMCURow(cinfo); - cinfo->global_state = kDecProcessScan; -} - -int ConsumeInput(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (cinfo->global_state == kDecProcessScan && m->streaming_mode_ && - cinfo->input_iMCU_row > cinfo->output_iMCU_row) { - // Prevent input from getting ahead of output in streaming mode. - return JPEG_SUSPENDED; - } - jpeg_source_mgr* src = cinfo->src; - int status; - for (;;) { - const uint8_t* data; - size_t len; - if (m->input_buffer_.empty()) { - data = cinfo->src->next_input_byte; - len = cinfo->src->bytes_in_buffer; - } else { - data = &m->input_buffer_[m->input_buffer_pos_]; - len = m->input_buffer_.size() - m->input_buffer_pos_; - } - size_t pos = 0; - if (cinfo->global_state == kDecProcessScan) { - status = ProcessScan(cinfo, data, len, &pos, &m->codestream_bits_ahead_); - } else { - status = ProcessMarkers(cinfo, data, len, &pos); - } - if (m->input_buffer_.empty()) { - cinfo->src->next_input_byte += pos; - cinfo->src->bytes_in_buffer -= pos; - } else { - m->input_buffer_pos_ += pos; - size_t bytes_left = m->input_buffer_.size() - m->input_buffer_pos_; - if (bytes_left <= src->bytes_in_buffer) { - src->next_input_byte += (src->bytes_in_buffer - bytes_left); - src->bytes_in_buffer = bytes_left; - m->input_buffer_.clear(); - m->input_buffer_pos_ = 0; - } - } - if (status == kHandleRestart) { - JXL_DASSERT(m->input_buffer_.size() <= - m->input_buffer_pos_ + src->bytes_in_buffer); - m->input_buffer_.clear(); - m->input_buffer_pos_ = 0; - if (cinfo->unread_marker == 0xd0 + m->next_restart_marker_) { - cinfo->unread_marker = 0; - } else { - if (!(*cinfo->src->resync_to_restart)(cinfo, m->next_restart_marker_)) { - return JPEG_SUSPENDED; - } - } - m->next_restart_marker_ += 1; - m->next_restart_marker_ &= 0x7; - m->restarts_to_go_ = cinfo->restart_interval; - if (cinfo->unread_marker != 0) { - JPEGLI_WARN("Failed to resync to next restart marker, skipping scan."); - return JPEG_SCAN_COMPLETED; - } - continue; - } - if (status == kHandleMarkerProcessor) { - JXL_DASSERT(m->input_buffer_.size() <= - m->input_buffer_pos_ + src->bytes_in_buffer); - m->input_buffer_.clear(); - m->input_buffer_pos_ = 0; - if (!(*GetMarkerProcessor(cinfo))(cinfo)) { - return JPEG_SUSPENDED; - } - cinfo->unread_marker = 0; - continue; - } - if (status != kNeedMoreInput) { - break; - } - if (m->input_buffer_.empty()) { - JXL_DASSERT(m->input_buffer_pos_ == 0); - m->input_buffer_.assign(src->next_input_byte, - src->next_input_byte + src->bytes_in_buffer); - } - if (!(*cinfo->src->fill_input_buffer)(cinfo)) { - m->input_buffer_.clear(); - m->input_buffer_pos_ = 0; - return JPEG_SUSPENDED; - } - if (src->bytes_in_buffer == 0) { - JPEGLI_ERROR("Empty input."); - } - m->input_buffer_.insert(m->input_buffer_.end(), src->next_input_byte, - src->next_input_byte + src->bytes_in_buffer); - } - if (status == JPEG_SCAN_COMPLETED) { - cinfo->global_state = kDecProcessMarkers; - } else if (status == JPEG_REACHED_SOS) { - if (cinfo->global_state == kDecInHeader) { - cinfo->global_state = kDecHeaderDone; - } else { - PrepareForScan(cinfo); - } - } - return status; -} - -bool IsInputReady(j_decompress_ptr cinfo) { - if (cinfo->master->found_eoi_) { - return true; - } - if (cinfo->input_scan_number > cinfo->output_scan_number) { - return true; - } - if (cinfo->input_scan_number < cinfo->output_scan_number) { - return false; - } - if (cinfo->input_iMCU_row == cinfo->total_iMCU_rows) { - return true; - } - return cinfo->input_iMCU_row > - cinfo->output_iMCU_row + (cinfo->master->streaming_mode_ ? 0 : 2); -} - -bool ReadOutputPass(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (!m->pixels_) { - size_t stride = cinfo->out_color_components * cinfo->output_width; - size_t num_samples = cinfo->output_height * stride; - m->pixels_ = Allocate(cinfo, num_samples, JPOOL_IMAGE); - m->scanlines_ = - Allocate(cinfo, cinfo->output_height, JPOOL_IMAGE); - for (size_t i = 0; i < cinfo->output_height; ++i) { - m->scanlines_[i] = &m->pixels_[i * stride]; - } - } - size_t num_output_rows = 0; - while (num_output_rows < cinfo->output_height) { - if (IsInputReady(cinfo)) { - ProgressMonitorOutputPass(cinfo); - ProcessOutput(cinfo, &num_output_rows, m->scanlines_, - cinfo->output_height); - } else if (ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return false; - } - } - cinfo->output_scanline = 0; - cinfo->output_iMCU_row = 0; - return true; -} - -boolean PrepareQuantizedOutput(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (cinfo->raw_data_out) { - JPEGLI_ERROR("Color quantization is not supported in raw data mode."); - } - if (m->output_data_type_ != JPEGLI_TYPE_UINT8) { - JPEGLI_ERROR("Color quantization must use 8-bit mode."); - } - if (cinfo->colormap) { - m->quant_mode_ = 3; - } else if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { - m->quant_mode_ = 2; - } else if (cinfo->enable_1pass_quant) { - m->quant_mode_ = 1; - } else { - JPEGLI_ERROR("Invalid quantization mode change"); - } - if (m->quant_mode_ > 1 && cinfo->dither_mode == JDITHER_ORDERED) { - cinfo->dither_mode = JDITHER_FS; - } - if (m->quant_mode_ == 1) { - ChooseColorMap1Pass(cinfo); - } else if (m->quant_mode_ == 2) { - m->quant_pass_ = 0; - if (!ReadOutputPass(cinfo)) { - return FALSE; - } - ChooseColorMap2Pass(cinfo); - } - if (m->quant_mode_ == 2 || - (m->quant_mode_ == 3 && m->regenerate_inverse_colormap_)) { - CreateInverseColorMap(cinfo); - } - if (cinfo->dither_mode == JDITHER_ORDERED) { - CreateOrderedDitherTables(cinfo); - } else if (cinfo->dither_mode == JDITHER_FS) { - InitFSDitherState(cinfo); - } - m->quant_pass_ = 1; - return TRUE; -} - -void AllocateCoefficientBuffer(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - j_common_ptr comptr = reinterpret_cast(cinfo); - jvirt_barray_ptr* coef_arrays = jpegli::Allocate( - cinfo, cinfo->num_components, JPOOL_IMAGE); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - size_t height_in_blocks = - m->streaming_mode_ ? comp->v_samp_factor : comp->height_in_blocks; - coef_arrays[c] = (*cinfo->mem->request_virt_barray)( - comptr, JPOOL_IMAGE, TRUE, comp->width_in_blocks, height_in_blocks, - comp->v_samp_factor); - } - cinfo->master->coef_arrays = coef_arrays; - (*cinfo->mem->realize_virt_arrays)(comptr); -} - -void AllocateOutputBuffers(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - size_t iMCU_width = cinfo->max_h_samp_factor * m->min_scaled_dct_size; - size_t output_stride = m->iMCU_cols_ * iMCU_width; - m->need_context_rows_ = false; - for (int c = 0; c < cinfo->num_components; ++c) { - if (cinfo->do_fancy_upsampling && m->v_factor[c] == 2) { - m->need_context_rows_ = true; - } - } - for (int c = 0; c < cinfo->num_components; ++c) { - const auto& comp = cinfo->comp_info[c]; - size_t cheight = comp.v_samp_factor * m->scaled_dct_size[c]; - int downsampled_width = output_stride / m->h_factor[c]; - m->raw_height_[c] = comp.height_in_blocks * m->scaled_dct_size[c]; - if (m->need_context_rows_) { - cheight *= 3; - } - m->raw_output_[c].Allocate(cinfo, cheight, downsampled_width); - } - int num_all_components = - std::max(cinfo->out_color_components, cinfo->num_components); - for (int c = 0; c < num_all_components; ++c) { - m->render_output_[c].Allocate(cinfo, cinfo->max_v_samp_factor, - output_stride); - } - m->idct_scratch_ = Allocate(cinfo, 5 * DCTSIZE2, JPOOL_IMAGE_ALIGNED); - // Padding for horizontal chroma upsampling. - constexpr size_t kPaddingLeft = 64; - constexpr size_t kPaddingRight = 64; - m->upsample_scratch_ = Allocate( - cinfo, output_stride + kPaddingLeft + kPaddingRight, JPOOL_IMAGE_ALIGNED); - size_t bytes_per_sample = jpegli_bytes_per_sample(m->output_data_type_); - size_t bytes_per_pixel = cinfo->out_color_components * bytes_per_sample; - size_t scratch_stride = RoundUpTo(output_stride, HWY_ALIGNMENT); - m->output_scratch_ = Allocate( - cinfo, bytes_per_pixel * scratch_stride, JPOOL_IMAGE_ALIGNED); - m->smoothing_scratch_ = - Allocate(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED); - size_t coeffs_per_block = cinfo->num_components * DCTSIZE2; - m->nonzeros_ = Allocate(cinfo, coeffs_per_block, JPOOL_IMAGE_ALIGNED); - m->sumabs_ = Allocate(cinfo, coeffs_per_block, JPOOL_IMAGE_ALIGNED); - m->biases_ = Allocate(cinfo, coeffs_per_block, JPOOL_IMAGE_ALIGNED); - m->dequant_ = Allocate(cinfo, coeffs_per_block, JPOOL_IMAGE_ALIGNED); - memset(m->dequant_, 0, coeffs_per_block * sizeof(float)); -} - -} // namespace jpegli - -void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version, - size_t structsize) { - cinfo->mem = nullptr; - if (structsize != sizeof(*cinfo)) { - JPEGLI_ERROR("jpeg_decompress_struct has wrong size."); - } - jpegli::InitMemoryManager(reinterpret_cast(cinfo)); - cinfo->is_decompressor = TRUE; - cinfo->progress = nullptr; - cinfo->src = nullptr; - for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) { - quant_tbl_ptr = nullptr; - } - for (int i = 0; i < NUM_HUFF_TBLS; i++) { - cinfo->dc_huff_tbl_ptrs[i] = nullptr; - cinfo->ac_huff_tbl_ptrs[i] = nullptr; - } - cinfo->global_state = jpegli::kDecStart; - cinfo->sample_range_limit = nullptr; // not used - cinfo->rec_outbuf_height = 1; // output works with any buffer height - cinfo->master = new jpeg_decomp_master; - jpeg_decomp_master* m = cinfo->master; - for (auto& app_marker_parser : m->app_marker_parsers) { - app_marker_parser = nullptr; - } - m->com_marker_parser = nullptr; - memset(m->markers_to_save_, 0, sizeof(m->markers_to_save_)); - jpegli::InitializeDecompressParams(cinfo); - jpegli::InitializeImage(cinfo); -} - -void jpegli_destroy_decompress(j_decompress_ptr cinfo) { - jpegli_destroy(reinterpret_cast(cinfo)); -} - -void jpegli_abort_decompress(j_decompress_ptr cinfo) { - jpegli_abort(reinterpret_cast(cinfo)); -} - -void jpegli_save_markers(j_decompress_ptr cinfo, int marker_code, - unsigned int length_limit) { - // TODO(szabadka) Limit our memory usage by taking into account length_limit. - jpeg_decomp_master* m = cinfo->master; - if (marker_code < 0xe0) { - JPEGLI_ERROR("jpegli_save_markers: invalid marker code %d", marker_code); - } - m->markers_to_save_[marker_code - 0xe0] = 1; -} - -void jpegli_set_marker_processor(j_decompress_ptr cinfo, int marker_code, - jpeg_marker_parser_method routine) { - jpeg_decomp_master* m = cinfo->master; - if (marker_code == 0xfe) { - m->com_marker_parser = routine; - } else if (marker_code >= 0xe0 && marker_code <= 0xef) { - m->app_marker_parsers[marker_code - 0xe0] = routine; - } else { - JPEGLI_ERROR("jpegli_set_marker_processor: invalid marker code %d", - marker_code); - } -} - -int jpegli_consume_input(j_decompress_ptr cinfo) { - if (cinfo->global_state == jpegli::kDecStart) { - (*cinfo->err->reset_error_mgr)(reinterpret_cast(cinfo)); - (*cinfo->src->init_source)(cinfo); - jpegli::InitializeDecompressParams(cinfo); - jpegli::InitializeImage(cinfo); - cinfo->global_state = jpegli::kDecInHeader; - } - if (cinfo->global_state == jpegli::kDecHeaderDone) { - return JPEG_REACHED_SOS; - } - if (cinfo->master->found_eoi_) { - return JPEG_REACHED_EOI; - } - if (cinfo->global_state == jpegli::kDecInHeader || - cinfo->global_state == jpegli::kDecProcessMarkers || - cinfo->global_state == jpegli::kDecProcessScan) { - return jpegli::ConsumeInput(cinfo); - } - JPEGLI_ERROR("Unexpected state %d", cinfo->global_state); - return JPEG_REACHED_EOI; // return value does not matter -} - -int jpegli_read_header(j_decompress_ptr cinfo, boolean require_image) { - if (cinfo->global_state != jpegli::kDecStart && - cinfo->global_state != jpegli::kDecInHeader) { - JPEGLI_ERROR("jpegli_read_header: unexpected state %d", - cinfo->global_state); - } - if (cinfo->src == nullptr) { - JPEGLI_ERROR("Missing source."); - } - for (;;) { - int retcode = jpegli_consume_input(cinfo); - if (retcode == JPEG_SUSPENDED) { - return retcode; - } else if (retcode == JPEG_REACHED_SOS) { - break; - } else if (retcode == JPEG_REACHED_EOI) { - if (require_image) { - JPEGLI_ERROR("jpegli_read_header: unexpected EOI marker."); - } - jpegli_abort_decompress(cinfo); - return JPEG_HEADER_TABLES_ONLY; - } - }; - return JPEG_HEADER_OK; -} - -boolean jpegli_read_icc_profile(j_decompress_ptr cinfo, JOCTET** icc_data_ptr, - unsigned int* icc_data_len) { - if (cinfo->global_state == jpegli::kDecStart || - cinfo->global_state == jpegli::kDecInHeader) { - JPEGLI_ERROR("jpegli_read_icc_profile: unexpected state %d", - cinfo->global_state); - } - if (icc_data_ptr == nullptr || icc_data_len == nullptr) { - JPEGLI_ERROR("jpegli_read_icc_profile: invalid output buffer"); - } - jpeg_decomp_master* m = cinfo->master; - if (m->icc_profile_.empty()) { - *icc_data_ptr = nullptr; - *icc_data_len = 0; - return FALSE; - } - *icc_data_len = m->icc_profile_.size(); - *icc_data_ptr = static_cast(malloc(*icc_data_len)); - if (*icc_data_ptr == nullptr) { - JPEGLI_ERROR("jpegli_read_icc_profile: Out of memory"); - } - memcpy(*icc_data_ptr, m->icc_profile_.data(), *icc_data_len); - return TRUE; -} - -void jpegli_core_output_dimensions(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (!m->found_sof_) { - JPEGLI_ERROR("No SOF marker found."); - } - if (cinfo->raw_data_out) { - if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) { - JPEGLI_ERROR("Output scaling is not supported in raw output mode"); - } - } - if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) { - int dctsize = 16; - while (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * (dctsize - 1)) { - --dctsize; - } - m->min_scaled_dct_size = dctsize; - cinfo->output_width = - jpegli::DivCeil(cinfo->image_width * dctsize, DCTSIZE); - cinfo->output_height = - jpegli::DivCeil(cinfo->image_height * dctsize, DCTSIZE); - for (int c = 0; c < cinfo->num_components; ++c) { - m->scaled_dct_size[c] = m->min_scaled_dct_size; - } - } else { - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - m->min_scaled_dct_size = DCTSIZE; - for (int c = 0; c < cinfo->num_components; ++c) { - m->scaled_dct_size[c] = DCTSIZE; - } - } -} - -void jpegli_calc_output_dimensions(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - jpegli_core_output_dimensions(cinfo); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - m->h_factor[c] = cinfo->max_h_samp_factor / comp->h_samp_factor; - m->v_factor[c] = cinfo->max_v_samp_factor / comp->v_samp_factor; - } - if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) { - for (int c = 0; c < cinfo->num_components; ++c) { - // Prefer IDCT scaling over 2x upsampling. - while (m->scaled_dct_size[c] < DCTSIZE && (m->v_factor[c] % 2) == 0 && - (m->h_factor[c] % 2) == 0) { - m->scaled_dct_size[c] *= 2; - m->v_factor[c] /= 2; - m->h_factor[c] /= 2; - } - } - } - switch (cinfo->out_color_space) { - case JCS_GRAYSCALE: - cinfo->out_color_components = 1; - break; - case JCS_RGB: - case JCS_YCbCr: -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - case JCS_EXT_BGR: -#endif - cinfo->out_color_components = 3; - break; - case JCS_CMYK: - case JCS_YCCK: -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGBX: - case JCS_EXT_BGRX: - case JCS_EXT_XBGR: - case JCS_EXT_XRGB: -#endif -#ifdef JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_EXT_ABGR: - case JCS_EXT_ARGB: -#endif - cinfo->out_color_components = 4; - break; - default: - cinfo->out_color_components = cinfo->num_components; - } - cinfo->output_components = - cinfo->quantize_colors ? 1 : cinfo->out_color_components; - cinfo->rec_outbuf_height = 1; -} - -boolean jpegli_has_multiple_scans(j_decompress_ptr cinfo) { - if (cinfo->global_state != jpegli::kDecHeaderDone && - cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_has_multiple_scans: unexpected state %d", - cinfo->global_state); - } - return TO_JXL_BOOL(cinfo->master->is_multiscan_); -} - -boolean jpegli_input_complete(j_decompress_ptr cinfo) { - return TO_JXL_BOOL(cinfo->master->found_eoi_); -} - -boolean jpegli_start_decompress(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - if (cinfo->global_state == jpegli::kDecHeaderDone) { - m->streaming_mode_ = !m->is_multiscan_ && - !FROM_JXL_BOOL(cinfo->buffered_image) && - (!FROM_JXL_BOOL(cinfo->quantize_colors) || - !FROM_JXL_BOOL(cinfo->two_pass_quantize)); - jpegli::AllocateCoefficientBuffer(cinfo); - jpegli_calc_output_dimensions(cinfo); - jpegli::PrepareForScan(cinfo); - if (cinfo->quantize_colors) { - if (cinfo->colormap != nullptr) { - cinfo->enable_external_quant = TRUE; - } else if (cinfo->two_pass_quantize && - cinfo->out_color_space == JCS_RGB) { - cinfo->enable_2pass_quant = TRUE; - } else { - cinfo->enable_1pass_quant = TRUE; - } - } - jpegli::InitProgressMonitor(cinfo, /*coef_only=*/false); - jpegli::AllocateOutputBuffers(cinfo); - if (cinfo->buffered_image == TRUE) { - cinfo->output_scan_number = 0; - return TRUE; - } - } else if (!m->is_multiscan_) { - JPEGLI_ERROR("jpegli_start_decompress: unexpected state %d", - cinfo->global_state); - } - if (m->is_multiscan_) { - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_start_decompress: unexpected state %d", - cinfo->global_state); - } - while (!m->found_eoi_) { - jpegli::ProgressMonitorInputPass(cinfo); - if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return FALSE; - } - } - } - cinfo->output_scan_number = cinfo->input_scan_number; - jpegli::PrepareForOutput(cinfo); - if (cinfo->quantize_colors) { - return jpegli::PrepareQuantizedOutput(cinfo); - } else { - return TRUE; - } -} - -boolean jpegli_start_output(j_decompress_ptr cinfo, int scan_number) { - jpeg_decomp_master* m = cinfo->master; - if (!cinfo->buffered_image) { - JPEGLI_ERROR("jpegli_start_output: buffered image mode was not set"); - } - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_start_output: unexpected state %d", - cinfo->global_state); - } - cinfo->output_scan_number = std::max(1, scan_number); - if (m->found_eoi_) { - cinfo->output_scan_number = - std::min(cinfo->output_scan_number, cinfo->input_scan_number); - } - jpegli::InitProgressMonitorForOutput(cinfo); - jpegli::PrepareForOutput(cinfo); - if (cinfo->quantize_colors) { - return jpegli::PrepareQuantizedOutput(cinfo); - } else { - return TRUE; - } -} - -boolean jpegli_finish_output(j_decompress_ptr cinfo) { - if (!cinfo->buffered_image) { - JPEGLI_ERROR("jpegli_finish_output: buffered image mode was not set"); - } - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_finish_output: unexpected state %d", - cinfo->global_state); - } - // Advance input to the start of the next scan, or to the end of input. - while (cinfo->input_scan_number <= cinfo->output_scan_number && - !cinfo->master->found_eoi_) { - if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return FALSE; - } - } - return TRUE; -} - -JDIMENSION jpegli_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION max_lines) { - jpeg_decomp_master* m = cinfo->master; - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_read_scanlines: unexpected state %d", - cinfo->global_state); - } - if (cinfo->buffered_image) { - if (cinfo->output_scan_number == 0) { - JPEGLI_ERROR( - "jpegli_read_scanlines: " - "jpegli_start_output() was not called"); - } - } else if (m->is_multiscan_ && !m->found_eoi_) { - JPEGLI_ERROR( - "jpegli_read_scanlines: " - "jpegli_start_decompress() did not finish"); - } - if (cinfo->output_scanline + max_lines > cinfo->output_height) { - max_lines = cinfo->output_height - cinfo->output_scanline; - } - jpegli::ProgressMonitorOutputPass(cinfo); - size_t num_output_rows = 0; - while (num_output_rows < max_lines) { - if (jpegli::IsInputReady(cinfo)) { - jpegli::ProcessOutput(cinfo, &num_output_rows, scanlines, max_lines); - } else if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - break; - } - } - return num_output_rows; -} - -JDIMENSION jpegli_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { - // TODO(szabadka) Skip the IDCT for skipped over blocks. - return jpegli_read_scanlines(cinfo, nullptr, num_lines); -} - -void jpegli_crop_scanline(j_decompress_ptr cinfo, JDIMENSION* xoffset, - JDIMENSION* width) { - jpeg_decomp_master* m = cinfo->master; - if ((cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) || - cinfo->output_scanline != 0) { - JPEGLI_ERROR("jpegli_crop_decompress: unexpected state %d", - cinfo->global_state); - } - if (cinfo->raw_data_out) { - JPEGLI_ERROR("Output cropping is not supported in raw data mode"); - } - if (xoffset == nullptr || width == nullptr || *width == 0 || - *xoffset + *width > cinfo->output_width) { - JPEGLI_ERROR("jpegli_crop_scanline: Invalid arguments"); - } - // TODO(szabadka) Skip the IDCT for skipped over blocks. - size_t xend = *xoffset + *width; - size_t iMCU_width = m->min_scaled_dct_size * cinfo->max_h_samp_factor; - *xoffset = (*xoffset / iMCU_width) * iMCU_width; - *width = xend - *xoffset; - cinfo->master->xoffset_ = *xoffset; - cinfo->output_width = *width; -} - -JDIMENSION jpegli_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION max_lines) { - if ((cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) || - !cinfo->raw_data_out) { - JPEGLI_ERROR("jpegli_read_raw_data: unexpected state %d", - cinfo->global_state); - } - size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE; - if (max_lines < iMCU_height) { - JPEGLI_ERROR("jpegli_read_raw_data: output buffer too small"); - } - jpegli::ProgressMonitorOutputPass(cinfo); - while (!jpegli::IsInputReady(cinfo)) { - if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return 0; - } - } - if (cinfo->output_iMCU_row < cinfo->total_iMCU_rows) { - jpegli::ProcessRawOutput(cinfo, data); - return iMCU_height; - } - return 0; -} - -jvirt_barray_ptr* jpegli_read_coefficients(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - m->streaming_mode_ = false; - if (!cinfo->buffered_image && cinfo->global_state == jpegli::kDecHeaderDone) { - jpegli::AllocateCoefficientBuffer(cinfo); - jpegli_calc_output_dimensions(cinfo); - jpegli::InitProgressMonitor(cinfo, /*coef_only=*/true); - jpegli::PrepareForScan(cinfo); - } - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_read_coefficients: unexpected state %d", - cinfo->global_state); - } - if (!cinfo->buffered_image) { - while (!m->found_eoi_) { - jpegli::ProgressMonitorInputPass(cinfo); - if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return nullptr; - } - } - cinfo->output_scanline = cinfo->output_height; - } - return m->coef_arrays; -} - -boolean jpegli_finish_decompress(j_decompress_ptr cinfo) { - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_finish_decompress: unexpected state %d", - cinfo->global_state); - } - if (!cinfo->buffered_image && cinfo->output_scanline < cinfo->output_height) { - JPEGLI_ERROR("Incomplete output"); - } - while (!cinfo->master->found_eoi_) { - if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { - return FALSE; - } - } - (*cinfo->src->term_source)(cinfo); - jpegli_abort_decompress(cinfo); - return TRUE; -} - -boolean jpegli_resync_to_restart(j_decompress_ptr cinfo, int desired) { - JPEGLI_WARN("Invalid restart marker found: 0x%02x vs 0x%02x.", - cinfo->unread_marker, 0xd0 + desired); - // This is a trivial implementation, we just let the decoder skip the entire - // scan and attempt to render the partial input. - return TRUE; -} - -void jpegli_new_colormap(j_decompress_ptr cinfo) { - if (cinfo->global_state != jpegli::kDecProcessScan && - cinfo->global_state != jpegli::kDecProcessMarkers) { - JPEGLI_ERROR("jpegli_new_colormap: unexpected state %d", - cinfo->global_state); - } - if (!cinfo->buffered_image) { - JPEGLI_ERROR("jpegli_new_colormap: not in buffered image mode"); - } - if (!cinfo->enable_external_quant) { - JPEGLI_ERROR("external colormap quantizer was not enabled"); - } - if (!cinfo->quantize_colors || cinfo->colormap == nullptr) { - JPEGLI_ERROR("jpegli_new_colormap: not in external colormap mode"); - } - cinfo->master->regenerate_inverse_colormap_ = true; -} - -void jpegli_set_output_format(j_decompress_ptr cinfo, JpegliDataType data_type, - JpegliEndianness endianness) { - switch (data_type) { - case JPEGLI_TYPE_UINT8: - case JPEGLI_TYPE_UINT16: - case JPEGLI_TYPE_FLOAT: - cinfo->master->output_data_type_ = data_type; - break; - default: - JPEGLI_ERROR("Unsupported data type %d", data_type); - } - switch (endianness) { - case JPEGLI_NATIVE_ENDIAN: - cinfo->master->swap_endianness_ = false; - break; - case JPEGLI_LITTLE_ENDIAN: - cinfo->master->swap_endianness_ = !IsLittleEndian(); - break; - case JPEGLI_BIG_ENDIAN: - cinfo->master->swap_endianness_ = IsLittleEndian(); - break; - default: - JPEGLI_ERROR("Unsupported endianness %d", endianness); - } -} diff --git a/third_party/jpeg-xl/lib/jpegli/decode.h b/third_party/jpeg-xl/lib/jpegli/decode.h deleted file mode 100644 index 78c39b409cd16..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file contains the C API of the decoder part of the libjpegli library, -// which is based on the C API of libjpeg, with the function names changed from -// jpeg_* to jpegli_*, while decompressor object definitions are included -// directly from jpeglib.h -// -// Applications can use the libjpegli library in one of the following ways: -// -// (1) Include jpegli/encode.h and/or jpegli/decode.h, update the function -// names of the API and link against libjpegli. -// -// (2) Leave the application code unchanged, but replace the libjpeg.so library -// with the one built by this project that is API- and ABI-compatible with -// libjpeg-turbo's version of libjpeg.so. - -#ifndef LIB_JPEGLI_DECODE_H_ -#define LIB_JPEGLI_DECODE_H_ - -#include "lib/jpegli/common.h" -#include "lib/jpegli/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define jpegli_create_decompress(cinfo) \ - jpegli_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ - (size_t)sizeof(struct jpeg_decompress_struct)) - -void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version, - size_t structsize); - -void jpegli_stdio_src(j_decompress_ptr cinfo, FILE *infile); - -void jpegli_mem_src(j_decompress_ptr cinfo, const unsigned char *inbuffer, - unsigned long insize /* NOLINT */); - -int jpegli_read_header(j_decompress_ptr cinfo, boolean require_image); - -boolean jpegli_start_decompress(j_decompress_ptr cinfo); - -JDIMENSION jpegli_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION max_lines); - -JDIMENSION jpegli_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines); - -void jpegli_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, - JDIMENSION *width); - -boolean jpegli_finish_decompress(j_decompress_ptr cinfo); - -JDIMENSION jpegli_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION max_lines); - -jvirt_barray_ptr *jpegli_read_coefficients(j_decompress_ptr cinfo); - -boolean jpegli_has_multiple_scans(j_decompress_ptr cinfo); - -boolean jpegli_start_output(j_decompress_ptr cinfo, int scan_number); - -boolean jpegli_finish_output(j_decompress_ptr cinfo); - -boolean jpegli_input_complete(j_decompress_ptr cinfo); - -int jpegli_consume_input(j_decompress_ptr cinfo); - -#if JPEG_LIB_VERSION >= 80 -void jpegli_core_output_dimensions(j_decompress_ptr cinfo); -#endif -void jpegli_calc_output_dimensions(j_decompress_ptr cinfo); - -void jpegli_save_markers(j_decompress_ptr cinfo, int marker_code, - unsigned int length_limit); - -void jpegli_set_marker_processor(j_decompress_ptr cinfo, int marker_code, - jpeg_marker_parser_method routine); - -boolean jpegli_resync_to_restart(j_decompress_ptr cinfo, int desired); - -boolean jpegli_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, - unsigned int *icc_data_len); - -void jpegli_abort_decompress(j_decompress_ptr cinfo); - -void jpegli_destroy_decompress(j_decompress_ptr cinfo); - -void jpegli_new_colormap(j_decompress_ptr cinfo); - -// -// New API functions that are not available in libjpeg -// -// NOTE: This part of the API is still experimental and will probably change in -// the future. -// - -void jpegli_set_output_format(j_decompress_ptr cinfo, JpegliDataType data_type, - JpegliEndianness endianness); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // LIB_JPEGLI_DECODE_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc deleted file mode 100644 index b7ce6bb4d7340..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc +++ /dev/null @@ -1,1340 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" -#include "lib/jpegli/types.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { -namespace { - -constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; -constexpr size_t kNumSourceBuffers = 4; - -// Custom source manager that refills the input buffer in chunks, simulating -// a file reader with a fixed buffer size. -class SourceManager { - public: - SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size) - : data_(data), len_(len), max_chunk_size_(max_chunk_size) { - pub_.skip_input_data = skip_input_data; - pub_.resync_to_restart = jpegli_resync_to_restart; - pub_.term_source = term_source; - pub_.init_source = init_source; - pub_.fill_input_buffer = fill_input_buffer; - if (max_chunk_size_ == 0) max_chunk_size_ = len; - buffers_.resize(kNumSourceBuffers, std::vector(max_chunk_size_)); - Reset(); - } - - void Reset() { - pub_.next_input_byte = nullptr; - pub_.bytes_in_buffer = 0; - pos_ = 0; - chunk_idx_ = 0; - } - - ~SourceManager() { - EXPECT_EQ(0, pub_.bytes_in_buffer); - EXPECT_EQ(len_, pos_); - } - - private: - jpeg_source_mgr pub_; - const uint8_t* data_; - size_t len_; - size_t chunk_idx_; - size_t pos_; - size_t max_chunk_size_; - std::vector> buffers_; - - static void init_source(j_decompress_ptr cinfo) {} - - static boolean fill_input_buffer(j_decompress_ptr cinfo) { - auto* src = reinterpret_cast(cinfo->src); - if (src->pos_ < src->len_) { - size_t chunk_size = std::min(src->len_ - src->pos_, src->max_chunk_size_); - size_t next_idx = ++src->chunk_idx_ % kNumSourceBuffers; - uint8_t* next_buffer = src->buffers_[next_idx].data(); - memcpy(next_buffer, src->data_ + src->pos_, chunk_size); - src->pub_.next_input_byte = next_buffer; - src->pub_.bytes_in_buffer = chunk_size; - } else { - src->pub_.next_input_byte = kFakeEoiMarker; - src->pub_.bytes_in_buffer = 2; - src->len_ += 2; - } - src->pos_ += src->pub_.bytes_in_buffer; - return TRUE; - } - - static void skip_input_data(j_decompress_ptr cinfo, - long num_bytes /* NOLINT */) { - auto* src = reinterpret_cast(cinfo->src); - if (num_bytes <= 0) { - return; - } - if (src->pub_.bytes_in_buffer >= static_cast(num_bytes)) { - src->pub_.bytes_in_buffer -= num_bytes; - src->pub_.next_input_byte += num_bytes; - } else { - src->pos_ += num_bytes - src->pub_.bytes_in_buffer; - src->pub_.bytes_in_buffer = 0; - } - } - - static void term_source(j_decompress_ptr cinfo) {} -}; - -uint8_t markers_seen[kMarkerSequenceLen]; -size_t num_markers_seen = 0; - -uint8_t get_next_byte(j_decompress_ptr cinfo) { - if (cinfo->src->bytes_in_buffer == 0) { - (*cinfo->src->fill_input_buffer)(cinfo); - } - cinfo->src->bytes_in_buffer--; - return *cinfo->src->next_input_byte++; -} - -boolean test_marker_processor(j_decompress_ptr cinfo) { - markers_seen[num_markers_seen] = cinfo->unread_marker; - size_t marker_len = (get_next_byte(cinfo) << 8) + get_next_byte(cinfo); - EXPECT_EQ(2 + ((num_markers_seen + 2) % sizeof(kMarkerData)), marker_len); - if (marker_len > 2) { - (*cinfo->src->skip_input_data)(cinfo, marker_len - 2); - } - ++num_markers_seen; - return TRUE; -} - -void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo, - TestImage* output) { - JDIMENSION xoffset = 0; - JDIMENSION yoffset = 0; - JDIMENSION xsize_cropped = cinfo->output_width; - JDIMENSION ysize_cropped = cinfo->output_height; - if (dparams.crop_output) { - xoffset = xsize_cropped = cinfo->output_width / 3; - yoffset = ysize_cropped = cinfo->output_height / 3; - jpegli_crop_scanline(cinfo, &xoffset, &xsize_cropped); - } - output->ysize = ysize_cropped; - output->xsize = cinfo->output_width; - output->components = cinfo->out_color_components; - output->data_type = dparams.data_type; - output->endianness = dparams.endianness; - size_t bytes_per_sample = jpegli_bytes_per_sample(dparams.data_type); - if (cinfo->raw_data_out) { - output->color_space = cinfo->jpeg_color_space; - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - std::vector plane(ysize * xsize * bytes_per_sample); - output->raw_data.emplace_back(std::move(plane)); - } - } else { - output->color_space = cinfo->out_color_space; - output->AllocatePixels(); - } - size_t total_output_lines = 0; - while (cinfo->output_scanline < cinfo->output_height) { - size_t max_lines; - size_t num_output_lines; - if (cinfo->raw_data_out) { - size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE; - EXPECT_EQ(cinfo->output_scanline, cinfo->output_iMCU_row * iMCU_height); - max_lines = iMCU_height; - std::vector> rowdata(cinfo->num_components); - std::vector data(cinfo->num_components); - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - size_t num_lines = cinfo->comp_info[c].v_samp_factor * DCTSIZE; - rowdata[c].resize(num_lines); - size_t y0 = cinfo->output_iMCU_row * num_lines; - for (size_t i = 0; i < num_lines; ++i) { - rowdata[c][i] = - y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; - } - data[c] = rowdata[c].data(); - } - num_output_lines = jpegli_read_raw_data(cinfo, data.data(), max_lines); - } else { - size_t max_output_lines = dparams.max_output_lines; - if (max_output_lines == 0) max_output_lines = cinfo->output_height; - if (cinfo->output_scanline < yoffset) { - max_lines = yoffset - cinfo->output_scanline; - num_output_lines = jpegli_skip_scanlines(cinfo, max_lines); - } else if (cinfo->output_scanline >= yoffset + ysize_cropped) { - max_lines = cinfo->output_height - cinfo->output_scanline; - num_output_lines = jpegli_skip_scanlines(cinfo, max_lines); - } else { - size_t lines_left = yoffset + ysize_cropped - cinfo->output_scanline; - max_lines = std::min(max_output_lines, lines_left); - size_t stride = cinfo->output_width * cinfo->out_color_components * - bytes_per_sample; - std::vector scanlines(max_lines); - for (size_t i = 0; i < max_lines; ++i) { - size_t yidx = cinfo->output_scanline - yoffset + i; - scanlines[i] = &output->pixels[yidx * stride]; - } - num_output_lines = - jpegli_read_scanlines(cinfo, scanlines.data(), max_lines); - if (cinfo->quantize_colors) { - for (size_t i = 0; i < num_output_lines; ++i) { - UnmapColors(scanlines[i], cinfo->output_width, - cinfo->out_color_components, cinfo->colormap, - cinfo->actual_number_of_colors); - } - } - } - } - total_output_lines += num_output_lines; - EXPECT_EQ(total_output_lines, cinfo->output_scanline); - EXPECT_EQ(num_output_lines, max_lines); - } - EXPECT_EQ(cinfo->total_iMCU_rows, - DivCeil(cinfo->image_height, cinfo->max_v_samp_factor * DCTSIZE)); -} - -struct TestConfig { - std::string fn; - std::string fn_desc; - TestImage input; - CompressParams jparams; - DecompressParams dparams; - bool compare_to_orig = false; - float max_tolerance_factor = 1.01f; - float max_rms_dist = 1.0f; - float max_diff = 35.0f; -}; - -jxl::StatusOr> GetTestJpegData(TestConfig& config) { - std::vector compressed; - if (!config.fn.empty()) { - JXL_ASSIGN_OR_RETURN(compressed, ReadTestData(config.fn)); - } else { - GeneratePixels(&config.input); - JXL_RETURN_IF_ERROR( - EncodeWithJpegli(config.input, config.jparams, &compressed)); - } - if (config.dparams.size_factor < 1.0f) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - return compressed; -} - -void TestAPINonBuffered(const CompressParams& jparams, - const DecompressParams& dparams, - const TestImage& expected_output, - j_decompress_ptr cinfo, TestImage* output) { - if (jparams.add_marker) { - jpegli_save_markers(cinfo, kSpecialMarker0, 0xffff); - jpegli_save_markers(cinfo, kSpecialMarker1, 0xffff); - num_markers_seen = 0; - jpegli_set_marker_processor(cinfo, 0xe6, test_marker_processor); - jpegli_set_marker_processor(cinfo, 0xe7, test_marker_processor); - jpegli_set_marker_processor(cinfo, 0xe8, test_marker_processor); - } - if (!jparams.icc.empty()) { - jpegli_save_markers(cinfo, JPEG_APP0 + 2, 0xffff); - } - jpegli_read_header(cinfo, /*require_image=*/TRUE); - if (jparams.add_marker) { - EXPECT_EQ(num_markers_seen, kMarkerSequenceLen); - EXPECT_EQ(0, memcmp(markers_seen, kMarkerSequence, num_markers_seen)); - } - if (!jparams.icc.empty()) { - uint8_t* icc_data = nullptr; - unsigned int icc_len; - ASSERT_TRUE(jpegli_read_icc_profile(cinfo, &icc_data, &icc_len)); - ASSERT_TRUE(icc_data); - EXPECT_EQ(0, memcmp(jparams.icc.data(), icc_data, icc_len)); - free(icc_data); - } - // Check that jpegli_calc_output_dimensions can be called multiple times - // even with different parameters. - if (!cinfo->raw_data_out) { - cinfo->scale_num = 1; - cinfo->scale_denom = 2; - } - jpegli_calc_output_dimensions(cinfo); - SetDecompressParams(dparams, cinfo); - jpegli_set_output_format(cinfo, dparams.data_type, dparams.endianness); - VerifyHeader(jparams, cinfo); - jpegli_calc_output_dimensions(cinfo); - EXPECT_LE(expected_output.xsize, cinfo->output_width); - if (!dparams.crop_output) { - EXPECT_EQ(expected_output.xsize, cinfo->output_width); - } - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(cinfo); - ASSERT_TRUE(coef_arrays != nullptr); - CopyCoefficients(cinfo, coef_arrays, output); - } else { - jpegli_start_decompress(cinfo); - VerifyScanHeader(jparams, cinfo); - ReadOutputImage(dparams, cinfo, output); - } - jpegli_finish_decompress(cinfo); -} - -void TestAPIBuffered(const CompressParams& jparams, - const DecompressParams& dparams, j_decompress_ptr cinfo, - std::vector* output_progression) { - EXPECT_EQ(JPEG_REACHED_SOS, - jpegli_read_header(cinfo, /*require_image=*/TRUE)); - cinfo->buffered_image = TRUE; - SetDecompressParams(dparams, cinfo); - jpegli_set_output_format(cinfo, dparams.data_type, dparams.endianness); - VerifyHeader(jparams, cinfo); - bool has_multiple_scans = FROM_JXL_BOOL(jpegli_has_multiple_scans(cinfo)); - EXPECT_TRUE(jpegli_start_decompress(cinfo)); - // start decompress should not read the whole input in buffered image mode - EXPECT_FALSE(jpegli_input_complete(cinfo)); - EXPECT_EQ(0, cinfo->output_scan_number); - int sos_marker_cnt = 1; // read_header reads the first SOS marker - while (!jpegli_input_complete(cinfo)) { - EXPECT_EQ(cinfo->input_scan_number, sos_marker_cnt); - if (dparams.skip_scans && (cinfo->input_scan_number % 2) != 1) { - int result = JPEG_SUSPENDED; - while (result != JPEG_REACHED_SOS && result != JPEG_REACHED_EOI) { - result = jpegli_consume_input(cinfo); - } - if (result == JPEG_REACHED_SOS) ++sos_marker_cnt; - continue; - } - SetScanDecompressParams(dparams, cinfo, cinfo->input_scan_number); - EXPECT_TRUE(jpegli_start_output(cinfo, cinfo->input_scan_number)); - // start output sets output_scan_number, but does not change - // input_scan_number - EXPECT_EQ(cinfo->output_scan_number, cinfo->input_scan_number); - EXPECT_EQ(cinfo->input_scan_number, sos_marker_cnt); - VerifyScanHeader(jparams, cinfo); - TestImage output; - ReadOutputImage(dparams, cinfo, &output); - output_progression->emplace_back(std::move(output)); - // read scanlines/read raw data does not change input/output scan number - EXPECT_EQ(cinfo->input_scan_number, sos_marker_cnt); - EXPECT_EQ(cinfo->output_scan_number, cinfo->input_scan_number); - EXPECT_TRUE(jpegli_finish_output(cinfo)); - ++sos_marker_cnt; // finish output reads the next SOS marker or EOI - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(cinfo); - ASSERT_TRUE(coef_arrays != nullptr); - CopyCoefficients(cinfo, coef_arrays, &output_progression->back()); - } - } - jpegli_finish_decompress(cinfo); - if (dparams.size_factor == 1.0f) { - EXPECT_EQ(has_multiple_scans, cinfo->input_scan_number > 1); - } -} - -TEST(DecodeAPITest, ReuseCinfo) { - TestImage input; - TestImage output; - TestImage expected; - std::vector output_progression; - std::vector expected_output_progression; - CompressParams jparams; - DecompressParams dparams; - std::vector compressed; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - input.xsize = 129; - input.ysize = 73; - GeneratePixels(&input); - for (int h_samp : {2, 1}) { - for (int v_samp : {2, 1}) { - for (int progr : {0, 2}) { - jparams.h_sampling = {h_samp, 1, 1}; - jparams.v_sampling = {v_samp, 1, 1}; - jparams.progressive_mode = progr; - printf( - "Generating input with %dx%d chroma subsampling " - "progressive level %d\n", - h_samp, v_samp, progr); - JPEGLI_TEST_ENSURE_TRUE( - EncodeWithJpegli(input, jparams, &compressed)); - for (JpegIOMode output_mode : {PIXELS, RAW_DATA, COEFFICIENTS}) { - for (bool crop : {true, false}) { - if (crop && output_mode != PIXELS) continue; - for (int scale_num : {1, 2, 3, 4, 7, 8, 13, 16}) { - if (scale_num != 8 && output_mode != PIXELS) continue; - int scale_denom = 8; - while (scale_num % 2 == 0 && scale_denom % 2 == 0) { - scale_num /= 2; - scale_denom /= 2; - } - printf("Decoding with output mode %d output scaling %d/%d %s\n", - output_mode, scale_num, scale_denom, - crop ? "with cropped output" : ""); - dparams.output_mode = output_mode; - dparams.scale_num = scale_num; - dparams.scale_denom = scale_denom; - expected.Clear(); - DecodeWithLibjpeg(jparams, dparams, compressed, &expected); - output.Clear(); - cinfo.buffered_image = JXL_FALSE; - cinfo.raw_data_out = JXL_FALSE; - cinfo.scale_num = cinfo.scale_denom = 1; - SourceManager src(compressed.data(), compressed.size(), - 1u << 12); - cinfo.src = reinterpret_cast(&src); - jpegli_read_header(&cinfo, /*require_image=*/TRUE); - jpegli_abort_decompress(&cinfo); - src.Reset(); - TestAPINonBuffered(jparams, dparams, expected, &cinfo, &output); - float max_rms = output_mode == COEFFICIENTS ? 0.0f : 1.0f; - if (scale_num == 1 && scale_denom == 8 && h_samp != v_samp) { - max_rms = 5.0f; // libjpeg does not do fancy upsampling - } - VerifyOutputImage(expected, output, max_rms); - printf("Decoding in buffered image mode\n"); - expected_output_progression.clear(); - DecodeAllScansWithLibjpeg(jparams, dparams, compressed, - &expected_output_progression); - output_progression.clear(); - src.Reset(); - TestAPIBuffered(jparams, dparams, &cinfo, &output_progression); - JPEGLI_TEST_ENSURE_TRUE(output_progression.size() == - expected_output_progression.size()); - for (size_t i = 0; i < output_progression.size(); ++i) { - const TestImage& output = output_progression[i]; - const TestImage& expected = expected_output_progression[i]; - VerifyOutputImage(expected, output, max_rms); - } - } - } - } - } - } - } - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -std::vector GenerateBasicConfigs() { - std::vector all_configs; - for (int samp : {1, 2}) { - for (int progr : {0, 2}) { - TestConfig config; - config.input.xsize = 257 + samp * 37; - config.input.ysize = 265 + (progr / 2) * 17; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - GeneratePixels(&config.input); - all_configs.push_back(config); - } - } - return all_configs; -} - -TEST(DecodeAPITest, ReuseCinfoSameMemSource) { - std::vector all_configs = GenerateBasicConfigs(); - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - for (const TestConfig& config : all_configs) { - EncodeWithJpegli(config.input, config.jparams, &cinfo); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - } - std::vector all_outputs(all_configs.size()); - { - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, buffer, buffer_size); - for (size_t i = 0; i < all_configs.size(); ++i) { - TestAPINonBuffered(all_configs[i].jparams, DecompressParams(), - all_configs[i].input, &cinfo, &all_outputs[i]); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - } - for (size_t i = 0; i < all_configs.size(); ++i) { - VerifyOutputImage(all_configs[i].input, all_outputs[i], 2.35f); - } - if (buffer) free(buffer); -} - -TEST(DecodeAPITest, ReuseCinfoSameStdSource) { - std::vector all_configs = GenerateBasicConfigs(); - FILE* tmpf = tmpfile(); - ASSERT_TRUE(tmpf); - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_stdio_dest(&cinfo, tmpf); - for (const TestConfig& config : all_configs) { - EncodeWithJpegli(config.input, config.jparams, &cinfo); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - } - fseek(tmpf, 0, SEEK_SET); - std::vector all_outputs(all_configs.size()); - { - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_stdio_src(&cinfo, tmpf); - for (size_t i = 0; i < all_configs.size(); ++i) { - TestAPINonBuffered(all_configs[i].jparams, DecompressParams(), - all_configs[i].input, &cinfo, &all_outputs[i]); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - } - for (size_t i = 0; i < all_configs.size(); ++i) { - VerifyOutputImage(all_configs[i].input, all_outputs[i], 2.35f); - } - fclose(tmpf); -} - -TEST(DecodeAPITest, AbbreviatedStreams) { - uint8_t* table_stream = nullptr; - unsigned long table_stream_size = 0; // NOLINT - uint8_t* data_stream = nullptr; - unsigned long data_stream_size = 0; // NOLINT - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &table_stream, &table_stream_size); - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - jpegli_write_tables(&cinfo); - jpegli_mem_dest(&cinfo, &data_stream, &data_stream_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.optimize_coding = FALSE; - jpegli_set_progressive_level(&cinfo, 0); - jpegli_start_compress(&cinfo, FALSE); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_TRUE(try_catch_block()); - EXPECT_LT(data_stream_size, 50); - jpegli_destroy_compress(&cinfo); - } - { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, table_stream, table_stream_size); - jpegli_read_header(&cinfo, FALSE); - jpegli_mem_src(&cinfo, data_stream, data_stream_size); - jpegli_read_header(&cinfo, TRUE); - EXPECT_EQ(1, cinfo.image_width); - EXPECT_EQ(1, cinfo.image_height); - EXPECT_EQ(3, cinfo.num_components); - jpegli_start_decompress(&cinfo); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_read_scanlines(&cinfo, row, 1); - EXPECT_EQ(0, image[0]); - EXPECT_EQ(0, image[1]); - EXPECT_EQ(0, image[2]); - jpegli_finish_decompress(&cinfo); - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - } - if (table_stream) free(table_stream); - if (data_stream) free(data_stream); -} - -class DecodeAPITestParam : public ::testing::TestWithParam {}; - -TEST_P(DecodeAPITestParam, TestAPI) { - TestConfig config = GetParam(); - const DecompressParams& dparams = config.dparams; - if (dparams.skip_scans) return; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data"); - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size); - - TestImage output1; - DecodeWithLibjpeg(config.jparams, dparams, compressed, &output1); - - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - cinfo.src = reinterpret_cast(&src); - TestAPINonBuffered(config.jparams, dparams, output1, &cinfo, &output0); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - if (config.compare_to_orig) { - double rms0 = DistanceRms(config.input, output0); - double rms1 = DistanceRms(config.input, output1); - printf("rms: %f vs %f\n", rms0, rms1); - EXPECT_LE(rms0, rms1 * config.max_tolerance_factor); - } else { - VerifyOutputImage(output0, output1, config.max_rms_dist, config.max_diff); - } -} - -class DecodeAPITestParamBuffered : public ::testing::TestWithParam { -}; - -TEST_P(DecodeAPITestParamBuffered, TestAPI) { - TestConfig config = GetParam(); - const DecompressParams& dparams = config.dparams; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data."); - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size); - - std::vector output_progression1; - DecodeAllScansWithLibjpeg(config.jparams, dparams, compressed, - &output_progression1); - - std::vector output_progression0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - cinfo.src = reinterpret_cast(&src); - TestAPIBuffered(config.jparams, dparams, &cinfo, &output_progression0); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - ASSERT_EQ(output_progression0.size(), output_progression1.size()); - for (size_t i = 0; i < output_progression0.size(); ++i) { - const TestImage& output = output_progression0[i]; - const TestImage& expected = output_progression1[i]; - if (config.compare_to_orig) { - double rms0 = DistanceRms(config.input, output); - double rms1 = DistanceRms(config.input, expected); - printf("rms: %f vs %f\n", rms0, rms1); - EXPECT_LE(rms0, rms1 * config.max_tolerance_factor); - } else { - VerifyOutputImage(expected, output, config.max_rms_dist, config.max_diff); - } - } -} - -std::vector GenerateTests(bool buffered) { - std::vector all_tests; - { - std::vector> testfiles({ - {"jxl/flower/flower.png.im_q85_420_progr.jpg", "Q85YUV420PROGR"}, - {"jxl/flower/flower.png.im_q85_420_R13B.jpg", "Q85YUV420R13B"}, - {"jxl/flower/flower.png.im_q85_444.jpg", "Q85YUV444"}, - }); - for (size_t i = 0; i < (buffered ? 1u : testfiles.size()); ++i) { - TestConfig config; - config.fn = testfiles[i].first; - config.fn_desc = testfiles[i].second; - for (size_t chunk_size : {0, 1, 64, 65536}) { - config.dparams.chunk_size = chunk_size; - for (size_t max_output_lines : {0, 1, 8, 16}) { - config.dparams.max_output_lines = max_output_lines; - config.dparams.output_mode = PIXELS; - all_tests.push_back(config); - } - { - config.dparams.max_output_lines = 16; - config.dparams.output_mode = RAW_DATA; - all_tests.push_back(config); - } - } - } - } - - { - std::vector> testfiles({ - {"jxl/flower/flower_small.q85_444_non_interleaved.jpg", - "Q85YUV444NonInterleaved"}, - {"jxl/flower/flower_small.q85_420_non_interleaved.jpg", - "Q85YUV420NonInterleaved"}, - {"jxl/flower/flower_small.q85_444_partially_interleaved.jpg", - "Q85YUV444PartiallyInterleaved"}, - {"jxl/flower/flower_small.q85_420_partially_interleaved.jpg", - "Q85YUV420PartiallyInterleaved"}, - {"jxl/flower/flower.png.im_q85_422.jpg", "Q85YUV422"}, - {"jxl/flower/flower.png.im_q85_440.jpg", "Q85YUV440"}, - {"jxl/flower/flower.png.im_q85_444_1x2.jpg", "Q85YUV444_1x2"}, - {"jxl/flower/flower.png.im_q85_asymmetric.jpg", "Q85Asymmetric"}, - {"jxl/flower/flower.png.im_q85_gray.jpg", "Q85Gray"}, - {"jxl/flower/flower.png.im_q85_luma_subsample.jpg", "Q85LumaSubsample"}, - {"jxl/flower/flower.png.im_q85_rgb.jpg", "Q85RGB"}, - {"jxl/flower/flower.png.im_q85_rgb_subsample_blue.jpg", - "Q85RGBSubsampleBlue"}, - {"jxl/flower/flower_small.cmyk.jpg", "CMYK"}, - }); - for (size_t i = 0; i < (buffered ? 4u : testfiles.size()); ++i) { - for (JpegIOMode output_mode : {PIXELS, RAW_DATA}) { - TestConfig config; - config.fn = testfiles[i].first; - config.fn_desc = testfiles[i].second; - config.dparams.output_mode = output_mode; - all_tests.push_back(config); - } - } - } - - // Tests for common chroma subsampling and output modes. - for (JpegIOMode output_mode : {PIXELS, RAW_DATA, COEFFICIENTS}) { - for (int h_samp : {1, 2}) { - for (int v_samp : {1, 2}) { - for (bool fancy : {true, false}) { - if (!fancy && (output_mode != PIXELS || h_samp * v_samp == 1)) { - continue; - } - TestConfig config; - config.dparams.output_mode = output_mode; - config.dparams.do_fancy_upsampling = fancy; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h_samp, 1, 1}; - config.jparams.v_sampling = {v_samp, 1, 1}; - if (output_mode == COEFFICIENTS) { - config.max_rms_dist = 0.0f; - } - all_tests.push_back(config); - } - } - } - } - - // Tests for partial input. - for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f}) { - for (int progr : {0, 1, 3}) { - for (int samp : {1, 2}) { - for (bool skip_scans : {false, true}) { - if (skip_scans && (progr != 1 || size_factor < 0.5f)) continue; - for (JpegIOMode output_mode : {PIXELS, RAW_DATA}) { - TestConfig config; - config.input.xsize = 517; - config.input.ysize = 523; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - config.dparams.size_factor = size_factor; - config.dparams.output_mode = output_mode; - config.dparams.skip_scans = skip_scans; - // The last partially available block can behave differently. - // TODO(szabadka) Figure out if we can make the behaviour more - // similar. - config.max_rms_dist = samp == 1 ? 1.75f : 3.0f; - config.max_diff = 255.0f; - all_tests.push_back(config); - } - } - } - } - } - - // Tests for block smoothing. - for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f, 1.0f}) { - for (int samp : {1, 2}) { - for (bool skip_scans : {false, true}) { - if (skip_scans && size_factor < 0.3f) continue; - TestConfig config; - config.input.xsize = 517; - config.input.ysize = 523; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = 2; - config.dparams.size_factor = size_factor; - config.dparams.do_block_smoothing = true; - config.dparams.skip_scans = skip_scans; - // libjpeg does smoothing for incomplete scans differently at - // the border between current and previous scans. - config.max_rms_dist = 8.0f; - config.max_diff = 255.0f; - all_tests.push_back(config); - } - } - } - - // Test for switching output color quantization modes between scans. - if (buffered) { - TestConfig config; - config.jparams.progressive_mode = 2; - config.dparams.quantize_colors = true; - config.dparams.scan_params = { - {3, JDITHER_NONE, CQUANT_1PASS}, {4, JDITHER_ORDERED, CQUANT_1PASS}, - {5, JDITHER_FS, CQUANT_1PASS}, {6, JDITHER_NONE, CQUANT_EXTERNAL}, - {8, JDITHER_NONE, CQUANT_REUSE}, {9, JDITHER_NONE, CQUANT_EXTERNAL}, - {10, JDITHER_NONE, CQUANT_2PASS}, {11, JDITHER_NONE, CQUANT_REUSE}, - {12, JDITHER_NONE, CQUANT_2PASS}, {13, JDITHER_FS, CQUANT_2PASS}, - }; - config.compare_to_orig = true; - config.max_tolerance_factor = 1.04f; - all_tests.push_back(config); - } - - if (buffered) { - return all_tests; - } - - // Tests for output color quantization. - for (int num_colors : {8, 64, 256}) { - for (ColorQuantMode mode : {CQUANT_1PASS, CQUANT_EXTERNAL, CQUANT_2PASS}) { - if (mode == CQUANT_EXTERNAL && num_colors != 256) continue; - for (J_DITHER_MODE dither : {JDITHER_NONE, JDITHER_ORDERED, JDITHER_FS}) { - if (mode == CQUANT_EXTERNAL && dither != JDITHER_NONE) continue; - if (mode != CQUANT_1PASS && dither == JDITHER_ORDERED) continue; - for (bool crop : {false, true}) { - for (bool scale : {false, true}) { - for (bool samp : {false, true}) { - if ((num_colors != 256) && (crop || scale || samp)) { - continue; - } - if (mode == CQUANT_2PASS && crop) continue; - TestConfig config; - config.input.xsize = 1024; - config.input.ysize = 768; - config.dparams.quantize_colors = true; - config.dparams.desired_number_of_colors = num_colors; - config.dparams.scan_params = {{kLastScan, dither, mode}}; - config.dparams.crop_output = crop; - if (scale) { - config.dparams.scale_num = 7; - config.dparams.scale_denom = 8; - } - if (samp) { - config.jparams.h_sampling = {2, 1, 1}; - config.jparams.v_sampling = {2, 1, 1}; - } - if (!scale && !crop) { - config.compare_to_orig = true; - if (dither != JDITHER_NONE) { - config.max_tolerance_factor = 1.05f; - } - if (mode == CQUANT_2PASS && - (num_colors == 8 || dither == JDITHER_FS)) { - // TODO(szabadka) Lower this bound. - config.max_tolerance_factor = 1.5f; - } - } else { - // We only test for buffer overflows, etc. - config.max_rms_dist = 100.0f; - config.max_diff = 255.0f; - } - all_tests.push_back(config); - } - } - } - } - } - } - - // Tests for output formats. - for (JpegliDataType type : - {JPEGLI_TYPE_UINT8, JPEGLI_TYPE_UINT16, JPEGLI_TYPE_FLOAT}) { - for (JpegliEndianness endianness : - {JPEGLI_NATIVE_ENDIAN, JPEGLI_LITTLE_ENDIAN, JPEGLI_BIG_ENDIAN}) { - if (type == JPEGLI_TYPE_UINT8 && endianness != JPEGLI_NATIVE_ENDIAN) { - continue; - } - for (int channels = 1; channels <= 4; ++channels) { - TestConfig config; - config.dparams.data_type = type; - config.dparams.endianness = endianness; - config.input.color_space = JCS_UNKNOWN; - config.input.components = channels; - config.dparams.set_out_color_space = true; - config.dparams.out_color_space = JCS_UNKNOWN; - all_tests.push_back(config); - } - } - } - // Test for output cropping. - { - TestConfig config; - config.dparams.crop_output = true; - all_tests.push_back(config); - } - // Tests for color transforms. - for (J_COLOR_SPACE out_color_space : - {JCS_RGB, JCS_GRAYSCALE, JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBA, - JCS_EXT_BGRA, JCS_EXT_ARGB, JCS_EXT_ABGR}) { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.input.color_space = JCS_GRAYSCALE; - config.dparams.set_out_color_space = true; - config.dparams.out_color_space = out_color_space; - all_tests.push_back(config); - } - for (J_COLOR_SPACE jpeg_color_space : {JCS_RGB, JCS_YCbCr}) { - for (J_COLOR_SPACE out_color_space : - {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE, JCS_EXT_RGB, JCS_EXT_BGR, - JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ARGB, JCS_EXT_ABGR}) { - if (jpeg_color_space == JCS_RGB && out_color_space == JCS_YCbCr) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - config.dparams.set_out_color_space = true; - config.dparams.out_color_space = out_color_space; - all_tests.push_back(config); - } - } - for (J_COLOR_SPACE jpeg_color_space : {JCS_CMYK, JCS_YCCK}) { - for (J_COLOR_SPACE out_color_space : {JCS_CMYK, JCS_YCCK}) { - if (jpeg_color_space == JCS_CMYK && out_color_space == JCS_YCCK) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.input.color_space = JCS_CMYK; - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - config.dparams.set_out_color_space = true; - config.dparams.out_color_space = out_color_space; - all_tests.push_back(config); - } - } - // Tests for progressive levels. - for (int p = 0; p < 3 + NumTestScanScripts(); ++p) { - TestConfig config; - config.jparams.progressive_mode = p; - all_tests.push_back(config); - } - // Tests for RST markers. - for (size_t r : {1, 17, 1024}) { - for (size_t chunk_size : {1, 65536}) { - for (int progr : {0, 2}) { - TestConfig config; - config.dparams.chunk_size = chunk_size; - config.jparams.progressive_mode = progr; - config.jparams.restart_interval = r; - all_tests.push_back(config); - } - } - } - for (size_t rr : {1, 3, 8, 100}) { - TestConfig config; - config.jparams.restart_in_rows = rr; - all_tests.push_back(config); - } - // Tests for custom quantization tables. - for (int type : {0, 1, 10, 100, 10000}) { - for (int scale : {1, 50, 100, 200, 500}) { - for (bool add_raw : {false, true}) { - for (bool baseline : {true, false}) { - if (!baseline && (add_raw || type * scale < 25500)) continue; - TestConfig config; - config.input.xsize = 64; - config.input.ysize = 64; - CustomQuantTable table; - table.table_type = type; - table.scale_factor = scale; - table.force_baseline = baseline; - table.add_raw = add_raw; - table.Generate(); - config.jparams.quant_tables.push_back(table); - config.jparams.quant_indexes = {0, 0, 0}; - config.compare_to_orig = true; - config.max_tolerance_factor = 1.02; - all_tests.push_back(config); - } - } - } - } - for (int qidx = 0; qidx < 8; ++qidx) { - if (qidx == 3) continue; - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - all_tests.push_back(config); - } - for (int qidx = 0; qidx < 8; ++qidx) { - for (int slot_idx = 0; slot_idx < 2; ++slot_idx) { - if (qidx == 0 && slot_idx == 0) continue; - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - CustomQuantTable table; - table.slot_idx = slot_idx; - table.Generate(); - config.jparams.quant_tables.push_back(table); - all_tests.push_back(config); - } - } - for (int qidx = 0; qidx < 8; ++qidx) { - for (bool xyb : {false, true}) { - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.xyb_mode = xyb; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - { - CustomQuantTable table; - table.slot_idx = 0; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 1; - table.table_type = 20; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - config.compare_to_orig = true; - all_tests.push_back(config); - } - } - for (bool xyb : {false, true}) { - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.xyb_mode = xyb; - config.jparams.quant_indexes = {0, 1, 2}; - { - CustomQuantTable table; - table.slot_idx = 0; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 1; - table.table_type = 20; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 2; - table.table_type = 30; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - config.compare_to_orig = true; - all_tests.push_back(config); - } - // Tests for fixed (and custom) prefix codes. - for (J_COLOR_SPACE jpeg_color_space : {JCS_RGB, JCS_YCbCr}) { - for (bool flat_dc_luma : {false, true}) { - TestConfig config; - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - config.jparams.progressive_mode = 0; - config.jparams.optimize_coding = 0; - config.jparams.use_flat_dc_luma_code = flat_dc_luma; - all_tests.push_back(config); - } - } - for (J_COLOR_SPACE jpeg_color_space : {JCS_CMYK, JCS_YCCK}) { - for (bool flat_dc_luma : {false, true}) { - TestConfig config; - config.input.color_space = JCS_CMYK; - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - config.jparams.progressive_mode = 0; - config.jparams.optimize_coding = 0; - config.jparams.use_flat_dc_luma_code = flat_dc_luma; - all_tests.push_back(config); - } - } - // Test for jpeg without DHT marker. - { - TestConfig config; - config.jparams.progressive_mode = 0; - config.jparams.optimize_coding = 0; - config.jparams.omit_standard_tables = true; - all_tests.push_back(config); - } - // Test for custom component ids. - { - TestConfig config; - config.input.xsize = config.input.ysize = 128; - config.jparams.comp_ids = {7, 17, 177}; - all_tests.push_back(config); - } - // Tests for JFIF/Adobe markers. - for (int override_JFIF : {-1, 0, 1}) { - for (int override_Adobe : {-1, 0, 1}) { - if (override_JFIF == -1 && override_Adobe == -1) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 128; - config.jparams.override_JFIF = override_JFIF; - config.jparams.override_Adobe = override_Adobe; - all_tests.push_back(config); - } - } - // Tests for small images. - for (int xsize : {1, 7, 8, 9, 15, 16, 17}) { - for (int ysize : {1, 7, 8, 9, 15, 16, 17}) { - TestConfig config; - config.input.xsize = xsize; - config.input.ysize = ysize; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - all_tests.push_back(config); - } - } - // Tests for custom marker processor. - for (size_t chunk_size : {0, 1, 64, 65536}) { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.dparams.chunk_size = chunk_size; - config.jparams.add_marker = true; - all_tests.push_back(config); - } - // Tests for icc profile decoding. - for (size_t icc_size : {728, 70000, 1000000}) { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.jparams.icc.resize(icc_size); - for (size_t i = 0; i < icc_size; ++i) { - config.jparams.icc[i] = (i * 17) & 0xff; - } - all_tests.push_back(config); - } - // Tests for unusual sampling factors. - for (int h0_samp : {1, 2, 3, 4}) { - for (int v0_samp : {1, 2, 3, 4}) { - for (int dxb = 0; dxb < h0_samp; ++dxb) { - for (int dyb = 0; dyb < v0_samp; ++dyb) { - for (int dx = 0; dx < 2; ++dx) { - for (int dy = 0; dy < 2; ++dy) { - TestConfig config; - config.input.xsize = 128 + dyb * 8 + dy; - config.input.ysize = 256 + dxb * 8 + dx; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, 1}; - config.jparams.v_sampling = {v0_samp, 1, 1}; - config.compare_to_orig = true; - all_tests.push_back(config); - } - } - } - } - } - } - for (int h0_samp : {1, 2, 4}) { - for (int v0_samp : {1, 2, 4}) { - for (int h2_samp : {1, 2, 4}) { - for (int v2_samp : {1, 2, 4}) { - TestConfig config; - config.input.xsize = 137; - config.input.ysize = 75; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, h2_samp}; - config.jparams.v_sampling = {v0_samp, 1, v2_samp}; - config.compare_to_orig = true; - all_tests.push_back(config); - } - } - } - } - { - TestConfig config; - config.input.xsize = 137; - config.input.ysize = 80; - config.jparams.progressive_mode = 0; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {4, 2, 1}; - config.compare_to_orig = true; - all_tests.push_back(config); - } - for (int h0_samp : {1, 3}) { - for (int v0_samp : {1, 3}) { - for (int h2_samp : {1, 3}) { - for (int v2_samp : {1, 3}) { - TestConfig config; - config.input.xsize = 205; - config.input.ysize = 99; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, h2_samp}; - config.jparams.v_sampling = {v0_samp, 1, v2_samp}; - all_tests.push_back(config); - } - } - } - } - // Tests for output scaling. - for (int scale_num = 1; scale_num <= 16; ++scale_num) { - if (scale_num == 8) continue; - for (bool crop : {false, true}) { - for (int samp : {1, 2}) { - for (int progr : {0, 2}) { - TestConfig config; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - config.dparams.scale_num = scale_num; - config.dparams.scale_denom = 8; - config.dparams.crop_output = crop; - all_tests.push_back(config); - } - } - } - } - return all_tests; -} - -std::string QuantMode(ColorQuantMode mode) { - switch (mode) { - case CQUANT_1PASS: - return "1pass"; - case CQUANT_EXTERNAL: - return "External"; - case CQUANT_2PASS: - return "2pass"; - case CQUANT_REUSE: - return "Reuse"; - } - return ""; -} - -std::string DitherMode(J_DITHER_MODE mode) { - switch (mode) { - case JDITHER_NONE: - return "No"; - case JDITHER_ORDERED: - return "Ordered"; - case JDITHER_FS: - return "FS"; - } - return ""; -} - -std::ostream& operator<<(std::ostream& os, const DecompressParams& dparams) { - if (dparams.chunk_size == 0) { - os << "CompleteInput"; - } else { - os << "InputChunks" << dparams.chunk_size; - } - if (dparams.size_factor < 1.0f) { - os << "Partial" << static_cast(dparams.size_factor * 100) << "p"; - } - if (dparams.max_output_lines == 0) { - os << "CompleteOutput"; - } else { - os << "OutputLines" << dparams.max_output_lines; - } - if (dparams.output_mode == RAW_DATA) { - os << "RawDataOut"; - } else if (dparams.output_mode == COEFFICIENTS) { - os << "CoeffsOut"; - } - os << IOMethodName(dparams.data_type, dparams.endianness); - if (dparams.set_out_color_space) { - os << "OutColor" - << ColorSpaceName(static_cast(dparams.out_color_space)); - } - if (dparams.crop_output) { - os << "Crop"; - } - if (dparams.do_block_smoothing) { - os << "BlockSmoothing"; - } - if (!dparams.do_fancy_upsampling) { - os << "NoFancyUpsampling"; - } - if (dparams.scale_num != 1 || dparams.scale_denom != 1) { - os << "Scale" << dparams.scale_num << "_" << dparams.scale_denom; - } - if (dparams.quantize_colors) { - os << "Quant" << dparams.desired_number_of_colors << "colors"; - for (size_t i = 0; i < dparams.scan_params.size(); ++i) { - if (i > 0) os << "_"; - const auto& sparam = dparams.scan_params[i]; - os << QuantMode(sparam.color_quant_mode); - os << DitherMode(static_cast(sparam.dither_mode)) - << "Dither"; - } - } - if (dparams.skip_scans) { - os << "SkipScans"; - } - return os; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - if (!c.fn.empty()) { - os << c.fn_desc; - } else { - os << c.input; - } - os << c.jparams; - os << c.dparams; - return os; -} - -std::string TestDescription(const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(DecodeAPITest, DecodeAPITestParam, - testing::ValuesIn(GenerateTests(false)), - TestDescription); - -JPEGLI_INSTANTIATE_TEST_SUITE_P(DecodeAPITestBuffered, - DecodeAPITestParamBuffered, - testing::ValuesIn(GenerateTests(true)), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/decode_internal.h b/third_party/jpeg-xl/lib/jpegli/decode_internal.h deleted file mode 100644 index c36f6774b9ca6..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_internal.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_DECODE_INTERNAL_H_ -#define LIB_JPEGLI_DECODE_INTERNAL_H_ - -#include - -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jpegli/common_internal.h" -#include "lib/jpegli/huffman.h" -#include "lib/jpegli/types.h" - -namespace jpegli { - -static constexpr int kNeedMoreInput = 100; -static constexpr int kHandleRestart = 101; -static constexpr int kHandleMarkerProcessor = 102; -static constexpr int kProcessNextMarker = 103; -static constexpr size_t kAllHuffLutSize = NUM_HUFF_TBLS * kJpegHuffmanLutSize; - -typedef int16_t coeff_t; - -// State of the decoder that has to be saved before decoding one MCU in case -// we run out of the bitstream. -struct MCUCodingState { - coeff_t last_dc_coeff[kMaxComponents]; - int eobrun; - coeff_t coeffs[D_MAX_BLOCKS_IN_MCU * DCTSIZE2]; -}; - -} // namespace jpegli - -// Use this forward-declared libjpeg struct to hold all our private variables. -// TODO(szabadka) Remove variables that have a corresponding version in cinfo. -struct jpeg_decomp_master { - // - // Input handling state. - // - std::vector input_buffer_; - size_t input_buffer_pos_; - // Number of bits after codestream_pos_ that were already processed. - size_t codestream_bits_ahead_; - - // Coefficient buffers - jvirt_barray_ptr* coef_arrays; - JBLOCKARRAY coeff_rows[jpegli::kMaxComponents]; - - bool streaming_mode_; - - // - // Marker data processing state. - // - bool found_soi_; - bool found_dri_; - bool found_sof_; - bool found_sos_; - bool found_eoi_; - - // Whether this jpeg has multiple scans (progressive or non-interleaved - // sequential). - bool is_multiscan_; - - size_t icc_index_; - size_t icc_total_; - std::vector icc_profile_; - jpegli::HuffmanTableEntry dc_huff_lut_[jpegli::kAllHuffLutSize]; - jpegli::HuffmanTableEntry ac_huff_lut_[jpegli::kAllHuffLutSize]; - uint8_t markers_to_save_[32]; - jpeg_marker_parser_method app_marker_parsers[16]; - jpeg_marker_parser_method com_marker_parser; - - // Fields defined by SOF marker. - size_t iMCU_cols_; - int h_factor[jpegli::kMaxComponents]; - int v_factor[jpegli::kMaxComponents]; - - // Initialized at start of frame. - uint16_t scan_progression_[jpegli::kMaxComponents][DCTSIZE2]; - - // - // Per scan state. - // - size_t scan_mcu_row_; - size_t scan_mcu_col_; - size_t mcu_rows_per_iMCU_row_; - jpegli::coeff_t last_dc_coeff_[jpegli::kMaxComponents]; - int eobrun_; - int restarts_to_go_; - int next_restart_marker_; - - jpegli::MCUCodingState mcu_; - - // - // Rendering state. - // - int output_passes_done_; - JpegliDataType output_data_type_ = JPEGLI_TYPE_UINT8; - size_t xoffset_; - bool swap_endianness_ = false; - bool need_context_rows_; - bool regenerate_inverse_colormap_; - bool apply_smoothing; - - int min_scaled_dct_size; - int scaled_dct_size[jpegli::kMaxComponents]; - - size_t raw_height_[jpegli::kMaxComponents]; - jpegli::RowBuffer raw_output_[jpegli::kMaxComponents]; - jpegli::RowBuffer render_output_[jpegli::kMaxComponents]; - - void (*inverse_transform[jpegli::kMaxComponents])( - const int16_t* JXL_RESTRICT qblock, const float* JXL_RESTRICT dequant, - const float* JXL_RESTRICT biases, float* JXL_RESTRICT scratch_space, - float* JXL_RESTRICT output, size_t output_stride, size_t dctsize); - - void (*color_transform)(float* row[jpegli::kMaxComponents], size_t len); - - float* idct_scratch_; - float* upsample_scratch_; - uint8_t* output_scratch_; - int16_t* smoothing_scratch_; - float* dequant_; - // 1 = 1pass, 2 = 2pass, 3 = external - int quant_mode_; - int quant_pass_; - int num_colors_[jpegli::kMaxComponents]; - uint8_t* colormap_lut_; - uint8_t* pixels_; - JSAMPARRAY scanlines_; - std::vector> candidate_lists_; - float* dither_[jpegli::kMaxComponents]; - float* error_row_[2 * jpegli::kMaxComponents]; - size_t dither_size_; - size_t dither_mask_; - - // Per channel and per frequency statistics about the number of nonzeros and - // the sum of coefficient absolute values, used in dequantization bias - // computation. - int* nonzeros_; - int* sumabs_; - size_t num_processed_blocks_[jpegli::kMaxComponents]; - float* biases_; -#define SAVED_COEFS 10 - // This holds the coef_bits of the scan before the current scan, - // i.e. the bottom half when rendering incomplete scans. - int (*coef_bits_latch)[SAVED_COEFS]; - int (*prev_coef_bits_latch)[SAVED_COEFS]; -}; - -#endif // LIB_JPEGLI_DECODE_INTERNAL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc b/third_party/jpeg-xl/lib/jpegli/decode_marker.cc deleted file mode 100644 index 2621ed08670cc..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/decode_marker.h" - -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/huffman.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jxl/base/printf_macros.h" - -namespace jpegli { -namespace { - -constexpr int kMaxDimPixels = 65535; -constexpr uint8_t kIccProfileTag[12] = "ICC_PROFILE"; - -// Macros for commonly used error conditions. - -#define JPEG_VERIFY_LEN(n) \ - if (pos + (n) > len) { \ - JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \ - " need=%d len=%" PRIuS, \ - pos, static_cast(n), len); \ - } - -#define JPEG_VERIFY_INPUT(var, low, high) \ - if ((var) < (low) || (var) > (high)) { \ - JPEGLI_ERROR("Invalid " #var ": %d", static_cast(var)); \ - } - -#define JPEG_VERIFY_MARKER_END() \ - if (pos != len) { \ - JPEGLI_ERROR("Invalid marker length: declared=%" PRIuS " actual=%" PRIuS, \ - len, pos); \ - } - -inline int ReadUint8(const uint8_t* data, size_t* pos) { - return data[(*pos)++]; -} - -inline int ReadUint16(const uint8_t* data, size_t* pos) { - int v = (data[*pos] << 8) + data[*pos + 1]; - *pos += 2; - return v; -} - -void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - if (!m->found_soi_) { - JPEGLI_ERROR("Unexpected SOF marker."); - } - if (m->found_sof_) { - JPEGLI_ERROR("Duplicate SOF marker."); - } - m->found_sof_ = true; - cinfo->progressive_mode = TO_JXL_BOOL(cinfo->unread_marker == 0xc2); - cinfo->arith_code = 0; - size_t pos = 2; - JPEG_VERIFY_LEN(6); - cinfo->data_precision = ReadUint8(data, &pos); - cinfo->image_height = ReadUint16(data, &pos); - cinfo->image_width = ReadUint16(data, &pos); - cinfo->num_components = ReadUint8(data, &pos); - JPEG_VERIFY_INPUT(cinfo->data_precision, kJpegPrecision, kJpegPrecision); - JPEG_VERIFY_INPUT(cinfo->image_height, 1, kMaxDimPixels); - JPEG_VERIFY_INPUT(cinfo->image_width, 1, kMaxDimPixels); - JPEG_VERIFY_INPUT(cinfo->num_components, 1, kMaxComponents); - JPEG_VERIFY_LEN(3 * cinfo->num_components); - cinfo->comp_info = jpegli::Allocate( - cinfo, cinfo->num_components, JPOOL_IMAGE); - - // Read sampling factors and quant table index for each component. - uint8_t ids_seen[256] = {0}; - cinfo->max_h_samp_factor = 1; - cinfo->max_v_samp_factor = 1; - for (int i = 0; i < cinfo->num_components; ++i) { - jpeg_component_info* comp = &cinfo->comp_info[i]; - comp->component_index = i; - const int id = ReadUint8(data, &pos); - if (ids_seen[id]) { // (cf. section B.2.2, syntax of Ci) - JPEGLI_ERROR("Duplicate ID %d in SOF.", id); - } - ids_seen[id] = 1; - comp->component_id = id; - int factor = ReadUint8(data, &pos); - int h_samp_factor = factor >> 4; - int v_samp_factor = factor & 0xf; - JPEG_VERIFY_INPUT(h_samp_factor, 1, MAX_SAMP_FACTOR); - JPEG_VERIFY_INPUT(v_samp_factor, 1, MAX_SAMP_FACTOR); - comp->h_samp_factor = h_samp_factor; - comp->v_samp_factor = v_samp_factor; - cinfo->max_h_samp_factor = - std::max(cinfo->max_h_samp_factor, h_samp_factor); - cinfo->max_v_samp_factor = - std::max(cinfo->max_v_samp_factor, v_samp_factor); - int quant_tbl_idx = ReadUint8(data, &pos); - JPEG_VERIFY_INPUT(quant_tbl_idx, 0, NUM_QUANT_TBLS - 1); - comp->quant_tbl_no = quant_tbl_idx; - comp->quant_table = nullptr; // will be allocated after SOS marker - } - JPEG_VERIFY_MARKER_END(); - - // Set the input colorspace based on the markers we have seen and set - // default output colorspace. - if (cinfo->num_components == 1) { - cinfo->jpeg_color_space = JCS_GRAYSCALE; - cinfo->out_color_space = JCS_GRAYSCALE; - } else if (cinfo->num_components == 3) { - if (cinfo->saw_JFIF_marker) { - cinfo->jpeg_color_space = JCS_YCbCr; - } else if (cinfo->saw_Adobe_marker) { - cinfo->jpeg_color_space = - cinfo->Adobe_transform == 0 ? JCS_RGB : JCS_YCbCr; - } else { - cinfo->jpeg_color_space = JCS_YCbCr; - if (cinfo->comp_info[0].component_id == 'R' && // - cinfo->comp_info[1].component_id == 'G' && // - cinfo->comp_info[2].component_id == 'B') { - cinfo->jpeg_color_space = JCS_RGB; - } - } - cinfo->out_color_space = JCS_RGB; - } else if (cinfo->num_components == 4) { - if (cinfo->saw_Adobe_marker) { - cinfo->jpeg_color_space = - cinfo->Adobe_transform == 0 ? JCS_CMYK : JCS_YCCK; - } else { - cinfo->jpeg_color_space = JCS_CMYK; - } - cinfo->out_color_space = JCS_CMYK; - } - - // We have checked above that none of the sampling factors are 0, so the max - // sampling factors can not be 0. - cinfo->total_iMCU_rows = - DivCeil(cinfo->image_height, cinfo->max_v_samp_factor * DCTSIZE); - m->iMCU_cols_ = - DivCeil(cinfo->image_width, cinfo->max_h_samp_factor * DCTSIZE); - // Compute the block dimensions for each component. - for (int i = 0; i < cinfo->num_components; ++i) { - jpeg_component_info* comp = &cinfo->comp_info[i]; - if (cinfo->max_h_samp_factor % comp->h_samp_factor != 0 || - cinfo->max_v_samp_factor % comp->v_samp_factor != 0) { - JPEGLI_ERROR("Non-integral subsampling ratios."); - } - m->h_factor[i] = cinfo->max_h_samp_factor / comp->h_samp_factor; - m->v_factor[i] = cinfo->max_v_samp_factor / comp->v_samp_factor; - comp->downsampled_width = DivCeil(cinfo->image_width, m->h_factor[i]); - comp->downsampled_height = DivCeil(cinfo->image_height, m->v_factor[i]); - comp->width_in_blocks = DivCeil(comp->downsampled_width, DCTSIZE); - comp->height_in_blocks = DivCeil(comp->downsampled_height, DCTSIZE); - } - memset(m->scan_progression_, 0, sizeof(m->scan_progression_)); -} - -void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - if (!m->found_sof_) { - JPEGLI_ERROR("Unexpected SOS marker."); - } - m->found_sos_ = true; - size_t pos = 2; - JPEG_VERIFY_LEN(1); - cinfo->comps_in_scan = ReadUint8(data, &pos); - JPEG_VERIFY_INPUT(cinfo->comps_in_scan, 1, cinfo->num_components); - JPEG_VERIFY_INPUT(cinfo->comps_in_scan, 1, MAX_COMPS_IN_SCAN); - - JPEG_VERIFY_LEN(2 * cinfo->comps_in_scan); - bool is_interleaved = (cinfo->comps_in_scan > 1); - uint8_t ids_seen[256] = {0}; - cinfo->blocks_in_MCU = 0; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - int id = ReadUint8(data, &pos); - if (ids_seen[id]) { // (cf. section B.2.3, regarding CSj) - JPEGLI_ERROR("Duplicate ID %d in SOS.", id); - } - ids_seen[id] = 1; - jpeg_component_info* comp = nullptr; - for (int j = 0; j < cinfo->num_components; ++j) { - if (cinfo->comp_info[j].component_id == id) { - comp = &cinfo->comp_info[j]; - cinfo->cur_comp_info[i] = comp; - } - } - if (!comp) { - JPEGLI_ERROR("SOS marker: Could not find component with id %d", id); - } - int c = ReadUint8(data, &pos); - comp->dc_tbl_no = c >> 4; - comp->ac_tbl_no = c & 0xf; - JPEG_VERIFY_INPUT(comp->dc_tbl_no, 0, 3); - JPEG_VERIFY_INPUT(comp->ac_tbl_no, 0, 3); - comp->MCU_width = is_interleaved ? comp->h_samp_factor : 1; - comp->MCU_height = is_interleaved ? comp->v_samp_factor : 1; - comp->MCU_blocks = comp->MCU_width * comp->MCU_height; - if (cinfo->blocks_in_MCU + comp->MCU_blocks > D_MAX_BLOCKS_IN_MCU) { - JPEGLI_ERROR("Too many blocks in MCU."); - } - for (int j = 0; j < comp->MCU_blocks; ++j) { - cinfo->MCU_membership[cinfo->blocks_in_MCU++] = i; - } - } - JPEG_VERIFY_LEN(3); - cinfo->Ss = ReadUint8(data, &pos); - cinfo->Se = ReadUint8(data, &pos); - JPEG_VERIFY_INPUT(cinfo->Ss, 0, 63); - JPEG_VERIFY_INPUT(cinfo->Se, cinfo->Ss, 63); - int c = ReadUint8(data, &pos); - cinfo->Ah = c >> 4; - cinfo->Al = c & 0xf; - JPEG_VERIFY_MARKER_END(); - - if (cinfo->input_scan_number == 0) { - m->is_multiscan_ = (cinfo->comps_in_scan < cinfo->num_components || - FROM_JXL_BOOL(cinfo->progressive_mode)); - } - if (cinfo->Ah != 0 && cinfo->Al != cinfo->Ah - 1) { - // section G.1.1.1.2 : Successive approximation control only improves - // by one bit at a time. - JPEGLI_ERROR("Invalid progressive parameters: Al=%d Ah=%d", cinfo->Al, - cinfo->Ah); - } - if (!cinfo->progressive_mode) { - cinfo->Ss = 0; - cinfo->Se = 63; - cinfo->Ah = 0; - cinfo->Al = 0; - } - const uint16_t scan_bitmask = - cinfo->Ah == 0 ? (0xffff << cinfo->Al) : (1u << cinfo->Al); - const uint16_t refinement_bitmask = (1 << cinfo->Al) - 1; - if (!cinfo->coef_bits) { - cinfo->coef_bits = - Allocate(cinfo, cinfo->num_components * 2, JPOOL_IMAGE); - m->coef_bits_latch = - Allocate(cinfo, cinfo->num_components, JPOOL_IMAGE); - m->prev_coef_bits_latch = - Allocate(cinfo, cinfo->num_components, JPOOL_IMAGE); - - for (int c = 0; c < cinfo->num_components; ++c) { - for (int i = 0; i < DCTSIZE2; ++i) { - cinfo->coef_bits[c][i] = -1; - if (i < SAVED_COEFS) { - m->coef_bits_latch[c][i] = -1; - } - } - } - } - - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - int comp_idx = cinfo->cur_comp_info[i]->component_index; - for (int k = cinfo->Ss; k <= cinfo->Se; ++k) { - if (m->scan_progression_[comp_idx][k] & scan_bitmask) { - JPEGLI_ERROR( - "Overlapping scans: component=%d k=%d prev_mask: %u cur_mask %u", - comp_idx, k, m->scan_progression_[i][k], scan_bitmask); - } - if (m->scan_progression_[comp_idx][k] & refinement_bitmask) { - JPEGLI_ERROR( - "Invalid scan order, a more refined scan was already done: " - "component=%d k=%d prev_mask=%u cur_mask=%u", - comp_idx, k, m->scan_progression_[i][k], scan_bitmask); - } - m->scan_progression_[comp_idx][k] |= scan_bitmask; - } - } - if (cinfo->Al > 10) { - JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al); - } -} - -// Reads the Define Huffman Table (DHT) marker segment and builds the Huffman -// decoding table in either dc_huff_lut_ or ac_huff_lut_, depending on the type -// and solt_id of Huffman code being read. -void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - size_t pos = 2; - if (pos == len) { - JPEGLI_ERROR("DHT marker: no Huffman table found"); - } - while (pos < len) { - JPEG_VERIFY_LEN(1 + kJpegHuffmanMaxBitLength); - // The index of the Huffman code in the current set of Huffman codes. For AC - // component Huffman codes, 0x10 is added to the index. - int slot_id = ReadUint8(data, &pos); - int huffman_index = slot_id; - bool is_ac_table = ((slot_id & 0x10) != 0); - JHUFF_TBL** table; - if (is_ac_table) { - huffman_index -= 0x10; - JPEG_VERIFY_INPUT(huffman_index, 0, NUM_HUFF_TBLS - 1); - table = &cinfo->ac_huff_tbl_ptrs[huffman_index]; - } else { - JPEG_VERIFY_INPUT(huffman_index, 0, NUM_HUFF_TBLS - 1); - table = &cinfo->dc_huff_tbl_ptrs[huffman_index]; - } - if (*table == nullptr) { - *table = jpegli_alloc_huff_table(reinterpret_cast(cinfo)); - } - int total_count = 0; - for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) { - int count = ReadUint8(data, &pos); - (*table)->bits[i] = count; - total_count += count; - } - if (is_ac_table) { - JPEG_VERIFY_INPUT(total_count, 0, kJpegHuffmanAlphabetSize); - } else { - // Allow symbols up to 15 here, we check later whether any invalid symbols - // are actually decoded. - // TODO(szabadka) Make sure decoder works (does not crash) with up to - // 15-nbits DC symbols and then increase kJpegDCAlphabetSize. - JPEG_VERIFY_INPUT(total_count, 0, 16); - } - JPEG_VERIFY_LEN(total_count); - for (int i = 0; i < total_count; ++i) { - int value = ReadUint8(data, &pos); - if (!is_ac_table) { - JPEG_VERIFY_INPUT(value, 0, 15); - } - (*table)->huffval[i] = value; - } - for (int i = total_count; i < kJpegHuffmanAlphabetSize; ++i) { - (*table)->huffval[i] = 0; - } - } - JPEG_VERIFY_MARKER_END(); -} - -void ProcessDQT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - if (m->found_sos_) { - JPEGLI_ERROR("Updating quant tables between scans is not supported."); - } - size_t pos = 2; - if (pos == len) { - JPEGLI_ERROR("DQT marker: no quantization table found"); - } - while (pos < len) { - JPEG_VERIFY_LEN(1); - int quant_table_index = ReadUint8(data, &pos); - int precision = quant_table_index >> 4; - JPEG_VERIFY_INPUT(precision, 0, 1); - quant_table_index &= 0xf; - JPEG_VERIFY_INPUT(quant_table_index, 0, NUM_QUANT_TBLS - 1); - JPEG_VERIFY_LEN((precision + 1) * DCTSIZE2); - - if (cinfo->quant_tbl_ptrs[quant_table_index] == nullptr) { - cinfo->quant_tbl_ptrs[quant_table_index] = - jpegli_alloc_quant_table(reinterpret_cast(cinfo)); - } - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_table_index]; - - for (size_t i = 0; i < DCTSIZE2; ++i) { - int quant_val = - precision ? ReadUint16(data, &pos) : ReadUint8(data, &pos); - JPEG_VERIFY_INPUT(quant_val, 1, 65535); - quant_table->quantval[kJPEGNaturalOrder[i]] = quant_val; - } - } - JPEG_VERIFY_MARKER_END(); -} - -void ProcessDNL(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - // Ignore marker. -} - -void ProcessDRI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - if (m->found_dri_) { - JPEGLI_ERROR("Duplicate DRI marker."); - } - m->found_dri_ = true; - size_t pos = 2; - JPEG_VERIFY_LEN(2); - cinfo->restart_interval = ReadUint16(data, &pos); - JPEG_VERIFY_MARKER_END(); -} - -void ProcessAPP(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - const uint8_t marker = cinfo->unread_marker; - const uint8_t* payload = data + 2; - size_t payload_size = len - 2; - if (marker == 0xE0) { - if (payload_size >= 14 && memcmp(payload, "JFIF", 4) == 0) { - cinfo->saw_JFIF_marker = TRUE; - cinfo->JFIF_major_version = payload[5]; - cinfo->JFIF_minor_version = payload[6]; - cinfo->density_unit = payload[7]; - cinfo->X_density = (payload[8] << 8) + payload[9]; - cinfo->Y_density = (payload[10] << 8) + payload[11]; - } - } else if (marker == 0xEE) { - if (payload_size >= 12 && memcmp(payload, "Adobe", 5) == 0) { - cinfo->saw_Adobe_marker = TRUE; - cinfo->Adobe_transform = payload[11]; - } - } else if (marker == 0xE2) { - if (payload_size >= sizeof(kIccProfileTag) && - memcmp(payload, kIccProfileTag, sizeof(kIccProfileTag)) == 0) { - payload += sizeof(kIccProfileTag); - payload_size -= sizeof(kIccProfileTag); - if (payload_size < 2) { - JPEGLI_ERROR("ICC chunk is too small."); - } - uint8_t index = payload[0]; - uint8_t total = payload[1]; - ++m->icc_index_; - if (m->icc_index_ != index) { - JPEGLI_ERROR("Invalid ICC chunk order."); - } - if (total == 0) { - JPEGLI_ERROR("Invalid ICC chunk total."); - } - if (m->icc_total_ == 0) { - m->icc_total_ = total; - } else if (m->icc_total_ != total) { - JPEGLI_ERROR("Invalid ICC chunk total."); - } - if (m->icc_index_ > m->icc_total_) { - JPEGLI_ERROR("Invalid ICC chunk index."); - } - m->icc_profile_.insert(m->icc_profile_.end(), payload + 2, - payload + payload_size); - } - } -} - -void ProcessCOM(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - // Ignore marker. -} - -void ProcessSOI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - jpeg_decomp_master* m = cinfo->master; - if (m->found_soi_) { - JPEGLI_ERROR("Duplicate SOI marker"); - } - m->found_soi_ = true; -} - -void ProcessEOI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - cinfo->master->found_eoi_ = true; -} - -void SaveMarker(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { - const uint8_t marker = cinfo->unread_marker; - const uint8_t* payload = data + 2; - size_t payload_size = len - 2; - - // Insert new saved marker to the head of the list. - jpeg_saved_marker_ptr next = cinfo->marker_list; - cinfo->marker_list = - jpegli::Allocate(cinfo, 1, JPOOL_IMAGE); - cinfo->marker_list->next = next; - cinfo->marker_list->marker = marker; - cinfo->marker_list->original_length = payload_size; - cinfo->marker_list->data_length = payload_size; - cinfo->marker_list->data = - jpegli::Allocate(cinfo, payload_size, JPOOL_IMAGE); - memcpy(cinfo->marker_list->data, payload, payload_size); -} - -uint8_t ProcessNextMarker(j_decompress_ptr cinfo, const uint8_t* const data, - const size_t len, size_t* pos) { - jpeg_decomp_master* m = cinfo->master; - size_t num_skipped = 0; - uint8_t marker = cinfo->unread_marker; - if (marker == 0) { - // kIsValidMarker[i] == 1 means (0xc0 + i) is a valid marker. - static const uint8_t kIsValidMarker[] = { - 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - }; - // Skip bytes between markers. - while (*pos + 1 < len && (data[*pos] != 0xff || data[*pos + 1] < 0xc0 || - !kIsValidMarker[data[*pos + 1] - 0xc0])) { - ++(*pos); - ++num_skipped; - } - if (*pos + 2 > len) { - return kNeedMoreInput; - } - marker = data[*pos + 1]; - if (num_skipped > 0) { - if (m->found_soi_) { - JPEGLI_WARN("Skipped %d bytes before marker 0x%02x", - static_cast(num_skipped), marker); - } else { - JPEGLI_ERROR("Did not find SOI marker."); - } - } - *pos += 2; - cinfo->unread_marker = marker; - } - if (!m->found_soi_ && marker != 0xd8) { - JPEGLI_ERROR("Did not find SOI marker."); - } - if (GetMarkerProcessor(cinfo)) { - return kHandleMarkerProcessor; - } - const uint8_t* marker_data = &data[*pos]; - size_t marker_len = 0; - if (marker != 0xd8 && marker != 0xd9) { - if (*pos + 2 > len) { - return kNeedMoreInput; - } - marker_len += (data[*pos] << 8) + data[*pos + 1]; - if (marker_len < 2) { - JPEGLI_ERROR("Invalid marker length"); - } - if (*pos + marker_len > len) { - // TODO(szabadka) Limit our memory usage by using the skip_input_data - // source manager callback on APP markers that are not saved. - return kNeedMoreInput; - } - if (marker >= 0xe0 && m->markers_to_save_[marker - 0xe0]) { - SaveMarker(cinfo, marker_data, marker_len); - } - } - if (marker == 0xc0 || marker == 0xc1 || marker == 0xc2) { - ProcessSOF(cinfo, marker_data, marker_len); - } else if (marker == 0xc4) { - ProcessDHT(cinfo, marker_data, marker_len); - } else if (marker == 0xda) { - ProcessSOS(cinfo, marker_data, marker_len); - } else if (marker == 0xdb) { - ProcessDQT(cinfo, marker_data, marker_len); - } else if (marker == 0xdc) { - ProcessDNL(cinfo, marker_data, marker_len); - } else if (marker == 0xdd) { - ProcessDRI(cinfo, marker_data, marker_len); - } else if (marker >= 0xe0 && marker <= 0xef) { - ProcessAPP(cinfo, marker_data, marker_len); - } else if (marker == 0xfe) { - ProcessCOM(cinfo, marker_data, marker_len); - } else if (marker == 0xd8) { - ProcessSOI(cinfo, marker_data, marker_len); - } else if (marker == 0xd9) { - ProcessEOI(cinfo, marker_data, marker_len); - } else { - JPEGLI_ERROR("Unexpected marker 0x%x", marker); - } - *pos += marker_len; - cinfo->unread_marker = 0; - if (marker == 0xda) { - return JPEG_REACHED_SOS; - } else if (marker == 0xd9) { - return JPEG_REACHED_EOI; - } - return kProcessNextMarker; -} - -} // namespace - -jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - uint8_t marker = cinfo->unread_marker; - jpeg_marker_parser_method callback = nullptr; - if (marker >= 0xe0 && marker <= 0xef) { - callback = m->app_marker_parsers[marker - 0xe0]; - } else if (marker == 0xfe) { - callback = m->com_marker_parser; - } - return callback; -} - -int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* const data, - const size_t len, size_t* pos) { - for (;;) { - int status = ProcessNextMarker(cinfo, data, len, pos); - if (status != kProcessNextMarker) { - return status; - } - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.h b/third_party/jpeg-xl/lib/jpegli/decode_marker.h deleted file mode 100644 index f3d47f6ad27bc..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_marker.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_DECODE_MARKER_H_ -#define LIB_JPEGLI_DECODE_MARKER_H_ - -#include - -#include "lib/jpegli/common.h" - -namespace jpegli { - -// Reads the available input in the source manager's input buffer until either -// the end of the next SOS marker or the end of the input. -// The corresponding fields of cinfo are updated with the processed input data. -// Upon return, the input buffer will be at the start or at the end of a marker -// data segment (inter-marker data is allowed). -// Return value is one of: -// * JPEG_SUSPENDED, if the current input buffer ends before the next SOS or -// EOI marker. Input buffer refill is handled by the caller; -// * JPEG_REACHED_SOS, if the next SOS marker is found; -// * JPEG_REACHED_EOR, if the end of the input is found. -int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* data, size_t len, - size_t* pos); - -jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_DECODE_MARKER_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc b/third_party/jpeg-xl/lib/jpegli/decode_scan.cc deleted file mode 100644 index 6c4e5ac5f77e5..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/decode_scan.h" - -#include - -#include // HWY_ALIGN_MAX - -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { -namespace { - -// Max 14 block per MCU (when 1 channel is subsampled) -// Max 64 nonzero coefficients per block -// Max 16 symbol bits plus 11 extra bits per nonzero symbol -// Max 2 bytes per 8 bits (worst case is all bytes are escaped 0xff) -constexpr int kMaxMCUByteSize = 6048; - -// Helper structure to read bits from the entropy coded data segment. -struct BitReaderState { - BitReaderState(const uint8_t* data, const size_t len, size_t pos) - : data_(data), len_(len), start_pos_(pos) { - Reset(pos); - } - - void Reset(size_t pos) { - pos_ = pos; - val_ = 0; - bits_left_ = 0; - next_marker_pos_ = len_; - FillBitWindow(); - } - - // Returns the next byte and skips the 0xff/0x00 escape sequences. - uint8_t GetNextByte() { - if (pos_ >= next_marker_pos_) { - ++pos_; - return 0; - } - uint8_t c = data_[pos_++]; - if (c == 0xff) { - uint8_t escape = pos_ < len_ ? data_[pos_] : 0; - if (escape == 0) { - ++pos_; - } else { - // 0xff was followed by a non-zero byte, which means that we found the - // start of the next marker segment. - next_marker_pos_ = pos_ - 1; - } - } - return c; - } - - void FillBitWindow() { - if (bits_left_ <= 16) { - while (bits_left_ <= 56) { - val_ <<= 8; - val_ |= static_cast(GetNextByte()); - bits_left_ += 8; - } - } - } - - int ReadBits(int nbits) { - FillBitWindow(); - uint64_t val = (val_ >> (bits_left_ - nbits)) & ((1ULL << nbits) - 1); - bits_left_ -= nbits; - return val; - } - - // Sets *pos to the next stream position, and *bit_pos to the bit position - // within the next byte where parsing should continue. - // Returns false if the stream ended too early. - bool FinishStream(size_t* pos, size_t* bit_pos) { - *bit_pos = (8 - (bits_left_ & 7)) & 7; - // Give back some bytes that we did not use. - int unused_bytes_left = DivCeil(bits_left_, 8); - while (unused_bytes_left-- > 0) { - --pos_; - // If we give back a 0 byte, we need to check if it was a 0xff/0x00 escape - // sequence, and if yes, we need to give back one more byte. - if (((pos_ == len_ && pos_ == next_marker_pos_) || - (pos_ > 0 && pos_ < next_marker_pos_ && data_[pos_] == 0)) && - (data_[pos_ - 1] == 0xff)) { - --pos_; - } - } - if (pos_ >= next_marker_pos_) { - *pos = next_marker_pos_; - if (pos_ > next_marker_pos_ || *bit_pos > 0) { - // Data ran out before the scan was complete. - return false; - } - } - *pos = pos_; - return true; - } - - const uint8_t* data_; - const size_t len_; - size_t pos_; - uint64_t val_; - int bits_left_; - size_t next_marker_pos_; - size_t start_pos_; -}; - -// Returns the next Huffman-coded symbol. -int ReadSymbol(const HuffmanTableEntry* table, BitReaderState* br) { - int nbits; - br->FillBitWindow(); - int val = (br->val_ >> (br->bits_left_ - 8)) & 0xff; - table += val; - nbits = table->bits - 8; - if (nbits > 0) { - br->bits_left_ -= 8; - table += table->value; - val = (br->val_ >> (br->bits_left_ - nbits)) & ((1 << nbits) - 1); - table += val; - } - br->bits_left_ -= table->bits; - return table->value; -} - -/** - * Returns the DC diff or AC value for extra bits value x and prefix code s. - * - * CCITT Rec. T.81 (1992 E) - * Table F.1 – Difference magnitude categories for DC coding - * SSSS | DIFF values - * ------+-------------------------- - * 0 | 0 - * 1 | –1, 1 - * 2 | –3, –2, 2, 3 - * 3 | –7..–4, 4..7 - * ......|.......................... - * 11 | –2047..–1024, 1024..2047 - * - * CCITT Rec. T.81 (1992 E) - * Table F.2 – Categories assigned to coefficient values - * [ Same as Table F.1, but does not include SSSS equal to 0 and 11] - * - * - * CCITT Rec. T.81 (1992 E) - * F.1.2.1.1 Structure of DC code table - * For each category,... additional bits... appended... to uniquely identify - * which difference... occurred... When DIFF is positive... SSSS... bits of DIFF - * are appended. When DIFF is negative... SSSS... bits of (DIFF – 1) are - * appended... Most significant bit... is 0 for negative differences and 1 for - * positive differences. - * - * In other words the upper half of extra bits range represents DIFF as is. - * The lower half represents the negative DIFFs with an offset. - */ -int HuffExtend(int x, int s) { - JXL_DASSERT(s > 0); - int half = 1 << (s - 1); - if (x >= half) { - JXL_DASSERT(x < (1 << s)); - return x; - } else { - return x - (1 << s) + 1; - } -} - -// Decodes one 8x8 block of DCT coefficients from the bit stream. -bool DecodeDCTBlock(const HuffmanTableEntry* dc_huff, - const HuffmanTableEntry* ac_huff, int Ss, int Se, int Al, - int* eobrun, BitReaderState* br, coeff_t* last_dc_coeff, - coeff_t* coeffs) { - // Nowadays multiplication is even faster than variable shift. - int Am = 1 << Al; - bool eobrun_allowed = Ss > 0; - if (Ss == 0) { - int s = ReadSymbol(dc_huff, br); - if (s >= kJpegDCAlphabetSize) { - return false; - } - int diff = 0; - if (s > 0) { - int bits = br->ReadBits(s); - diff = HuffExtend(bits, s); - } - int coeff = diff + *last_dc_coeff; - const int dc_coeff = coeff * Am; - coeffs[0] = dc_coeff; - // TODO(eustas): is there a more elegant / explicit way to check this? - if (dc_coeff != coeffs[0]) { - return false; - } - *last_dc_coeff = coeff; - ++Ss; - } - if (Ss > Se) { - return true; - } - if (*eobrun > 0) { - --(*eobrun); - return true; - } - for (int k = Ss; k <= Se; k++) { - int sr = ReadSymbol(ac_huff, br); - if (sr >= kJpegHuffmanAlphabetSize) { - return false; - } - int r = sr >> 4; - int s = sr & 15; - if (s > 0) { - k += r; - if (k > Se) { - return false; - } - if (s + Al >= kJpegDCAlphabetSize) { - return false; - } - int bits = br->ReadBits(s); - int coeff = HuffExtend(bits, s); - coeffs[kJPEGNaturalOrder[k]] = coeff * Am; - } else if (r == 15) { - k += 15; - } else { - *eobrun = 1 << r; - if (r > 0) { - if (!eobrun_allowed) { - return false; - } - *eobrun += br->ReadBits(r); - } - break; - } - } - --(*eobrun); - return true; -} - -bool RefineDCTBlock(const HuffmanTableEntry* ac_huff, int Ss, int Se, int Al, - int* eobrun, BitReaderState* br, coeff_t* coeffs) { - // Nowadays multiplication is even faster than variable shift. - int Am = 1 << Al; - bool eobrun_allowed = Ss > 0; - if (Ss == 0) { - int s = br->ReadBits(1); - coeff_t dc_coeff = coeffs[0]; - dc_coeff |= s * Am; - coeffs[0] = dc_coeff; - ++Ss; - } - if (Ss > Se) { - return true; - } - int p1 = Am; - int m1 = -Am; - int k = Ss; - int r; - int s; - bool in_zero_run = false; - if (*eobrun <= 0) { - for (; k <= Se; k++) { - s = ReadSymbol(ac_huff, br); - if (s >= kJpegHuffmanAlphabetSize) { - return false; - } - r = s >> 4; - s &= 15; - if (s) { - if (s != 1) { - return false; - } - s = br->ReadBits(1) ? p1 : m1; - in_zero_run = false; - } else { - if (r != 15) { - *eobrun = 1 << r; - if (r > 0) { - if (!eobrun_allowed) { - return false; - } - *eobrun += br->ReadBits(r); - } - break; - } - in_zero_run = true; - } - do { - coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]]; - if (thiscoef != 0) { - if (br->ReadBits(1)) { - if ((thiscoef & p1) == 0) { - if (thiscoef >= 0) { - thiscoef += p1; - } else { - thiscoef += m1; - } - } - } - coeffs[kJPEGNaturalOrder[k]] = thiscoef; - } else { - if (--r < 0) { - break; - } - } - k++; - } while (k <= Se); - if (s) { - if (k > Se) { - return false; - } - coeffs[kJPEGNaturalOrder[k]] = s; - } - } - } - if (in_zero_run) { - return false; - } - if (*eobrun > 0) { - for (; k <= Se; k++) { - coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]]; - if (thiscoef != 0) { - if (br->ReadBits(1)) { - if ((thiscoef & p1) == 0) { - if (thiscoef >= 0) { - thiscoef += p1; - } else { - thiscoef += m1; - } - } - } - coeffs[kJPEGNaturalOrder[k]] = thiscoef; - } - } - } - --(*eobrun); - return true; -} - -void SaveMCUCodingState(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - memcpy(m->mcu_.last_dc_coeff, m->last_dc_coeff_, sizeof(m->last_dc_coeff_)); - m->mcu_.eobrun = m->eobrun_; - size_t offset = 0; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - const jpeg_component_info* comp = cinfo->cur_comp_info[i]; - int c = comp->component_index; - size_t block_x = m->scan_mcu_col_ * comp->MCU_width; - for (int iy = 0; iy < comp->MCU_height; ++iy) { - size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy; - size_t biy = block_y % comp->v_samp_factor; - if (block_y >= comp->height_in_blocks) { - continue; - } - size_t nblocks = - std::min(comp->MCU_width, comp->width_in_blocks - block_x); - size_t ncoeffs = nblocks * DCTSIZE2; - coeff_t* coeffs = &m->coeff_rows[c][biy][block_x][0]; - memcpy(&m->mcu_.coeffs[offset], coeffs, ncoeffs * sizeof(coeffs[0])); - offset += ncoeffs; - } - } -} - -void RestoreMCUCodingState(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - memcpy(m->last_dc_coeff_, m->mcu_.last_dc_coeff, sizeof(m->last_dc_coeff_)); - m->eobrun_ = m->mcu_.eobrun; - size_t offset = 0; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - const jpeg_component_info* comp = cinfo->cur_comp_info[i]; - int c = comp->component_index; - size_t block_x = m->scan_mcu_col_ * comp->MCU_width; - for (int iy = 0; iy < comp->MCU_height; ++iy) { - size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy; - size_t biy = block_y % comp->v_samp_factor; - if (block_y >= comp->height_in_blocks) { - continue; - } - size_t nblocks = - std::min(comp->MCU_width, comp->width_in_blocks - block_x); - size_t ncoeffs = nblocks * DCTSIZE2; - coeff_t* coeffs = &m->coeff_rows[c][biy][block_x][0]; - memcpy(coeffs, &m->mcu_.coeffs[offset], ncoeffs * sizeof(coeffs[0])); - offset += ncoeffs; - } - } -} - -bool FinishScan(j_decompress_ptr cinfo, const uint8_t* data, const size_t len, - size_t* pos, size_t* bit_pos) { - jpeg_decomp_master* m = cinfo->master; - if (m->eobrun_ > 0) { - JPEGLI_ERROR("End-of-block run too long."); - } - m->eobrun_ = -1; - memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_)); - if (*bit_pos == 0) { - return true; - } - if (data[*pos] == 0xff) { - // After last br.FinishStream we checked that there is at least 2 bytes - // in the buffer. - JXL_DASSERT(*pos + 1 < len); - // br.FinishStream would have detected an early marker. - JXL_DASSERT(data[*pos + 1] == 0); - *pos += 2; - } else { - *pos += 1; - } - *bit_pos = 0; - return true; -} - -} // namespace - -void PrepareForiMCURow(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - const jpeg_component_info* comp = cinfo->cur_comp_info[i]; - int c = comp->component_index; - int by0 = cinfo->input_iMCU_row * comp->v_samp_factor; - int block_rows_left = comp->height_in_blocks - by0; - int max_block_rows = std::min(comp->v_samp_factor, block_rows_left); - int offset = m->streaming_mode_ ? 0 : by0; - m->coeff_rows[c] = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coef_arrays[c], offset, - max_block_rows, TRUE); - } -} - -int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data, - const size_t len, size_t* pos, size_t* bit_pos) { - if (len == 0) { - return kNeedMoreInput; - } - jpeg_decomp_master* m = cinfo->master; - for (;;) { - // Handle the restart intervals. - if (cinfo->restart_interval > 0 && m->restarts_to_go_ == 0) { - if (!FinishScan(cinfo, data, len, pos, bit_pos)) { - return kNeedMoreInput; - } - // Go to the next marker, warn if we had to skip any data. - size_t num_skipped = 0; - while (*pos + 1 < len && (data[*pos] != 0xff || data[*pos + 1] == 0 || - data[*pos + 1] == 0xff)) { - ++(*pos); - ++num_skipped; - } - if (num_skipped > 0) { - JPEGLI_WARN("Skipped %d bytes before restart marker", - static_cast(num_skipped)); - } - if (*pos + 2 > len) { - return kNeedMoreInput; - } - cinfo->unread_marker = data[*pos + 1]; - *pos += 2; - return kHandleRestart; - } - - size_t start_pos = *pos; - BitReaderState br(data, len, start_pos); - if (*bit_pos > 0) { - br.ReadBits(*bit_pos); - } - if (start_pos + kMaxMCUByteSize > len) { - SaveMCUCodingState(cinfo); - } - - // Decode one MCU. - HWY_ALIGN_MAX static coeff_t sink_block[DCTSIZE2] = {0}; - bool scan_ok = true; - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - const jpeg_component_info* comp = cinfo->cur_comp_info[i]; - int c = comp->component_index; - const HuffmanTableEntry* dc_lut = - &m->dc_huff_lut_[comp->dc_tbl_no * kJpegHuffmanLutSize]; - const HuffmanTableEntry* ac_lut = - &m->ac_huff_lut_[comp->ac_tbl_no * kJpegHuffmanLutSize]; - for (int iy = 0; iy < comp->MCU_height; ++iy) { - size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy; - int biy = block_y % comp->v_samp_factor; - for (int ix = 0; ix < comp->MCU_width; ++ix) { - size_t block_x = m->scan_mcu_col_ * comp->MCU_width + ix; - coeff_t* coeffs; - if (block_x >= comp->width_in_blocks || - block_y >= comp->height_in_blocks) { - // Note that it is OK that sink_block is uninitialized because - // it will never be used in any branches, even in the RefineDCTBlock - // case, because only DC scans can be interleaved and we don't use - // the zero-ness of the DC coeff in the DC refinement code-path. - coeffs = sink_block; - } else { - coeffs = &m->coeff_rows[c][biy][block_x][0]; - } - if (cinfo->Ah == 0) { - if (!DecodeDCTBlock(dc_lut, ac_lut, cinfo->Ss, cinfo->Se, cinfo->Al, - &m->eobrun_, &br, - &m->last_dc_coeff_[comp->component_index], - coeffs)) { - scan_ok = false; - } - } else { - if (!RefineDCTBlock(ac_lut, cinfo->Ss, cinfo->Se, cinfo->Al, - &m->eobrun_, &br, coeffs)) { - scan_ok = false; - } - } - } - } - } - size_t new_pos; - size_t new_bit_pos; - bool stream_ok = br.FinishStream(&new_pos, &new_bit_pos); - if (new_pos + 2 > len) { - // If reading stopped within the last two bytes, we have to request more - // input even if FinishStream() returned true, since the Huffman code - // reader could have peaked ahead some bits past the current input chunk - // and thus the last prefix code length could have been wrong. We can do - // this because a valid JPEG bit stream has two extra bytes at the end. - RestoreMCUCodingState(cinfo); - return kNeedMoreInput; - } - *pos = new_pos; - *bit_pos = new_bit_pos; - if (!stream_ok) { - // We hit a marker during parsing. - JXL_DASSERT(data[*pos] == 0xff); - JXL_DASSERT(data[*pos + 1] != 0); - RestoreMCUCodingState(cinfo); - JPEGLI_WARN("Incomplete scan detected."); - return JPEG_SCAN_COMPLETED; - } - if (!scan_ok) { - JPEGLI_ERROR("Failed to decode DCT block"); - } - if (m->restarts_to_go_ > 0) { - --m->restarts_to_go_; - } - ++m->scan_mcu_col_; - if (m->scan_mcu_col_ == cinfo->MCUs_per_row) { - ++m->scan_mcu_row_; - m->scan_mcu_col_ = 0; - if (m->scan_mcu_row_ == cinfo->MCU_rows_in_scan) { - if (!FinishScan(cinfo, data, len, pos, bit_pos)) { - return kNeedMoreInput; - } - break; - } else if ((m->scan_mcu_row_ % m->mcu_rows_per_iMCU_row_) == 0) { - // Current iMCU row is done. - break; - } - } - } - ++cinfo->input_iMCU_row; - if (cinfo->input_iMCU_row < cinfo->total_iMCU_rows) { - PrepareForiMCURow(cinfo); - return JPEG_ROW_COMPLETED; - } - return JPEG_SCAN_COMPLETED; -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.h b/third_party/jpeg-xl/lib/jpegli/decode_scan.h deleted file mode 100644 index dd1bfcd110283..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/decode_scan.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_DECODE_SCAN_H_ -#define LIB_JPEGLI_DECODE_SCAN_H_ - -#include - -#include "lib/jpegli/common.h" - -namespace jpegli { - -// Reads the available input in the source manager's input buffer until the end -// of the next iMCU row. -// The corresponding fields of cinfo are updated with the processed input data. -// Upon return, the input buffer will be at the start of an MCU, or at the end -// of the scan. -// Return value is one of: -// * JPEG_SUSPENDED, if the input buffer ends before the end of an iMCU row; -// * JPEG_ROW_COMPLETED, if the next iMCU row (but not the scan) is reached; -// * JPEG_SCAN_COMPLETED, if the end of the scan is reached. -int ProcessScan(j_decompress_ptr cinfo, const uint8_t* data, size_t len, - size_t* pos, size_t* bit_pos); - -void PrepareForiMCURow(j_decompress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_DECODE_SCAN_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc b/third_party/jpeg-xl/lib/jpegli/destination_manager.cc deleted file mode 100644 index 05d35797b87cb..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include "lib/jpegli/encode.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" - -namespace jpegli { - -constexpr size_t kDestBufferSize = 64 << 10; - -struct StdioDestinationManager { - jpeg_destination_mgr pub; - FILE* f; - uint8_t* buffer; - - static void init_destination(j_compress_ptr cinfo) { - auto* dest = reinterpret_cast(cinfo->dest); - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = kDestBufferSize; - } - - static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto* dest = reinterpret_cast(cinfo->dest); - if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) { - JPEGLI_ERROR("Failed to write to output stream."); - } - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = kDestBufferSize; - return TRUE; - } - - static void term_destination(j_compress_ptr cinfo) { - auto* dest = reinterpret_cast(cinfo->dest); - size_t bytes_left = kDestBufferSize - dest->pub.free_in_buffer; - if (bytes_left && - fwrite(dest->buffer, 1, bytes_left, dest->f) != bytes_left) { - JPEGLI_ERROR("Failed to write to output stream."); - } - fflush(dest->f); - if (ferror(dest->f)) { - JPEGLI_ERROR("Failed to write to output stream."); - } - } -}; - -struct MemoryDestinationManager { - jpeg_destination_mgr pub; - // Output buffer supplied by the application - uint8_t** output; - unsigned long* output_size; // NOLINT - // Output buffer allocated by us. - uint8_t* temp_buffer; - // Current output buffer (either application supplied or allocated by us). - uint8_t* current_buffer; - size_t buffer_size; - - static void init_destination(j_compress_ptr cinfo) {} - - static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto* dest = reinterpret_cast(cinfo->dest); - uint8_t* next_buffer = - reinterpret_cast(malloc(dest->buffer_size * 2)); - memcpy(next_buffer, dest->current_buffer, dest->buffer_size); - if (dest->temp_buffer != nullptr) { - free(dest->temp_buffer); - } - dest->temp_buffer = next_buffer; - dest->current_buffer = next_buffer; - *dest->output = next_buffer; - *dest->output_size = dest->buffer_size; - dest->pub.next_output_byte = next_buffer + dest->buffer_size; - dest->pub.free_in_buffer = dest->buffer_size; - dest->buffer_size *= 2; - return TRUE; - } - - static void term_destination(j_compress_ptr cinfo) { - auto* dest = reinterpret_cast(cinfo->dest); - *dest->output_size = dest->buffer_size - dest->pub.free_in_buffer; - } -}; - -} // namespace jpegli - -void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) { - if (outfile == nullptr) { - JPEGLI_ERROR("jpegli_stdio_dest: Invalid destination."); - } - if (cinfo->dest && cinfo->dest->init_destination != - jpegli::StdioDestinationManager::init_destination) { - JPEGLI_ERROR("jpegli_stdio_dest: a different dest manager was already set"); - } - if (!cinfo->dest) { - cinfo->dest = reinterpret_cast( - jpegli::Allocate(cinfo, 1)); - } - auto* dest = reinterpret_cast(cinfo->dest); - dest->f = outfile; - dest->buffer = jpegli::Allocate(cinfo, jpegli::kDestBufferSize); - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = jpegli::kDestBufferSize; - dest->pub.init_destination = - jpegli::StdioDestinationManager::init_destination; - dest->pub.empty_output_buffer = - jpegli::StdioDestinationManager::empty_output_buffer; - dest->pub.term_destination = - jpegli::StdioDestinationManager::term_destination; -} - -void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer, - unsigned long* outsize /* NOLINT */) { - if (outbuffer == nullptr || outsize == nullptr) { - JPEGLI_ERROR("jpegli_mem_dest: Invalid destination."); - } - if (cinfo->dest && cinfo->dest->init_destination != - jpegli::MemoryDestinationManager::init_destination) { - JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set"); - } - if (!cinfo->dest) { - auto* dest = jpegli::Allocate(cinfo, 1); - dest->temp_buffer = nullptr; - cinfo->dest = reinterpret_cast(dest); - } - auto* dest = reinterpret_cast(cinfo->dest); - dest->pub.init_destination = - jpegli::MemoryDestinationManager::init_destination; - dest->pub.empty_output_buffer = - jpegli::MemoryDestinationManager::empty_output_buffer; - dest->pub.term_destination = - jpegli::MemoryDestinationManager::term_destination; - dest->output = outbuffer; - dest->output_size = outsize; - if (*outbuffer == nullptr || *outsize == 0) { - dest->temp_buffer = - reinterpret_cast(malloc(jpegli::kDestBufferSize)); - *outbuffer = dest->temp_buffer; - *outsize = jpegli::kDestBufferSize; - } - dest->current_buffer = *outbuffer; - dest->buffer_size = *outsize; - dest->pub.next_output_byte = dest->current_buffer; - dest->pub.free_in_buffer = dest->buffer_size; -} diff --git a/third_party/jpeg-xl/lib/jpegli/downsample.cc b/third_party/jpeg-xl/lib/jpegli/downsample.cc deleted file mode 100644 index 1e5bb1563b190..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/downsample.cc +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/downsample.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/downsample.cc" -#include -#include - -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/error.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::Vec; - -using D = HWY_CAPPED(float, 8); -constexpr D d; - -void DownsampleRow2x1(const float* row_in, size_t len, float* row_out) { - const size_t N = Lanes(d); - const size_t len_out = len / 2; - const auto mul = Set(d, 0.5f); - Vec v0, v1; // NOLINT - for (size_t x = 0; x < len_out; x += N) { - LoadInterleaved2(d, row_in + 2 * x, v0, v1); - Store(Mul(mul, Add(v0, v1)), d, row_out + x); - } -} - -void DownsampleRow3x1(const float* row_in, size_t len, float* row_out) { - const size_t N = Lanes(d); - const size_t len_out = len / 3; - const auto mul = Set(d, 1.0f / 3); - Vec v0, v1, v2; // NOLINT - for (size_t x = 0; x < len_out; x += N) { - LoadInterleaved3(d, row_in + 3 * x, v0, v1, v2); - Store(Mul(mul, Add(Add(v0, v1), v2)), d, row_out + x); - } -} - -void DownsampleRow4x1(const float* row_in, size_t len, float* row_out) { - const size_t N = Lanes(d); - const size_t len_out = len / 4; - const auto mul = Set(d, 0.25f); - Vec v0, v1, v2, v3; // NOLINT - for (size_t x = 0; x < len_out; x += N) { - LoadInterleaved4(d, row_in + 4 * x, v0, v1, v2, v3); - Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x); - } -} - -void Downsample2x1(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow2x1(rows_in[0], len, row_out); -} - -void Downsample3x1(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow3x1(rows_in[0], len, row_out); -} - -void Downsample4x1(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow4x1(rows_in[0], len, row_out); -} - -void Downsample1x2(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - const size_t N = Lanes(d); - const auto mul = Set(d, 0.5f); - float* row0 = rows_in[0]; - float* row1 = rows_in[1]; - for (size_t x = 0; x < len; x += N) { - Store(Mul(mul, Add(Load(d, row0 + x), Load(d, row1 + x))), d, row_out + x); - } -} - -void Downsample2x2(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - const size_t N = Lanes(d); - const size_t len_out = len / 2; - const auto mul = Set(d, 0.25f); - float* row0 = rows_in[0]; - float* row1 = rows_in[1]; - Vec v0, v1, v2, v3; // NOLINT - for (size_t x = 0; x < len_out; x += N) { - LoadInterleaved2(d, row0 + 2 * x, v0, v1); - LoadInterleaved2(d, row1 + 2 * x, v2, v3); - Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x); - } -} - -void Downsample3x2(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow3x1(rows_in[0], len, rows_in[0]); - DownsampleRow3x1(rows_in[1], len, rows_in[1]); - Downsample1x2(rows_in, len / 3, row_out); -} - -void Downsample4x2(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow4x1(rows_in[0], len, rows_in[0]); - DownsampleRow4x1(rows_in[1], len, rows_in[1]); - Downsample1x2(rows_in, len / 4, row_out); -} - -void Downsample1x3(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - const size_t N = Lanes(d); - const auto mul = Set(d, 1.0f / 3); - float* row0 = rows_in[0]; - float* row1 = rows_in[1]; - float* row2 = rows_in[2]; - for (size_t x = 0; x < len; x += N) { - const auto in0 = Load(d, row0 + x); - const auto in1 = Load(d, row1 + x); - const auto in2 = Load(d, row2 + x); - Store(Mul(mul, Add(Add(in0, in1), in2)), d, row_out + x); - } -} - -void Downsample2x3(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow2x1(rows_in[0], len, rows_in[0]); - DownsampleRow2x1(rows_in[1], len, rows_in[1]); - DownsampleRow2x1(rows_in[2], len, rows_in[2]); - Downsample1x3(rows_in, len / 2, row_out); -} - -void Downsample3x3(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow3x1(rows_in[0], len, rows_in[0]); - DownsampleRow3x1(rows_in[1], len, rows_in[1]); - DownsampleRow3x1(rows_in[2], len, rows_in[2]); - Downsample1x3(rows_in, len / 3, row_out); -} - -void Downsample4x3(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow4x1(rows_in[0], len, rows_in[0]); - DownsampleRow4x1(rows_in[1], len, rows_in[1]); - DownsampleRow4x1(rows_in[2], len, rows_in[2]); - Downsample1x3(rows_in, len / 4, row_out); -} - -void Downsample1x4(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - const size_t N = Lanes(d); - const auto mul = Set(d, 0.25f); - float* row0 = rows_in[0]; - float* row1 = rows_in[1]; - float* row2 = rows_in[2]; - float* row3 = rows_in[3]; - for (size_t x = 0; x < len; x += N) { - const auto in0 = Load(d, row0 + x); - const auto in1 = Load(d, row1 + x); - const auto in2 = Load(d, row2 + x); - const auto in3 = Load(d, row3 + x); - Store(Mul(mul, Add(Add(in0, in1), Add(in2, in3))), d, row_out + x); - } -} - -void Downsample2x4(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow2x1(rows_in[0], len, rows_in[0]); - DownsampleRow2x1(rows_in[1], len, rows_in[1]); - DownsampleRow2x1(rows_in[2], len, rows_in[2]); - DownsampleRow2x1(rows_in[3], len, rows_in[3]); - Downsample1x4(rows_in, len / 2, row_out); -} - -void Downsample3x4(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow3x1(rows_in[0], len, rows_in[0]); - DownsampleRow3x1(rows_in[1], len, rows_in[1]); - DownsampleRow3x1(rows_in[2], len, rows_in[2]); - DownsampleRow3x1(rows_in[3], len, rows_in[3]); - Downsample1x4(rows_in, len / 3, row_out); -} - -void Downsample4x4(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) { - DownsampleRow4x1(rows_in[0], len, rows_in[0]); - DownsampleRow4x1(rows_in[1], len, rows_in[1]); - DownsampleRow4x1(rows_in[2], len, rows_in[2]); - DownsampleRow4x1(rows_in[3], len, rows_in[3]); - Downsample1x4(rows_in, len / 4, row_out); -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -HWY_EXPORT(Downsample1x2); -HWY_EXPORT(Downsample1x3); -HWY_EXPORT(Downsample1x4); -HWY_EXPORT(Downsample2x1); -HWY_EXPORT(Downsample2x2); -HWY_EXPORT(Downsample2x3); -HWY_EXPORT(Downsample2x4); -HWY_EXPORT(Downsample3x1); -HWY_EXPORT(Downsample3x2); -HWY_EXPORT(Downsample3x3); -HWY_EXPORT(Downsample3x4); -HWY_EXPORT(Downsample4x1); -HWY_EXPORT(Downsample4x2); -HWY_EXPORT(Downsample4x3); -HWY_EXPORT(Downsample4x4); - -void NullDownsample(float* rows_in[MAX_SAMP_FACTOR], size_t len, - float* row_out) {} - -void ChooseDownsampleMethods(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - for (int c = 0; c < cinfo->num_components; c++) { - m->downsample_method[c] = nullptr; - jpeg_component_info* comp = &cinfo->comp_info[c]; - const int h_factor = cinfo->max_h_samp_factor / comp->h_samp_factor; - const int v_factor = cinfo->max_v_samp_factor / comp->v_samp_factor; - if (v_factor == 1) { - if (h_factor == 1) { - m->downsample_method[c] = NullDownsample; - } else if (h_factor == 2) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x1); - } else if (h_factor == 3) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x1); - } else if (h_factor == 4) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x1); - } - } else if (v_factor == 2) { - if (h_factor == 1) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x2); - } else if (h_factor == 2) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x2); - } else if (h_factor == 3) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x2); - } else if (h_factor == 4) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x2); - } - } else if (v_factor == 3) { - if (h_factor == 1) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x2); - } else if (h_factor == 2) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x2); - } else if (h_factor == 3) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x2); - } else if (h_factor == 4) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x2); - } - } else if (v_factor == 4) { - if (h_factor == 1) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x4); - } else if (h_factor == 2) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x4); - } else if (h_factor == 3) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x4); - } else if (h_factor == 4) { - m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x4); - } - } - if (m->downsample_method[c] == nullptr) { - JPEGLI_ERROR("Unsupported downsampling ratio %dx%d", h_factor, v_factor); - } - } -} - -void DownsampleInputBuffer(j_compress_ptr cinfo) { - if (cinfo->max_h_samp_factor == 1 && cinfo->max_v_samp_factor == 1) { - return; - } - jpeg_comp_master* m = cinfo->master; - const size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - const size_t y0 = m->next_iMCU_row * iMCU_height; - const size_t y1 = y0 + iMCU_height; - const size_t xsize_padded = m->xsize_blocks * DCTSIZE; - for (int c = 0; c < cinfo->num_components; c++) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - const int h_factor = cinfo->max_h_samp_factor / comp->h_samp_factor; - const int v_factor = cinfo->max_v_samp_factor / comp->v_samp_factor; - if (h_factor == 1 && v_factor == 1) { - continue; - } - auto& input = *m->smooth_input[c]; - auto& output = *m->raw_data[c]; - const size_t y_out0 = y0 / v_factor; - float* rows_in[MAX_SAMP_FACTOR]; - for (size_t y_in = y0, y_out = y_out0; y_in < y1; - y_in += v_factor, ++y_out) { - for (int iy = 0; iy < v_factor; ++iy) { - rows_in[iy] = input.Row(y_in + iy); - } - float* row_out = output.Row(y_out); - (*m->downsample_method[c])(rows_in, xsize_padded, row_out); - } - } -} - -void ApplyInputSmoothing(j_compress_ptr cinfo) { - if (!cinfo->smoothing_factor) { - return; - } - jpeg_comp_master* m = cinfo->master; - const float kW1 = cinfo->smoothing_factor / 1024.0; - const float kW0 = 1.0f - 8.0f * kW1; - const size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - const ssize_t y0 = m->next_iMCU_row * iMCU_height; - const ssize_t y1 = y0 + iMCU_height; - const ssize_t xsize_padded = m->xsize_blocks * DCTSIZE; - for (int c = 0; c < cinfo->num_components; c++) { - auto& input = m->input_buffer[c]; - auto& output = *m->smooth_input[c]; - if (m->next_iMCU_row == 0) { - input.CopyRow(-1, 0, 1); - } - if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) { - size_t last_row = m->ysize_blocks * DCTSIZE - 1; - input.CopyRow(last_row + 1, last_row, 1); - } - // TODO(szabadka) SIMDify this. - for (ssize_t y = y0; y < y1; ++y) { - const float* row_t = input.Row(y - 1); - const float* row_m = input.Row(y); - const float* row_b = input.Row(y + 1); - float* row_out = output.Row(y); - for (ssize_t x = 0; x < xsize_padded; ++x) { - float val_tl = row_t[x - 1]; - float val_tm = row_t[x]; - float val_tr = row_t[x + 1]; - float val_ml = row_m[x - 1]; - float val_mm = row_m[x]; - float val_mr = row_m[x + 1]; - float val_bl = row_b[x - 1]; - float val_bm = row_b[x]; - float val_br = row_b[x + 1]; - float val1 = (val_tl + val_tm + val_tr + val_ml + val_mr + val_bl + - val_bm + val_br); - row_out[x] = val_mm * kW0 + val1 * kW1; - } - } - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/downsample.h b/third_party/jpeg-xl/lib/jpegli/downsample.h deleted file mode 100644 index 3ccf069e4e50c..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/downsample.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_DOWNSAMPLE_H_ -#define LIB_JPEGLI_DOWNSAMPLE_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void ChooseDownsampleMethods(j_compress_ptr cinfo); - -void DownsampleInputBuffer(j_compress_ptr cinfo); - -void ApplyInputSmoothing(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_DOWNSAMPLE_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/encode.cc b/third_party/jpeg-xl/lib/jpegli/encode.cc deleted file mode 100644 index 410eeed8083ec..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode.cc +++ /dev/null @@ -1,1274 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/encode.h" - -#include - -#include -#include -#include - -#include "lib/jpegli/adaptive_quantization.h" -#include "lib/jpegli/bit_writer.h" -#include "lib/jpegli/bitstream.h" -#include "lib/jpegli/color_transform.h" -#include "lib/jpegli/downsample.h" -#include "lib/jpegli/encode_finish.h" -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/encode_streaming.h" -#include "lib/jpegli/entropy_coding.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/huffman.h" -#include "lib/jpegli/input.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jpegli/quant.h" - -namespace jpegli { - -constexpr size_t kMaxBytesInMarker = 65533; - -void CheckState(j_compress_ptr cinfo, int state) { - if (cinfo->global_state != state) { - JPEGLI_ERROR("Unexpected global state %d [expected %d]", - cinfo->global_state, state); - } -} - -void CheckState(j_compress_ptr cinfo, int state1, int state2) { - if (cinfo->global_state != state1 && cinfo->global_state != state2) { - JPEGLI_ERROR("Unexpected global state %d [expected %d or %d]", - cinfo->global_state, state1, state2); - } -} - -// -// Parameter setup -// - -// Initialize cinfo fields that are not dependent on input image. This is shared -// between jpegli_CreateCompress() and jpegli_set_defaults() -void InitializeCompressParams(j_compress_ptr cinfo) { - cinfo->data_precision = 8; - cinfo->num_scans = 0; - cinfo->scan_info = nullptr; - cinfo->raw_data_in = FALSE; - cinfo->arith_code = FALSE; - cinfo->optimize_coding = FALSE; - cinfo->CCIR601_sampling = FALSE; - cinfo->smoothing_factor = 0; - cinfo->dct_method = JDCT_FLOAT; - cinfo->restart_interval = 0; - cinfo->restart_in_rows = 0; - cinfo->write_JFIF_header = FALSE; - cinfo->JFIF_major_version = 1; - cinfo->JFIF_minor_version = 1; - cinfo->density_unit = 0; - cinfo->X_density = 1; - cinfo->Y_density = 1; -#if JPEG_LIB_VERSION >= 70 - cinfo->scale_num = 1; - cinfo->scale_denom = 1; - cinfo->do_fancy_downsampling = FALSE; - cinfo->min_DCT_h_scaled_size = DCTSIZE; - cinfo->min_DCT_v_scaled_size = DCTSIZE; -#endif - cinfo->master->psnr_target = 0.0f; - cinfo->master->psnr_tolerance = 0.01f; - cinfo->master->min_distance = 0.1f; - cinfo->master->max_distance = 25.0f; -} - -float LinearQualityToDistance(int scale_factor) { - scale_factor = std::min(5000, std::max(0, scale_factor)); - int quality = - scale_factor < 100 ? 100 - scale_factor / 2 : 5000 / scale_factor; - return jpegli_quality_to_distance(quality); -} - -template -void SetSentTableFlag(T** table_ptrs, size_t num, boolean val) { - for (size_t i = 0; i < num; ++i) { - if (table_ptrs[i]) table_ptrs[i]->sent_table = val; - } -} - -// -// Compressor initialization -// - -struct ProgressiveScan { - int Ss, Se, Ah, Al; - bool interleaved; -}; - -void SetDefaultScanScript(j_compress_ptr cinfo) { - int level = cinfo->master->progressive_level; - std::vector progressive_mode; - bool interleave_dc = - (cinfo->max_h_samp_factor == 1 && cinfo->max_v_samp_factor == 1); - if (level == 0) { - progressive_mode.push_back({0, 63, 0, 0, true}); - } else if (level == 1) { - progressive_mode.push_back({0, 0, 0, 0, interleave_dc}); - progressive_mode.push_back({1, 63, 0, 1, false}); - progressive_mode.push_back({1, 63, 1, 0, false}); - } else { - progressive_mode.push_back({0, 0, 0, 0, interleave_dc}); - progressive_mode.push_back({1, 2, 0, 0, false}); - progressive_mode.push_back({3, 63, 0, 2, false}); - progressive_mode.push_back({3, 63, 2, 1, false}); - progressive_mode.push_back({3, 63, 1, 0, false}); - } - - cinfo->script_space_size = 0; - for (const auto& scan : progressive_mode) { - int comps = scan.interleaved ? MAX_COMPS_IN_SCAN : 1; - cinfo->script_space_size += DivCeil(cinfo->num_components, comps); - } - cinfo->script_space = - Allocate(cinfo, cinfo->script_space_size); - - jpeg_scan_info* next_scan = cinfo->script_space; - for (const auto& scan : progressive_mode) { - int comps = scan.interleaved ? MAX_COMPS_IN_SCAN : 1; - for (int c = 0; c < cinfo->num_components; c += comps) { - next_scan->Ss = scan.Ss; - next_scan->Se = scan.Se; - next_scan->Ah = scan.Ah; - next_scan->Al = scan.Al; - next_scan->comps_in_scan = std::min(comps, cinfo->num_components - c); - for (int j = 0; j < next_scan->comps_in_scan; ++j) { - next_scan->component_index[j] = c + j; - } - ++next_scan; - } - } - JPEGLI_CHECK(next_scan - cinfo->script_space == cinfo->script_space_size); - cinfo->scan_info = cinfo->script_space; - cinfo->num_scans = cinfo->script_space_size; -} - -void ValidateScanScript(j_compress_ptr cinfo) { - // Mask of coefficient bits defined by the scan script, for each component - // and coefficient index. - uint16_t comp_mask[kMaxComponents][DCTSIZE2] = {}; - static constexpr int kMaxRefinementBit = 10; - - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info& si = cinfo->scan_info[i]; - if (si.comps_in_scan < 1 || si.comps_in_scan > MAX_COMPS_IN_SCAN) { - JPEGLI_ERROR("Invalid number of components in scan %d", si.comps_in_scan); - } - int last_ci = -1; - for (int j = 0; j < si.comps_in_scan; ++j) { - int ci = si.component_index[j]; - if (ci < 0 || ci >= cinfo->num_components) { - JPEGLI_ERROR("Invalid component index %d in scan", ci); - } else if (ci == last_ci) { - JPEGLI_ERROR("Duplicate component index %d in scan", ci); - } else if (ci < last_ci) { - JPEGLI_ERROR("Out of order component index %d in scan", ci); - } - last_ci = ci; - } - if (si.Ss < 0 || si.Se < si.Ss || si.Se >= DCTSIZE2) { - JPEGLI_ERROR("Invalid spectral range %d .. %d in scan", si.Ss, si.Se); - } - if (si.Ah < 0 || si.Al < 0 || si.Al > kMaxRefinementBit) { - JPEGLI_ERROR("Invalid refinement bits %d/%d", si.Ah, si.Al); - } - if (!cinfo->progressive_mode) { - if (si.Ss != 0 || si.Se != DCTSIZE2 - 1 || si.Ah != 0 || si.Al != 0) { - JPEGLI_ERROR("Invalid scan for sequential mode"); - } - } else { - if (si.Ss == 0 && si.Se != 0) { - JPEGLI_ERROR("DC and AC together in progressive scan"); - } - } - if (si.Ss != 0 && si.comps_in_scan != 1) { - JPEGLI_ERROR("Interleaved AC only scan."); - } - for (int j = 0; j < si.comps_in_scan; ++j) { - int ci = si.component_index[j]; - if (si.Ss != 0 && comp_mask[ci][0] == 0) { - JPEGLI_ERROR("AC before DC in component %d of scan", ci); - } - for (int k = si.Ss; k <= si.Se; ++k) { - if (comp_mask[ci][k] == 0) { - if (si.Ah != 0) { - JPEGLI_ERROR("Invalid first scan refinement bit"); - } - comp_mask[ci][k] = ((0xffff << si.Al) & 0xffff); - } else { - if (comp_mask[ci][k] != ((0xffff << si.Ah) & 0xffff) || - si.Al != si.Ah - 1) { - JPEGLI_ERROR("Invalid refinement bit progression."); - } - comp_mask[ci][k] |= 1 << si.Al; - } - } - } - if (si.comps_in_scan > 1) { - size_t mcu_size = 0; - for (int j = 0; j < si.comps_in_scan; ++j) { - int ci = si.component_index[j]; - jpeg_component_info* comp = &cinfo->comp_info[ci]; - mcu_size += comp->h_samp_factor * comp->v_samp_factor; - } - if (mcu_size > C_MAX_BLOCKS_IN_MCU) { - JPEGLI_ERROR("MCU size too big"); - } - } - } - for (int c = 0; c < cinfo->num_components; ++c) { - for (int k = 0; k < DCTSIZE2; ++k) { - if (comp_mask[c][k] != 0xffff) { - JPEGLI_ERROR("Incomplete scan of component %d and frequency %d", c, k); - } - } - } -} - -void ProcessCompressionParams(j_compress_ptr cinfo) { - if (cinfo->dest == nullptr) { - JPEGLI_ERROR("Missing destination."); - } - if (cinfo->image_width < 1 || cinfo->image_height < 1 || - cinfo->input_components < 1) { - JPEGLI_ERROR("Empty input image."); - } - if (cinfo->image_width > static_cast(JPEG_MAX_DIMENSION) || - cinfo->image_height > static_cast(JPEG_MAX_DIMENSION) || - cinfo->input_components > static_cast(kMaxComponents)) { - JPEGLI_ERROR("Input image too big."); - } - if (cinfo->num_components < 1 || - cinfo->num_components > static_cast(kMaxComponents)) { - JPEGLI_ERROR("Invalid number of components."); - } - if (cinfo->data_precision != kJpegPrecision) { - JPEGLI_ERROR("Invalid data precision"); - } - if (cinfo->arith_code) { - JPEGLI_ERROR("Arithmetic coding is not implemented."); - } - if (cinfo->CCIR601_sampling) { - JPEGLI_ERROR("CCIR601 sampling is not implemented."); - } - if (cinfo->restart_interval > 65535u) { - JPEGLI_ERROR("Restart interval too big"); - } - if (cinfo->smoothing_factor < 0 || cinfo->smoothing_factor > 100) { - JPEGLI_ERROR("Invalid smoothing factor %d", cinfo->smoothing_factor); - } - jpeg_comp_master* m = cinfo->master; - cinfo->max_h_samp_factor = cinfo->max_v_samp_factor = 1; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - if (comp->component_index != c) { - JPEGLI_ERROR("Invalid component index"); - } - for (int j = 0; j < c; ++j) { - if (cinfo->comp_info[j].component_id == comp->component_id) { - JPEGLI_ERROR("Duplicate component id %d", comp->component_id); - } - } - if (comp->h_samp_factor <= 0 || comp->v_samp_factor <= 0 || - comp->h_samp_factor > MAX_SAMP_FACTOR || - comp->v_samp_factor > MAX_SAMP_FACTOR) { - JPEGLI_ERROR("Invalid sampling factor %d x %d", comp->h_samp_factor, - comp->v_samp_factor); - } - if (cinfo->num_components == 1) { - // Force samp factors to 1x1 for single-component images. - comp->h_samp_factor = comp->v_samp_factor = 1; - } - cinfo->max_h_samp_factor = - std::max(comp->h_samp_factor, cinfo->max_h_samp_factor); - cinfo->max_v_samp_factor = - std::max(comp->v_samp_factor, cinfo->max_v_samp_factor); - } - size_t iMCU_width = DCTSIZE * cinfo->max_h_samp_factor; - size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - size_t total_iMCU_cols = DivCeil(cinfo->image_width, iMCU_width); - cinfo->total_iMCU_rows = DivCeil(cinfo->image_height, iMCU_height); - m->xsize_blocks = total_iMCU_cols * cinfo->max_h_samp_factor; - m->ysize_blocks = cinfo->total_iMCU_rows * cinfo->max_v_samp_factor; - - size_t blocks_per_iMCU = 0; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - if (cinfo->max_h_samp_factor % comp->h_samp_factor != 0 || - cinfo->max_v_samp_factor % comp->v_samp_factor != 0) { - JPEGLI_ERROR("Non-integral sampling ratios are not supported."); - } - m->h_factor[c] = cinfo->max_h_samp_factor / comp->h_samp_factor; - m->v_factor[c] = cinfo->max_v_samp_factor / comp->v_samp_factor; - comp->downsampled_width = DivCeil(cinfo->image_width, m->h_factor[c]); - comp->downsampled_height = DivCeil(cinfo->image_height, m->v_factor[c]); - comp->width_in_blocks = DivCeil(comp->downsampled_width, DCTSIZE); - comp->height_in_blocks = DivCeil(comp->downsampled_height, DCTSIZE); - blocks_per_iMCU += comp->h_samp_factor * comp->v_samp_factor; - } - m->blocks_per_iMCU_row = total_iMCU_cols * blocks_per_iMCU; - // Disable adaptive quantization for subsampled luma channel. - int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0; - jpeg_component_info* y_comp = &cinfo->comp_info[y_channel]; - if (y_comp->h_samp_factor != cinfo->max_h_samp_factor || - y_comp->v_samp_factor != cinfo->max_v_samp_factor) { - m->use_adaptive_quantization = false; - } - if (cinfo->scan_info == nullptr) { - SetDefaultScanScript(cinfo); - } - cinfo->progressive_mode = TO_JXL_BOOL(cinfo->scan_info->Ss != 0 || - cinfo->scan_info->Se != DCTSIZE2 - 1); - ValidateScanScript(cinfo); - m->scan_token_info = - Allocate(cinfo, cinfo->num_scans, JPOOL_IMAGE); - memset(m->scan_token_info, 0, cinfo->num_scans * sizeof(ScanTokenInfo)); - m->ac_ctx_offset = Allocate(cinfo, cinfo->num_scans, JPOOL_IMAGE); - size_t num_ac_contexts = 0; - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info* scan_info = &cinfo->scan_info[i]; - m->ac_ctx_offset[i] = 4 + num_ac_contexts; - if (scan_info->Se > 0) { - num_ac_contexts += scan_info->comps_in_scan; - } - if (num_ac_contexts > 252) { - JPEGLI_ERROR("Too many AC scans in image"); - } - ScanTokenInfo* sti = &m->scan_token_info[i]; - if (scan_info->comps_in_scan == 1) { - int comp_idx = scan_info->component_index[0]; - jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - sti->MCUs_per_row = comp->width_in_blocks; - sti->MCU_rows_in_scan = comp->height_in_blocks; - sti->blocks_in_MCU = 1; - } else { - sti->MCUs_per_row = - DivCeil(cinfo->image_width, DCTSIZE * cinfo->max_h_samp_factor); - sti->MCU_rows_in_scan = - DivCeil(cinfo->image_height, DCTSIZE * cinfo->max_v_samp_factor); - sti->blocks_in_MCU = 0; - for (int j = 0; j < scan_info->comps_in_scan; ++j) { - int comp_idx = scan_info->component_index[j]; - jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - sti->blocks_in_MCU += comp->h_samp_factor * comp->v_samp_factor; - } - } - size_t num_MCUs = sti->MCU_rows_in_scan * sti->MCUs_per_row; - sti->num_blocks = num_MCUs * sti->blocks_in_MCU; - if (cinfo->restart_in_rows <= 0) { - sti->restart_interval = cinfo->restart_interval; - } else { - sti->restart_interval = - std::min(sti->MCUs_per_row * cinfo->restart_in_rows, 65535u); - } - sti->num_restarts = sti->restart_interval > 0 - ? DivCeil(num_MCUs, sti->restart_interval) - : 1; - sti->restarts = Allocate(cinfo, sti->num_restarts, JPOOL_IMAGE); - } - m->num_contexts = 4 + num_ac_contexts; -} - -bool IsStreamingSupported(j_compress_ptr cinfo) { - if (cinfo->global_state == kEncWriteCoeffs) { - return false; - } - // TODO(szabadka) Remove this restriction. - if (cinfo->restart_interval > 0 || cinfo->restart_in_rows > 0) { - return false; - } - if (cinfo->num_scans > 1) { - return false; - } - if (cinfo->master->psnr_target > 0) { - return false; - } - return true; -} - -void AllocateBuffers(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - memset(m->last_dc_coeff, 0, sizeof(m->last_dc_coeff)); - if (!IsStreamingSupported(cinfo) || cinfo->optimize_coding) { - int ysize_blocks = DivCeil(cinfo->image_height, DCTSIZE); - int num_arrays = cinfo->num_scans * ysize_blocks; - m->token_arrays = Allocate(cinfo, num_arrays, JPOOL_IMAGE); - m->cur_token_array = 0; - memset(m->token_arrays, 0, num_arrays * sizeof(TokenArray)); - m->num_tokens = 0; - m->total_num_tokens = 0; - } - if (cinfo->global_state == kEncWriteCoeffs) { - return; - } - size_t iMCU_width = DCTSIZE * cinfo->max_h_samp_factor; - size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - size_t total_iMCU_cols = DivCeil(cinfo->image_width, iMCU_width); - size_t xsize_full = total_iMCU_cols * iMCU_width; - size_t ysize_full = 3 * iMCU_height; - if (!cinfo->raw_data_in) { - int num_all_components = - std::max(cinfo->input_components, cinfo->num_components); - for (int c = 0; c < num_all_components; ++c) { - m->input_buffer[c].Allocate(cinfo, ysize_full, xsize_full); - } - } - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - size_t xsize = total_iMCU_cols * comp->h_samp_factor * DCTSIZE; - size_t ysize = 3 * comp->v_samp_factor * DCTSIZE; - if (cinfo->raw_data_in) { - m->input_buffer[c].Allocate(cinfo, ysize, xsize); - } - m->smooth_input[c] = &m->input_buffer[c]; - if (!cinfo->raw_data_in && cinfo->smoothing_factor) { - m->smooth_input[c] = Allocate>(cinfo, 1, JPOOL_IMAGE); - m->smooth_input[c]->Allocate(cinfo, ysize_full, xsize_full); - } - m->raw_data[c] = m->smooth_input[c]; - if (!cinfo->raw_data_in && (m->h_factor[c] > 1 || m->v_factor[c] > 1)) { - m->raw_data[c] = Allocate>(cinfo, 1, JPOOL_IMAGE); - m->raw_data[c]->Allocate(cinfo, ysize, xsize); - } - m->quant_mul[c] = Allocate(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED); - } - m->dct_buffer = Allocate(cinfo, 2 * DCTSIZE2, JPOOL_IMAGE_ALIGNED); - m->block_tmp = Allocate(cinfo, DCTSIZE2 * 4, JPOOL_IMAGE_ALIGNED); - if (!IsStreamingSupported(cinfo)) { - m->coeff_buffers = - Allocate(cinfo, cinfo->num_components, JPOOL_IMAGE); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - const size_t xsize_blocks = comp->width_in_blocks; - const size_t ysize_blocks = comp->height_in_blocks; - m->coeff_buffers[c] = (*cinfo->mem->request_virt_barray)( - reinterpret_cast(cinfo), JPOOL_IMAGE, - /*pre_zero=*/FALSE, xsize_blocks, ysize_blocks, comp->v_samp_factor); - } - } - if (m->use_adaptive_quantization) { - int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0; - jpeg_component_info* y_comp = &cinfo->comp_info[y_channel]; - const size_t xsize_blocks = y_comp->width_in_blocks; - const size_t vecsize = VectorSize(); - const size_t xsize_padded = DivCeil(2 * xsize_blocks, vecsize) * vecsize; - m->diff_buffer = - Allocate(cinfo, xsize_blocks * DCTSIZE + 8, JPOOL_IMAGE_ALIGNED); - m->fuzzy_erosion_tmp.Allocate(cinfo, 2, xsize_padded); - m->pre_erosion.Allocate(cinfo, 6 * cinfo->max_v_samp_factor, xsize_padded); - size_t qf_height = cinfo->max_v_samp_factor; - if (m->psnr_target > 0) { - qf_height *= cinfo->total_iMCU_rows; - } - m->quant_field.Allocate(cinfo, qf_height, xsize_blocks); - } else { - m->quant_field.Allocate(cinfo, 1, m->xsize_blocks); - m->quant_field.FillRow(0, 0, m->xsize_blocks); - } - for (int c = 0; c < cinfo->num_components; ++c) { - m->zero_bias_offset[c] = - Allocate(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED); - m->zero_bias_mul[c] = Allocate(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED); - memset(m->zero_bias_mul[c], 0, DCTSIZE2 * sizeof(float)); - memset(m->zero_bias_offset[c], 0, DCTSIZE2 * sizeof(float)); - } -} - -void InitProgressMonitor(j_compress_ptr cinfo) { - if (cinfo->progress == nullptr) { - return; - } - if (IsStreamingSupported(cinfo)) { - // We have only one input pass. - cinfo->progress->total_passes = 1; - } else { - // We have one input pass, a histogram pass for each scan, and an encode - // pass for each scan. - cinfo->progress->total_passes = 1 + 2 * cinfo->num_scans; - } -} - -// Common setup code between streaming and transcoding code paths. Called in -// both jpegli_start_compress() and jpegli_write_coefficients(). -void InitCompress(j_compress_ptr cinfo, boolean write_all_tables) { - jpeg_comp_master* m = cinfo->master; - (*cinfo->err->reset_error_mgr)(reinterpret_cast(cinfo)); - ProcessCompressionParams(cinfo); - InitProgressMonitor(cinfo); - AllocateBuffers(cinfo); - if (cinfo->global_state != kEncWriteCoeffs) { - ChooseInputMethod(cinfo); - if (!cinfo->raw_data_in) { - ChooseColorTransform(cinfo); - ChooseDownsampleMethods(cinfo); - } - QuantPass pass = m->psnr_target > 0 ? QuantPass::SEARCH_FIRST_PASS - : QuantPass::NO_SEARCH; - InitQuantizer(cinfo, pass); - } - if (write_all_tables) { - jpegli_suppress_tables(cinfo, FALSE); - } - if (!cinfo->optimize_coding && !cinfo->progressive_mode) { - CopyHuffmanTables(cinfo); - InitEntropyCoder(cinfo); - } - (*cinfo->dest->init_destination)(cinfo); - WriteFileHeader(cinfo); - JpegBitWriterInit(cinfo); - m->next_iMCU_row = 0; - m->last_restart_interval = 0; - m->next_dht_index = 0; -} - -// -// Input streaming -// - -void ProgressMonitorInputPass(j_compress_ptr cinfo) { - if (cinfo->progress == nullptr) { - return; - } - cinfo->progress->completed_passes = 0; - cinfo->progress->pass_counter = cinfo->next_scanline; - cinfo->progress->pass_limit = cinfo->image_height; - (*cinfo->progress->progress_monitor)(reinterpret_cast(cinfo)); -} - -void ReadInputRow(j_compress_ptr cinfo, const uint8_t* scanline, - float* row[kMaxComponents]) { - jpeg_comp_master* m = cinfo->master; - int num_all_components = - std::max(cinfo->input_components, cinfo->num_components); - for (int c = 0; c < num_all_components; ++c) { - row[c] = m->input_buffer[c].Row(m->next_input_row); - } - ++m->next_input_row; - if (scanline == nullptr) { - for (int c = 0; c < cinfo->input_components; ++c) { - memset(row[c], 0, cinfo->image_width * sizeof(row[c][0])); - } - return; - } - (*m->input_method)(scanline, cinfo->image_width, row); -} - -void PadInputBuffer(j_compress_ptr cinfo, float* row[kMaxComponents]) { - jpeg_comp_master* m = cinfo->master; - const size_t len0 = cinfo->image_width; - const size_t len1 = m->xsize_blocks * DCTSIZE; - for (int c = 0; c < cinfo->num_components; ++c) { - // Pad row to a multiple of the iMCU width, plus create a border of 1 - // repeated pixel for adaptive quant field calculation. - float last_val = row[c][len0 - 1]; - for (size_t x = len0; x <= len1; ++x) { - row[c][x] = last_val; - } - row[c][-1] = row[c][0]; - } - if (m->next_input_row == cinfo->image_height) { - size_t num_rows = m->ysize_blocks * DCTSIZE - cinfo->image_height; - for (size_t i = 0; i < num_rows; ++i) { - for (int c = 0; c < cinfo->num_components; ++c) { - float* dest = m->input_buffer[c].Row(m->next_input_row) - 1; - memcpy(dest, row[c] - 1, (len1 + 2) * sizeof(dest[0])); - } - ++m->next_input_row; - } - } -} - -void ProcessiMCURow(j_compress_ptr cinfo) { - JPEGLI_CHECK(cinfo->master->next_iMCU_row < cinfo->total_iMCU_rows); - if (!cinfo->raw_data_in) { - ApplyInputSmoothing(cinfo); - DownsampleInputBuffer(cinfo); - } - ComputeAdaptiveQuantField(cinfo); - if (IsStreamingSupported(cinfo)) { - if (cinfo->optimize_coding) { - ComputeTokensForiMCURow(cinfo); - } else { - WriteiMCURow(cinfo); - } - } else { - ComputeCoefficientsForiMCURow(cinfo); - } - ++cinfo->master->next_iMCU_row; -} - -void ProcessiMCURows(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - // To have context rows both above and below the current iMCU row, we delay - // processing the first iMCU row and process two iMCU rows after we receive - // the last input row. - if (m->next_input_row % iMCU_height == 0 && m->next_input_row > iMCU_height) { - ProcessiMCURow(cinfo); - } - if (m->next_input_row >= cinfo->image_height) { - ProcessiMCURow(cinfo); - } -} - -// -// Non-streaming part -// - -void ZigZagShuffleBlocks(j_compress_ptr cinfo) { - JCOEF tmp[DCTSIZE2]; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY blocks = GetBlockRow(cinfo, c, by); - for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { - JCOEF* block = &blocks[0][bx][0]; - for (int k = 0; k < DCTSIZE2; ++k) { - tmp[k] = block[kJPEGNaturalOrder[k]]; - } - memcpy(block, tmp, sizeof(tmp)); - } - } - } -} - -} // namespace jpegli - -// -// Parameter setup -// - -void jpegli_CreateCompress(j_compress_ptr cinfo, int version, - size_t structsize) { - cinfo->mem = nullptr; - if (structsize != sizeof(*cinfo)) { - JPEGLI_ERROR("jpegli_compress_struct has wrong size."); - } - jpegli::InitMemoryManager(reinterpret_cast(cinfo)); - cinfo->progress = nullptr; - cinfo->is_decompressor = FALSE; - cinfo->global_state = jpegli::kEncStart; - cinfo->dest = nullptr; - cinfo->image_width = 0; - cinfo->image_height = 0; - cinfo->input_components = 0; - cinfo->in_color_space = JCS_UNKNOWN; - cinfo->input_gamma = 1.0f; - cinfo->num_components = 0; - cinfo->jpeg_color_space = JCS_UNKNOWN; - cinfo->comp_info = nullptr; - for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) { - quant_tbl_ptr = nullptr; - } - for (int i = 0; i < NUM_HUFF_TBLS; ++i) { - cinfo->dc_huff_tbl_ptrs[i] = nullptr; - cinfo->ac_huff_tbl_ptrs[i] = nullptr; - } - memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L)); - memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U)); - memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K)); - cinfo->write_Adobe_marker = FALSE; - cinfo->master = jpegli::Allocate(cinfo, 1); - jpegli::InitializeCompressParams(cinfo); - cinfo->master->force_baseline = true; - cinfo->master->xyb_mode = false; - cinfo->master->cicp_transfer_function = 2; // unknown transfer function code - cinfo->master->use_std_tables = false; - cinfo->master->use_adaptive_quantization = true; - cinfo->master->progressive_level = jpegli::kDefaultProgressiveLevel; - cinfo->master->data_type = JPEGLI_TYPE_UINT8; - cinfo->master->endianness = JPEGLI_NATIVE_ENDIAN; - cinfo->master->coeff_buffers = nullptr; -} - -void jpegli_set_xyb_mode(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->xyb_mode = true; -} - -void jpegli_set_cicp_transfer_function(j_compress_ptr cinfo, int code) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->cicp_transfer_function = code; -} - -void jpegli_set_defaults(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - jpegli::InitializeCompressParams(cinfo); - jpegli_default_colorspace(cinfo); - jpegli_set_quality(cinfo, 90, TRUE); - jpegli_set_progressive_level(cinfo, jpegli::kDefaultProgressiveLevel); - jpegli::AddStandardHuffmanTables(reinterpret_cast(cinfo), - /*is_dc=*/false); - jpegli::AddStandardHuffmanTables(reinterpret_cast(cinfo), - /*is_dc=*/true); -} - -void jpegli_default_colorspace(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - if (cinfo->in_color_space == JCS_RGB && cinfo->master->xyb_mode) { - jpegli_set_colorspace(cinfo, JCS_RGB); - return; - } - switch (cinfo->in_color_space) { - case JCS_GRAYSCALE: - jpegli_set_colorspace(cinfo, JCS_GRAYSCALE); - break; - case JCS_RGB: -#ifdef JCS_EXTENSIONS - case JCS_EXT_RGB: - case JCS_EXT_BGR: - case JCS_EXT_RGBX: - case JCS_EXT_BGRX: - case JCS_EXT_XRGB: - case JCS_EXT_XBGR: -#endif -#if JCS_ALPHA_EXTENSIONS - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_EXT_ARGB: - case JCS_EXT_ABGR: -#endif - jpegli_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_YCbCr: - jpegli_set_colorspace(cinfo, JCS_YCbCr); - break; - case JCS_CMYK: - jpegli_set_colorspace(cinfo, JCS_CMYK); - break; - case JCS_YCCK: - jpegli_set_colorspace(cinfo, JCS_YCCK); - break; - case JCS_UNKNOWN: - jpegli_set_colorspace(cinfo, JCS_UNKNOWN); - break; - default: - JPEGLI_ERROR("Unsupported input colorspace %d", cinfo->in_color_space); - } -} - -void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->jpeg_color_space = colorspace; - switch (colorspace) { - case JCS_GRAYSCALE: - cinfo->num_components = 1; - break; - case JCS_RGB: - case JCS_YCbCr: - cinfo->num_components = 3; - break; - case JCS_CMYK: - case JCS_YCCK: - cinfo->num_components = 4; - break; - case JCS_UNKNOWN: - cinfo->num_components = - std::min(jpegli::kMaxComponents, cinfo->input_components); - break; - default: - JPEGLI_ERROR("Unsupported jpeg colorspace %d", colorspace); - } - // Adobe marker is only needed to distinguish CMYK and YCCK JPEGs. - cinfo->write_Adobe_marker = TO_JXL_BOOL(cinfo->jpeg_color_space == JCS_YCCK); - if (cinfo->comp_info == nullptr) { - cinfo->comp_info = - jpegli::Allocate(cinfo, MAX_COMPONENTS); - } - memset(cinfo->comp_info, 0, - jpegli::kMaxComponents * sizeof(jpeg_component_info)); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - comp->component_index = c; - comp->component_id = c + 1; - comp->h_samp_factor = 1; - comp->v_samp_factor = 1; - comp->quant_tbl_no = 0; - comp->dc_tbl_no = 0; - comp->ac_tbl_no = 0; - } - if (colorspace == JCS_RGB) { - cinfo->comp_info[0].component_id = 'R'; - cinfo->comp_info[1].component_id = 'G'; - cinfo->comp_info[2].component_id = 'B'; - if (cinfo->master->xyb_mode) { - // Subsample blue channel. - cinfo->comp_info[0].h_samp_factor = cinfo->comp_info[0].v_samp_factor = 2; - cinfo->comp_info[1].h_samp_factor = cinfo->comp_info[1].v_samp_factor = 2; - cinfo->comp_info[2].h_samp_factor = cinfo->comp_info[2].v_samp_factor = 1; - // Use separate quantization tables for each component - cinfo->comp_info[1].quant_tbl_no = 1; - cinfo->comp_info[2].quant_tbl_no = 2; - } - } else if (colorspace == JCS_CMYK) { - cinfo->comp_info[0].component_id = 'C'; - cinfo->comp_info[1].component_id = 'M'; - cinfo->comp_info[2].component_id = 'Y'; - cinfo->comp_info[3].component_id = 'K'; - } else if (colorspace == JCS_YCbCr || colorspace == JCS_YCCK) { - // Use separate quantization and Huffman tables for luma and chroma - cinfo->comp_info[1].quant_tbl_no = 1; - cinfo->comp_info[2].quant_tbl_no = 1; - cinfo->comp_info[1].dc_tbl_no = cinfo->comp_info[1].ac_tbl_no = 1; - cinfo->comp_info[2].dc_tbl_no = cinfo->comp_info[2].ac_tbl_no = 1; - // Use chroma subsampling by default - cinfo->comp_info[0].h_samp_factor = cinfo->comp_info[0].v_samp_factor = 2; - if (colorspace == JCS_YCCK) { - cinfo->comp_info[3].h_samp_factor = cinfo->comp_info[3].v_samp_factor = 2; - } - } -} - -void jpegli_set_distance(j_compress_ptr cinfo, float distance, - boolean force_baseline) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); - float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; - jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/true); -} - -float jpegli_quality_to_distance(int quality) { - return (quality >= 100 ? 0.01f - : quality >= 30 ? 0.1f + (100 - quality) * 0.09f - : 53.0f / 3000.0f * quality * quality - - 23.0f / 20.0f * quality + 25.0f); -} - -void jpegli_set_psnr(j_compress_ptr cinfo, float psnr, float tolerance, - float min_distance, float max_distance) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->psnr_target = psnr; - cinfo->master->psnr_tolerance = tolerance; - cinfo->master->min_distance = min_distance; - cinfo->master->max_distance = max_distance; -} - -void jpegli_set_quality(j_compress_ptr cinfo, int quality, - boolean force_baseline) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); - float distance = jpegli_quality_to_distance(quality); - float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; - jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false); -} - -void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor, - boolean force_baseline) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); - float distance = jpegli::LinearQualityToDistance(scale_factor); - float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; - jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false); -} - -#if JPEG_LIB_VERSION >= 70 -void jpegli_default_qtables(j_compress_ptr cinfo, boolean force_baseline) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = force_baseline; - float distances[NUM_QUANT_TBLS]; - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - distances[i] = jpegli::LinearQualityToDistance(cinfo->q_scale_factor[i]); - } - jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false); -} -#endif - -int jpegli_quality_scaling(int quality) { - quality = std::min(100, std::max(1, quality)); - return quality < 50 ? 5000 / quality : 200 - 2 * quality; -} - -void jpegli_use_standard_quant_tables(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->use_std_tables = true; -} - -void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl, - const unsigned int* basic_table, int scale_factor, - boolean force_baseline) { - CheckState(cinfo, jpegli::kEncStart); - if (which_tbl < 0 || which_tbl > NUM_QUANT_TBLS) { - JPEGLI_ERROR("Invalid quant table index %d", which_tbl); - } - if (cinfo->quant_tbl_ptrs[which_tbl] == nullptr) { - cinfo->quant_tbl_ptrs[which_tbl] = - jpegli_alloc_quant_table(reinterpret_cast(cinfo)); - } - int max_qval = force_baseline ? 255 : 32767U; - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[which_tbl]; - for (int k = 0; k < DCTSIZE2; ++k) { - int qval = (basic_table[k] * scale_factor + 50) / 100; - qval = std::max(1, std::min(qval, max_qval)); - quant_table->quantval[k] = qval; - } - quant_table->sent_table = FALSE; -} - -void jpegli_enable_adaptive_quantization(j_compress_ptr cinfo, boolean value) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->master->use_adaptive_quantization = FROM_JXL_BOOL(value); -} - -void jpegli_simple_progression(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - jpegli_set_progressive_level(cinfo, 2); -} - -void jpegli_set_progressive_level(j_compress_ptr cinfo, int level) { - CheckState(cinfo, jpegli::kEncStart); - if (level < 0) { - JPEGLI_ERROR("Invalid progressive level %d", level); - } - cinfo->master->progressive_level = level; -} - -void jpegli_set_input_format(j_compress_ptr cinfo, JpegliDataType data_type, - JpegliEndianness endianness) { - CheckState(cinfo, jpegli::kEncStart); - switch (data_type) { - case JPEGLI_TYPE_UINT8: - case JPEGLI_TYPE_UINT16: - case JPEGLI_TYPE_FLOAT: - cinfo->master->data_type = data_type; - break; - default: - JPEGLI_ERROR("Unsupported data type %d", data_type); - } - switch (endianness) { - case JPEGLI_NATIVE_ENDIAN: - case JPEGLI_LITTLE_ENDIAN: - case JPEGLI_BIG_ENDIAN: - cinfo->master->endianness = endianness; - break; - default: - JPEGLI_ERROR("Unsupported endianness %d", endianness); - } -} - -#if JPEG_LIB_VERSION >= 70 -void jpegli_calc_jpeg_dimensions(j_compress_ptr cinfo) { - // Since input scaling is not supported, we just copy the image dimensions. - cinfo->jpeg_width = cinfo->image_width; - cinfo->jpeg_height = cinfo->image_height; -} -#endif - -void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo, - j_compress_ptr dstinfo) { - CheckState(dstinfo, jpegli::kEncStart); - // Image parameters. - dstinfo->image_width = srcinfo->image_width; - dstinfo->image_height = srcinfo->image_height; - dstinfo->input_components = srcinfo->num_components; - dstinfo->in_color_space = srcinfo->jpeg_color_space; - dstinfo->input_gamma = srcinfo->output_gamma; - // Compression parameters. - jpegli_set_defaults(dstinfo); - jpegli_set_colorspace(dstinfo, srcinfo->jpeg_color_space); - if (dstinfo->num_components != srcinfo->num_components) { - const auto& cinfo = dstinfo; - JPEGLI_ERROR("Mismatch between src colorspace and components"); - } - dstinfo->data_precision = srcinfo->data_precision; - dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; - dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; - dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; - dstinfo->density_unit = srcinfo->density_unit; - dstinfo->X_density = srcinfo->X_density; - dstinfo->Y_density = srcinfo->Y_density; - for (int c = 0; c < dstinfo->num_components; ++c) { - jpeg_component_info* srccomp = &srcinfo->comp_info[c]; - jpeg_component_info* dstcomp = &dstinfo->comp_info[c]; - dstcomp->component_id = srccomp->component_id; - dstcomp->h_samp_factor = srccomp->h_samp_factor; - dstcomp->v_samp_factor = srccomp->v_samp_factor; - dstcomp->quant_tbl_no = srccomp->quant_tbl_no; - } - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - if (!srcinfo->quant_tbl_ptrs[i]) continue; - if (dstinfo->quant_tbl_ptrs[i] == nullptr) { - dstinfo->quant_tbl_ptrs[i] = jpegli::Allocate(dstinfo, 1); - } - memcpy(dstinfo->quant_tbl_ptrs[i], srcinfo->quant_tbl_ptrs[i], - sizeof(JQUANT_TBL)); - dstinfo->quant_tbl_ptrs[i]->sent_table = FALSE; - } -} - -void jpegli_suppress_tables(j_compress_ptr cinfo, boolean suppress) { - jpegli::SetSentTableFlag(cinfo->quant_tbl_ptrs, NUM_QUANT_TBLS, suppress); - jpegli::SetSentTableFlag(cinfo->dc_huff_tbl_ptrs, NUM_HUFF_TBLS, suppress); - jpegli::SetSentTableFlag(cinfo->ac_huff_tbl_ptrs, NUM_HUFF_TBLS, suppress); -} - -// -// Compressor initialization -// - -void jpegli_start_compress(j_compress_ptr cinfo, boolean write_all_tables) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->global_state = jpegli::kEncHeader; - jpegli::InitCompress(cinfo, write_all_tables); - cinfo->next_scanline = 0; - cinfo->master->next_input_row = 0; -} - -void jpegli_write_coefficients(j_compress_ptr cinfo, - jvirt_barray_ptr* coef_arrays) { - CheckState(cinfo, jpegli::kEncStart); - cinfo->global_state = jpegli::kEncWriteCoeffs; - jpegli::InitCompress(cinfo, /*write_all_tables=*/TRUE); - cinfo->master->coeff_buffers = coef_arrays; - cinfo->next_scanline = cinfo->image_height; - cinfo->master->next_input_row = cinfo->image_height; -} - -void jpegli_write_tables(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncStart); - if (cinfo->dest == nullptr) { - JPEGLI_ERROR("Missing destination."); - } - jpeg_comp_master* m = cinfo->master; - (*cinfo->err->reset_error_mgr)(reinterpret_cast(cinfo)); - (*cinfo->dest->init_destination)(cinfo); - jpegli::WriteOutput(cinfo, {0xFF, 0xD8}); // SOI - jpegli::EncodeDQT(cinfo, /*write_all_tables=*/true); - jpegli::CopyHuffmanTables(cinfo); - jpegli::EncodeDHT(cinfo, 0, m->num_huffman_tables); - jpegli::WriteOutput(cinfo, {0xFF, 0xD9}); // EOI - (*cinfo->dest->term_destination)(cinfo); - jpegli_suppress_tables(cinfo, TRUE); -} - -// -// Marker writing -// - -void jpegli_write_m_header(j_compress_ptr cinfo, int marker, - unsigned int datalen) { - CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncWriteCoeffs); - if (datalen > jpegli::kMaxBytesInMarker) { - JPEGLI_ERROR("Invalid marker length %u", datalen); - } - if (marker != 0xfe && (marker < 0xe0 || marker > 0xef)) { - JPEGLI_ERROR( - "jpegli_write_m_header: Only APP and COM markers are supported."); - } - std::vector marker_data(4 + datalen); - marker_data[0] = 0xff; - marker_data[1] = marker; - marker_data[2] = (datalen + 2) >> 8; - marker_data[3] = (datalen + 2) & 0xff; - jpegli::WriteOutput(cinfo, marker_data.data(), 4); -} - -void jpegli_write_m_byte(j_compress_ptr cinfo, int val) { - uint8_t data = val; - jpegli::WriteOutput(cinfo, &data, 1); -} - -void jpegli_write_marker(j_compress_ptr cinfo, int marker, - const JOCTET* dataptr, unsigned int datalen) { - jpegli_write_m_header(cinfo, marker, datalen); - jpegli::WriteOutput(cinfo, dataptr, datalen); -} - -void jpegli_write_icc_profile(j_compress_ptr cinfo, const JOCTET* icc_data_ptr, - unsigned int icc_data_len) { - constexpr size_t kMaxIccBytesInMarker = - jpegli::kMaxBytesInMarker - sizeof jpegli::kICCSignature - 2; - const int num_markers = - static_cast(jpegli::DivCeil(icc_data_len, kMaxIccBytesInMarker)); - size_t begin = 0; - for (int current_marker = 0; current_marker < num_markers; ++current_marker) { - const size_t length = std::min(kMaxIccBytesInMarker, icc_data_len - begin); - jpegli_write_m_header( - cinfo, jpegli::kICCMarker, - static_cast(length + sizeof jpegli::kICCSignature + 2)); - for (const unsigned char c : jpegli::kICCSignature) { - jpegli_write_m_byte(cinfo, c); - } - jpegli_write_m_byte(cinfo, current_marker + 1); - jpegli_write_m_byte(cinfo, num_markers); - for (size_t i = 0; i < length; ++i) { - jpegli_write_m_byte(cinfo, icc_data_ptr[begin]); - ++begin; - } - } -} - -// -// Input streaming -// - -JDIMENSION jpegli_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION num_lines) { - CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncReadImage); - if (cinfo->raw_data_in) { - JPEGLI_ERROR("jpegli_write_raw_data() must be called for raw data mode."); - } - jpegli::ProgressMonitorInputPass(cinfo); - if (cinfo->global_state == jpegli::kEncHeader && - jpegli::IsStreamingSupported(cinfo) && !cinfo->optimize_coding) { - jpegli::WriteFrameHeader(cinfo); - jpegli::WriteScanHeader(cinfo, 0); - } - cinfo->global_state = jpegli::kEncReadImage; - jpeg_comp_master* m = cinfo->master; - if (num_lines + cinfo->next_scanline > cinfo->image_height) { - num_lines = cinfo->image_height - cinfo->next_scanline; - } - JDIMENSION prev_scanline = cinfo->next_scanline; - size_t input_lag = (std::min(cinfo->image_height, m->next_input_row) - - cinfo->next_scanline); - if (input_lag > num_lines) { - JPEGLI_ERROR("Need at least %u lines to continue", input_lag); - } - if (input_lag > 0) { - if (!jpegli::EmptyBitWriterBuffer(&m->bw)) { - return 0; - } - cinfo->next_scanline += input_lag; - } - float* rows[jpegli::kMaxComponents]; - for (size_t i = input_lag; i < num_lines; ++i) { - jpegli::ReadInputRow(cinfo, scanlines[i], rows); - (*m->color_transform)(rows, cinfo->image_width); - jpegli::PadInputBuffer(cinfo, rows); - jpegli::ProcessiMCURows(cinfo); - if (!jpegli::EmptyBitWriterBuffer(&m->bw)) { - break; - } - ++cinfo->next_scanline; - } - return cinfo->next_scanline - prev_scanline; -} - -JDIMENSION jpegli_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION num_lines) { - CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncReadImage); - if (!cinfo->raw_data_in) { - JPEGLI_ERROR("jpegli_write_raw_data(): raw data mode was not set"); - } - jpegli::ProgressMonitorInputPass(cinfo); - if (cinfo->global_state == jpegli::kEncHeader && - jpegli::IsStreamingSupported(cinfo) && !cinfo->optimize_coding) { - jpegli::WriteFrameHeader(cinfo); - jpegli::WriteScanHeader(cinfo, 0); - } - cinfo->global_state = jpegli::kEncReadImage; - jpeg_comp_master* m = cinfo->master; - if (cinfo->next_scanline >= cinfo->image_height) { - return 0; - } - size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; - if (num_lines < iMCU_height) { - JPEGLI_ERROR("Missing input lines, minimum is %u", iMCU_height); - } - if (cinfo->next_scanline < m->next_input_row) { - JPEGLI_CHECK(m->next_input_row - cinfo->next_scanline == iMCU_height); - if (!jpegli::EmptyBitWriterBuffer(&m->bw)) { - return 0; - } - cinfo->next_scanline = m->next_input_row; - return iMCU_height; - } - size_t iMCU_y = m->next_input_row / iMCU_height; - float* rows[jpegli::kMaxComponents]; - for (int c = 0; c < cinfo->num_components; ++c) { - JSAMPARRAY plane = data[c]; - jpeg_component_info* comp = &cinfo->comp_info[c]; - size_t xsize = comp->width_in_blocks * DCTSIZE; - size_t ysize = comp->v_samp_factor * DCTSIZE; - size_t y0 = iMCU_y * ysize; - auto& buffer = m->input_buffer[c]; - for (size_t i = 0; i < ysize; ++i) { - rows[0] = buffer.Row(y0 + i); - if (plane[i] == nullptr) { - memset(rows[0], 0, xsize * sizeof(rows[0][0])); - } else { - (*m->input_method)(plane[i], xsize, rows); - } - // We need a border of 1 repeated pixel for adaptive quant field. - buffer.PadRow(y0 + i, xsize, /*border=*/1); - } - } - m->next_input_row += iMCU_height; - jpegli::ProcessiMCURows(cinfo); - if (!jpegli::EmptyBitWriterBuffer(&m->bw)) { - return 0; - } - cinfo->next_scanline += iMCU_height; - return iMCU_height; -} - -// -// Non-streaming part -// - -void jpegli_finish_compress(j_compress_ptr cinfo) { - CheckState(cinfo, jpegli::kEncReadImage, jpegli::kEncWriteCoeffs); - jpeg_comp_master* m = cinfo->master; - if (cinfo->next_scanline < cinfo->image_height) { - JPEGLI_ERROR("Incomplete image, expected %d rows, got %d", - cinfo->image_height, cinfo->next_scanline); - } - - if (cinfo->global_state == jpegli::kEncWriteCoeffs) { - // Zig-zag shuffle all the blocks. For non-transcoding case it was already - // done in EncodeiMCURow(). - jpegli::ZigZagShuffleBlocks(cinfo); - } - - if (m->psnr_target > 0) { - jpegli::QuantizetoPSNR(cinfo); - } - - const bool tokens_done = jpegli::IsStreamingSupported(cinfo); - const bool bitstream_done = - tokens_done && !FROM_JXL_BOOL(cinfo->optimize_coding); - - if (!tokens_done) { - jpegli::TokenizeJpeg(cinfo); - } - - if (cinfo->optimize_coding || cinfo->progressive_mode) { - jpegli::OptimizeHuffmanCodes(cinfo); - jpegli::InitEntropyCoder(cinfo); - } - - if (!bitstream_done) { - jpegli::WriteFrameHeader(cinfo); - for (int i = 0; i < cinfo->num_scans; ++i) { - jpegli::WriteScanHeader(cinfo, i); - jpegli::WriteScanData(cinfo, i); - } - } else { - JumpToByteBoundary(&m->bw); - if (!EmptyBitWriterBuffer(&m->bw)) { - JPEGLI_ERROR("Output suspension is not supported in finish_compress"); - } - } - - jpegli::WriteOutput(cinfo, {0xFF, 0xD9}); // EOI - (*cinfo->dest->term_destination)(cinfo); - - // Release memory and reset global state. - jpegli_abort_compress(cinfo); -} - -void jpegli_abort_compress(j_compress_ptr cinfo) { - jpegli_abort(reinterpret_cast(cinfo)); -} - -void jpegli_destroy_compress(j_compress_ptr cinfo) { - jpegli_destroy(reinterpret_cast(cinfo)); -} diff --git a/third_party/jpeg-xl/lib/jpegli/encode.h b/third_party/jpeg-xl/lib/jpegli/encode.h deleted file mode 100644 index 54ff9d1f9eec6..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file contains the C API of the encoder part of the libjpegli library, -// which is based on the C API of libjpeg, with the function names changed from -// jpeg_* to jpegli_*, while compressor object definitions are included directly -// from jpeglib.h -// -// Applications can use the libjpegli library in one of the following ways: -// -// (1) Include jpegli/encode.h and/or jpegli/decode.h, update the function -// names of the API and link against libjpegli. -// -// (2) Leave the application code unchanged, but replace the libjpeg.so library -// with the one built by this project that is API- and ABI-compatible with -// libjpeg-turbo's version of libjpeg.so. - -#ifndef LIB_JPEGLI_ENCODE_H_ -#define LIB_JPEGLI_ENCODE_H_ - -#include "lib/jpegli/common.h" -#include "lib/jpegli/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define jpegli_create_compress(cinfo) \ - jpegli_CreateCompress((cinfo), JPEG_LIB_VERSION, \ - (size_t)sizeof(struct jpeg_compress_struct)) -void jpegli_CreateCompress(j_compress_ptr cinfo, int version, - size_t structsize); - -void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile); - -void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer, - unsigned long* outsize /* NOLINT */); - -void jpegli_set_defaults(j_compress_ptr cinfo); - -void jpegli_default_colorspace(j_compress_ptr cinfo); - -void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace); - -void jpegli_set_quality(j_compress_ptr cinfo, int quality, - boolean force_baseline); - -void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor, - boolean force_baseline); - -#if JPEG_LIB_VERSION >= 70 -void jpegli_default_qtables(j_compress_ptr cinfo, boolean force_baseline); -#endif - -int jpegli_quality_scaling(int quality); - -void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl, - const unsigned int* basic_table, int scale_factor, - boolean force_baseline); - -void jpegli_simple_progression(j_compress_ptr cinfo); - -void jpegli_suppress_tables(j_compress_ptr cinfo, boolean suppress); - -#if JPEG_LIB_VERSION >= 70 -void jpegli_calc_jpeg_dimensions(j_compress_ptr cinfo); -#endif - -void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo, - j_compress_ptr dstinfo); - -void jpegli_write_m_header(j_compress_ptr cinfo, int marker, - unsigned int datalen); - -void jpegli_write_m_byte(j_compress_ptr cinfo, int val); - -void jpegli_write_marker(j_compress_ptr cinfo, int marker, - const JOCTET* dataptr, unsigned int datalen); - -void jpegli_write_icc_profile(j_compress_ptr cinfo, const JOCTET* icc_data_ptr, - unsigned int icc_data_len); - -void jpegli_start_compress(j_compress_ptr cinfo, boolean write_all_tables); - -void jpegli_write_tables(j_compress_ptr cinfo); - -JDIMENSION jpegli_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION num_lines); - -JDIMENSION jpegli_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION num_lines); - -void jpegli_write_coefficients(j_compress_ptr cinfo, - jvirt_barray_ptr* coef_arrays); - -void jpegli_finish_compress(j_compress_ptr cinfo); - -void jpegli_abort_compress(j_compress_ptr cinfo); - -void jpegli_destroy_compress(j_compress_ptr cinfo); - -// -// New API functions that are not available in libjpeg -// -// NOTE: This part of the API is still experimental and will probably change in -// the future. -// - -// Sets the butteraugli target distance for the compressor. This may override -// the default quantization table indexes based on jpeg colorspace, therefore -// it must be called after jpegli_set_defaults() or after the last -// jpegli_set_colorspace() or jpegli_default_colorspace() calls. -void jpegli_set_distance(j_compress_ptr cinfo, float distance, - boolean force_baseline); - -// Returns the butteraugli target distance for the given quality parameter. -float jpegli_quality_to_distance(int quality); - -// Enables distance parameter search to meet the given psnr target. -void jpegli_set_psnr(j_compress_ptr cinfo, float psnr, float tolerance, - float min_distance, float max_distance); - -// Changes the default behaviour of the encoder in the selection of quantization -// matrices and chroma subsampling. Must be called before jpegli_set_defaults() -// because some default setting depend on the XYB mode. -void jpegli_set_xyb_mode(j_compress_ptr cinfo); - -// Signals to the encoder that the pixel data that will be provided later -// through jpegli_write_scanlines() has this transfer function. This must be -// called before jpegli_set_defaults() because it changes the default -// quantization tables. -void jpegli_set_cicp_transfer_function(j_compress_ptr cinfo, int code); - -void jpegli_set_input_format(j_compress_ptr cinfo, JpegliDataType data_type, - JpegliEndianness endianness); - -// Sets whether or not the encoder uses adaptive quantization for creating more -// zero coefficients based on the local properties of the image. -// Enabled by default. -void jpegli_enable_adaptive_quantization(j_compress_ptr cinfo, boolean value); - -// Sets the default progression parameters, where level 0 is sequential, and -// greater level value means more progression steps. Default is 2. -void jpegli_set_progressive_level(j_compress_ptr cinfo, int level); - -// If this function is called before starting compression, the quality and -// linear quality parameters will be used to scale the standard quantization -// tables from Annex K of the JPEG standard. By default jpegli uses a different -// set of quantization tables and used different scaling parameters for DC and -// AC coefficients. Must be called before jpegli_set_defaults(). -void jpegli_use_standard_quant_tables(j_compress_ptr cinfo); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // LIB_JPEGLI_ENCODE_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc deleted file mode 100644 index c8a8e21f677aa..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc +++ /dev/null @@ -1,873 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/encode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" -#include "lib/jpegli/types.h" - -namespace jpegli { -namespace { - -struct TestConfig { - TestImage input; - CompressParams jparams; - JpegIOMode input_mode = PIXELS; - double max_bpp; - double max_dist; -}; - -class EncodeAPITestParam : public ::testing::TestWithParam {}; - -void GenerateInput(JpegIOMode input_mode, const CompressParams& jparams, - TestImage* input) { - GeneratePixels(input); - if (input_mode == RAW_DATA) { - GenerateRawData(jparams, input); - } else if (input_mode == COEFFICIENTS) { - GenerateCoeffs(jparams, input); - } -} - -TEST_P(EncodeAPITestParam, TestAPI) { - TestConfig config = GetParam(); - GenerateInput(config.input_mode, config.jparams, &config.input); - std::vector compressed; - ASSERT_TRUE(EncodeWithJpegli(config.input, config.jparams, &compressed)); - if (config.jparams.icc.empty()) { - double bpp = - compressed.size() * 8.0 / (config.input.xsize * config.input.ysize); - printf("bpp: %f\n", bpp); - EXPECT_LT(bpp, config.max_bpp); - } - DecompressParams dparams; - dparams.output_mode = - config.input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS; - if (config.jparams.set_jpeg_colorspace && - config.jparams.jpeg_color_space == JCS_GRAYSCALE) { - ConvertToGrayscale(&config.input); - } else { - dparams.set_out_color_space = true; - dparams.out_color_space = config.input.color_space; - } - TestImage output; - DecodeWithLibjpeg(config.jparams, dparams, compressed, &output); - VerifyOutputImage(config.input, output, config.max_dist); -} - -TEST(EncodeAPITest, ReuseCinfoSameImageTwice) { - TestImage input; - input.xsize = 129; - input.ysize = 73; - CompressParams jparams; - GenerateInput(PIXELS, jparams, &input); - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - std::vector compressed0; - std::vector compressed1; - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - EncodeWithJpegli(input, jparams, &cinfo); - compressed0.assign(buffer, buffer + buffer_size); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - EncodeWithJpegli(input, jparams, &cinfo); - compressed1.assign(buffer, buffer + buffer_size); - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); - ASSERT_EQ(compressed0.size(), compressed1.size()); - EXPECT_EQ(0, - memcmp(compressed0.data(), compressed1.data(), compressed0.size())); -} - -std::vector GenerateBasicConfigs() { - std::vector all_configs; - for (int samp : {1, 2}) { - for (int progr : {0, 2}) { - for (int optimize : {0, 1}) { - if (progr && optimize) continue; - TestConfig config; - config.input.xsize = 257 + samp * 37; - config.input.ysize = 265 + optimize * 17; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - config.jparams.optimize_coding = optimize; - config.max_dist = 2.4f; - GeneratePixels(&config.input); - all_configs.push_back(config); - } - } - } - return all_configs; -} - -TEST(EncodeAPITest, ReuseCinfoSameMemOutput) { - std::vector all_configs = GenerateBasicConfigs(); - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - for (const TestConfig& config : all_configs) { - EncodeWithJpegli(config.input, config.jparams, &cinfo); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - } - size_t pos = 0; - for (auto& config : all_configs) { - TestImage output; - pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, - buffer + pos, buffer_size - pos, &output); - VerifyOutputImage(config.input, output, config.max_dist); - } - if (buffer) free(buffer); -} - -TEST(EncodeAPITest, ReuseCinfoSameStdOutput) { - std::vector all_configs = GenerateBasicConfigs(); - FILE* tmpf = tmpfile(); - ASSERT_TRUE(tmpf); - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_stdio_dest(&cinfo, tmpf); - for (const TestConfig& config : all_configs) { - EncodeWithJpegli(config.input, config.jparams, &cinfo); - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - } - size_t total_size = ftell(tmpf); - fseek(tmpf, 0, SEEK_SET); - std::vector compressed(total_size); - ASSERT_TRUE(total_size == fread(compressed.data(), 1, total_size, tmpf)); - fclose(tmpf); - size_t pos = 0; - for (auto& config : all_configs) { - TestImage output; - pos += - DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, - &compressed[pos], compressed.size() - pos, &output); - VerifyOutputImage(config.input, output, config.max_dist); - } -} - -TEST(EncodeAPITest, ReuseCinfoChangeParams) { - TestImage input; - TestImage output; - CompressParams jparams; - DecompressParams dparams; - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - std::vector compressed; - jpeg_compress_struct cinfo; - const auto max_rms = [](int q, int hs, int vs) { - if (hs == 1 && vs == 1) return q == 90 ? 2.2 : 0.6; - if (hs == 2 && vs == 2) return q == 90 ? 2.8 : 1.2; - return q == 90 ? 2.4 : 1.0; - }; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - input.xsize = 129; - input.ysize = 73; - dparams.set_out_color_space = true; - for (JpegIOMode input_mode : {PIXELS, RAW_DATA, PIXELS, COEFFICIENTS}) { - for (int h_samp : {2, 1}) { - for (int v_samp : {2, 1}) { - for (int progr : {0, 2}) { - for (int quality : {90, 100}) { - input.Clear(); - input.color_space = - (input_mode == RAW_DATA ? JCS_YCbCr : JCS_RGB); - jparams.quality = quality; - jparams.h_sampling = {h_samp, 1, 1}; - jparams.v_sampling = {v_samp, 1, 1}; - jparams.progressive_mode = progr; - printf( - "Generating input with quality %d chroma subsampling %dx%d " - "input mode %d progressive_mode %d\n", - quality, h_samp, v_samp, input_mode, progr); - GenerateInput(input_mode, jparams, &input); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - if (input_mode != COEFFICIENTS) { - cinfo.image_width = input.xsize; - cinfo.image_height = input.ysize; - cinfo.input_components = input.components; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - jpegli_abort_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - } - EncodeWithJpegli(input, jparams, &cinfo); - compressed.resize(buffer_size); - std::copy_n(buffer, buffer_size, compressed.data()); - dparams.output_mode = - input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS; - dparams.out_color_space = input.color_space; - output.Clear(); - DecodeWithLibjpeg(jparams, dparams, compressed, &output); - VerifyOutputImage(input, output, - max_rms(quality, h_samp, v_samp)); - } - } - } - } - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncodeAPITest, AbbreviatedStreams) { - uint8_t* table_stream = nullptr; - unsigned long table_stream_size = 0; // NOLINT - uint8_t* data_stream = nullptr; - unsigned long data_stream_size = 0; // NOLINT - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &table_stream, &table_stream_size); - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - jpegli_write_tables(&cinfo); - jpegli_mem_dest(&cinfo, &data_stream, &data_stream_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.optimize_coding = FALSE; - jpegli_set_progressive_level(&cinfo, 0); - jpegli_start_compress(&cinfo, FALSE); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_TRUE(try_catch_block()); - EXPECT_LT(data_stream_size, 50); - jpegli_destroy_compress(&cinfo); - } - TestImage output; - DecodeWithLibjpeg(CompressParams(), DecompressParams(), table_stream, - table_stream_size, data_stream, data_stream_size, &output); - EXPECT_EQ(1, output.xsize); - EXPECT_EQ(1, output.ysize); - EXPECT_EQ(3, output.components); - EXPECT_EQ(0, output.pixels[0]); - EXPECT_EQ(0, output.pixels[1]); - EXPECT_EQ(0, output.pixels[2]); - if (table_stream) free(table_stream); - if (data_stream) free(data_stream); -} - -void CopyQuantTables(j_compress_ptr cinfo, uint16_t* quant_tables) { - for (int c = 0; c < cinfo->num_components; ++c) { - int quant_idx = cinfo->comp_info[c].quant_tbl_no; - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_idx]; - for (int k = 0; k < DCTSIZE2; ++k) { - quant_tables[c * DCTSIZE2 + k] = quant_table->quantval[k]; - } - } -} - -TEST(EncodeAPITest, QualitySettings) { - // Test that jpegli_set_quality, jpegli_set_linear_quality and - // jpegli_quality_scaling are consistent with each other. - uint16_t quant_tables0[3 * DCTSIZE2]; - uint16_t quant_tables1[3 * DCTSIZE2]; - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - for (boolean baseline : {FALSE, TRUE}) { - for (int q = 1; q <= 100; ++q) { - jpegli_set_quality(&cinfo, q, baseline); - CopyQuantTables(&cinfo, quant_tables0); - jpegli_set_linear_quality(&cinfo, jpegli_quality_scaling(q), baseline); - CopyQuantTables(&cinfo, quant_tables1); - EXPECT_EQ(0, - memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0))); -#if JPEG_LIB_VERSION >= 70 - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - cinfo.q_scale_factor[i] = jpegli_quality_scaling(q); - } - jpegli_default_qtables(&cinfo, baseline); - CopyQuantTables(&cinfo, quant_tables1); - EXPECT_EQ(0, - memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0))); -#endif - } - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - // Test jpegli_quality_scaling for some specific values . - EXPECT_EQ(5000, jpegli_quality_scaling(-1)); - EXPECT_EQ(5000, jpegli_quality_scaling(0)); - EXPECT_EQ(5000, jpegli_quality_scaling(1)); - EXPECT_EQ(100, jpegli_quality_scaling(50)); - EXPECT_EQ(50, jpegli_quality_scaling(75)); - EXPECT_EQ(20, jpegli_quality_scaling(90)); - EXPECT_EQ(0, jpegli_quality_scaling(100)); - EXPECT_EQ(0, jpegli_quality_scaling(101)); -} - -std::vector GenerateTests() { - std::vector all_tests; - for (int h_samp : {1, 2}) { - for (int v_samp : {1, 2}) { - for (int progr : {0, 2}) { - for (int optimize : {0, 1}) { - if (progr && optimize) continue; - TestConfig config; - config.jparams.h_sampling = {h_samp, 1, 1}; - config.jparams.v_sampling = {v_samp, 1, 1}; - config.jparams.progressive_mode = progr; - if (!progr) { - config.jparams.optimize_coding = optimize; - } - const float kMaxBpp[4] = {1.55, 1.4, 1.4, 1.32}; - const float kMaxDist[4] = {1.95, 2.2, 2.2, 2.0}; - const int idx = v_samp * 2 + h_samp - 3; - config.max_bpp = - kMaxBpp[idx] * (optimize ? 0.97 : 1.0) * (progr ? 0.97 : 1.0); - config.max_dist = kMaxDist[idx]; - all_tests.push_back(config); - } - } - } - } - { - TestConfig config; - config.jparams.quality = 100; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - config.max_bpp = 6.6; - config.max_dist = 0.6; - all_tests.push_back(config); - } - { - TestConfig config; - config.jparams.quality = 80; - config.max_bpp = 1.05; - config.max_dist = 2.7; - all_tests.push_back(config); - } - for (int samp : {1, 2}) { - for (int progr : {0, 2}) { - for (int optimize : {0, 1}) { - if (progr && optimize) continue; - TestConfig config; - config.input.xsize = 257; - config.input.ysize = 265; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - if (!progr) { - config.jparams.optimize_coding = optimize; - } - config.jparams.use_adaptive_quantization = false; - config.max_bpp = 2.05f; - config.max_dist = 2.3f; - all_tests.push_back(config); - } - } - } - for (int h0_samp : {1, 2, 4}) { - for (int v0_samp : {1, 2, 4}) { - for (int h2_samp : {1, 2, 4}) { - for (int v2_samp : {1, 2, 4}) { - TestConfig config; - config.input.xsize = 137; - config.input.ysize = 75; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, h2_samp}; - config.jparams.v_sampling = {v0_samp, 1, v2_samp}; - config.max_bpp = 2.5; - config.max_dist = 12.0; - all_tests.push_back(config); - } - } - } - } - for (int h0_samp : {1, 3}) { - for (int v0_samp : {1, 3}) { - for (int h2_samp : {1, 3}) { - for (int v2_samp : {1, 3}) { - TestConfig config; - config.input.xsize = 205; - config.input.ysize = 99; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, h2_samp}; - config.jparams.v_sampling = {v0_samp, 1, v2_samp}; - config.max_bpp = 2.5; - config.max_dist = 10.0; - all_tests.push_back(config); - } - } - } - } - for (int h0_samp : {1, 2, 3, 4}) { - for (int v0_samp : {1, 2, 3, 4}) { - TestConfig config; - config.input.xsize = 217; - config.input.ysize = 129; - config.jparams.progressive_mode = 2; - config.jparams.h_sampling = {h0_samp, 1, 1}; - config.jparams.v_sampling = {v0_samp, 1, 1}; - config.max_bpp = 2.0; - config.max_dist = 5.5; - all_tests.push_back(config); - } - } - for (int p = 0; p < 3 + NumTestScanScripts(); ++p) { - for (int samp : {1, 2}) { - for (int quality : {100, 90, 1}) { - for (int r : {0, 1024, 1}) { - for (int optimize : {0, 1}) { - bool progressive = p == 1 || p == 2 || p > 4; - if (progressive && !optimize) continue; - TestConfig config; - config.input.xsize = 273; - config.input.ysize = 265; - config.jparams.progressive_mode = p; - if (!progressive) { - config.jparams.optimize_coding = optimize; - } - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.quality = quality; - config.jparams.restart_interval = r; - config.max_bpp = quality == 100 ? 8.0 : 1.9; - if (r == 1) { - config.max_bpp += 10.0; - } - config.max_dist = quality == 1 ? 20.0 : 2.1; - all_tests.push_back(config); - } - } - } - } - } - { - TestConfig config; - config.jparams.simple_progression = true; - config.max_bpp = 1.48; - config.max_dist = 2.0; - all_tests.push_back(config); - } - { - TestConfig config; - config.input_mode = COEFFICIENTS; - config.jparams.h_sampling = {2, 1, 1}; - config.jparams.v_sampling = {2, 1, 1}; - config.jparams.progressive_mode = 0; - config.jparams.optimize_coding = 0; - config.max_bpp = 16; - config.max_dist = 0.0; - all_tests.push_back(config); - } - { - TestConfig config; - config.jparams.xyb_mode = true; - config.jparams.progressive_mode = 2; - config.max_bpp = 1.5; - config.max_dist = 3.5; - all_tests.push_back(config); - } - { - TestConfig config; - config.jparams.libjpeg_mode = true; - config.max_bpp = 2.1; - config.max_dist = 1.7; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - all_tests.push_back(config); - } - - for (J_COLOR_SPACE in_color_space : - {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE, JCS_EXT_RGB, JCS_EXT_BGR, - JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ARGB, JCS_EXT_ABGR}) { - for (J_COLOR_SPACE jpeg_color_space : {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE}) { - if (jpeg_color_space == JCS_RGB && in_color_space >= JCS_YCbCr) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.input.color_space = in_color_space; - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - config.max_bpp = jpeg_color_space == JCS_RGB ? 4.5 : 1.85; - config.max_dist = jpeg_color_space == JCS_RGB ? 1.4 : 2.05; - all_tests.push_back(config); - } - } - for (J_COLOR_SPACE in_color_space : {JCS_CMYK, JCS_YCCK}) { - for (J_COLOR_SPACE jpeg_color_space : {JCS_CMYK, JCS_YCCK}) { - if (jpeg_color_space == JCS_CMYK && in_color_space == JCS_YCCK) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.input.color_space = in_color_space; - if (in_color_space != jpeg_color_space) { - config.jparams.set_jpeg_colorspace = true; - config.jparams.jpeg_color_space = jpeg_color_space; - } - config.jparams.h_sampling = {1, 1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1, 1}; - config.max_bpp = jpeg_color_space == JCS_CMYK ? 4.0 : 3.6; - config.max_dist = jpeg_color_space == JCS_CMYK ? 1.2 : 1.5; - all_tests.push_back(config); - } - } - { - TestConfig config; - config.input.color_space = JCS_YCbCr; - config.max_bpp = 1.6; - config.max_dist = 1.35; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - all_tests.push_back(config); - } - for (bool xyb : {false, true}) { - TestConfig config; - config.input.color_space = JCS_GRAYSCALE; - config.jparams.xyb_mode = xyb; - config.max_bpp = 1.35; - config.max_dist = 1.4; - all_tests.push_back(config); - } - for (int channels = 1; channels <= 4; ++channels) { - TestConfig config; - config.input.color_space = JCS_UNKNOWN; - config.input.components = channels; - config.max_bpp = 1.35 * channels; - config.max_dist = 1.4; - all_tests.push_back(config); - } - for (size_t r : {1, 3, 17, 1024}) { - for (int progr : {0, 2}) { - TestConfig config; - config.jparams.restart_interval = r; - config.jparams.progressive_mode = progr; - config.max_bpp = 1.58 + 5.5 / r; - config.max_dist = 2.2; - all_tests.push_back(config); - } - } - for (size_t rr : {1, 3, 8, 100}) { - TestConfig config; - config.jparams.restart_in_rows = rr; - config.max_bpp = 1.6; - config.max_dist = 2.2; - all_tests.push_back(config); - } - for (int type : {0, 1, 10, 100, 10000}) { - for (int scale : {1, 50, 100, 200, 500}) { - for (bool add_raw : {false, true}) { - for (bool baseline : {true, false}) { - if (!baseline && (add_raw || type * scale < 25500)) continue; - TestConfig config; - config.input.xsize = 64; - config.input.ysize = 64; - CustomQuantTable table; - table.table_type = type; - table.scale_factor = scale; - table.force_baseline = baseline; - table.add_raw = add_raw; - table.Generate(); - config.jparams.optimize_coding = 1; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - config.jparams.quant_tables.push_back(table); - config.jparams.quant_indexes = {0, 0, 0}; - float q = (type == 0 ? 16 : type) * scale * 0.01f; - if (baseline && !add_raw) q = std::max(1.0f, std::min(255.0f, q)); - config.max_bpp = 1.5f + 25.0f / q; - config.max_dist = 0.6f + 0.25f * q; - all_tests.push_back(config); - } - } - } - } - for (int qidx = 0; qidx < 8; ++qidx) { - if (qidx == 3) continue; - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - config.max_bpp = 2.25; - config.max_dist = 2.8; - all_tests.push_back(config); - } - for (int qidx = 0; qidx < 8; ++qidx) { - for (int slot_idx = 0; slot_idx < 2; ++slot_idx) { - if (qidx == 0 && slot_idx == 0) continue; - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - CustomQuantTable table; - table.slot_idx = slot_idx; - table.Generate(); - config.jparams.quant_tables.push_back(table); - config.max_bpp = 2.3; - config.max_dist = 2.9; - all_tests.push_back(config); - } - } - for (int qidx = 0; qidx < 8; ++qidx) { - for (bool xyb : {false, true}) { - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.xyb_mode = xyb; - config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, - (qidx >> 0) & 1}; - if (!xyb) { - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - } - { - CustomQuantTable table; - table.slot_idx = 0; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 1; - table.table_type = 20; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - config.max_bpp = 2.0; - config.max_dist = 3.85; - all_tests.push_back(config); - } - } - for (bool xyb : {false, true}) { - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.jparams.xyb_mode = xyb; - config.jparams.quant_indexes = {0, 1, 2}; - if (!xyb) { - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - } - { - CustomQuantTable table; - table.slot_idx = 0; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 1; - table.table_type = 20; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - { - CustomQuantTable table; - table.slot_idx = 2; - table.table_type = 30; - table.Generate(); - config.jparams.quant_tables.push_back(table); - } - config.max_bpp = 1.5; - config.max_dist = 3.75; - all_tests.push_back(config); - } - { - TestConfig config; - config.jparams.comp_ids = {7, 17, 177}; - config.input.xsize = config.input.ysize = 128; - config.max_bpp = 2.25; - config.max_dist = 2.4; - all_tests.push_back(config); - } - for (int override_JFIF : {-1, 0, 1}) { - for (int override_Adobe : {-1, 0, 1}) { - if (override_JFIF == -1 && override_Adobe == -1) continue; - TestConfig config; - config.input.xsize = config.input.ysize = 128; - config.jparams.override_JFIF = override_JFIF; - config.jparams.override_Adobe = override_Adobe; - config.max_bpp = 2.25; - config.max_dist = 2.4; - all_tests.push_back(config); - } - } - { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.max_bpp = 1.85; - config.max_dist = 2.05; - config.jparams.add_marker = true; - all_tests.push_back(config); - } - for (size_t icc_size : {728, 70000, 1000000}) { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.max_dist = 2.05; - config.jparams.icc.resize(icc_size); - for (size_t i = 0; i < icc_size; ++i) { - config.jparams.icc[i] = (i * 17) & 0xff; - } - all_tests.push_back(config); - } - for (JpegIOMode input_mode : {PIXELS, RAW_DATA, COEFFICIENTS}) { - TestConfig config; - config.input.xsize = config.input.ysize = 256; - config.input_mode = input_mode; - if (input_mode == RAW_DATA) { - config.input.color_space = JCS_YCbCr; - } - config.jparams.progressive_mode = 0; - config.jparams.optimize_coding = 0; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {1, 1, 1}; - config.max_bpp = 1.85; - config.max_dist = 2.05; - if (input_mode == COEFFICIENTS) { - config.max_bpp = 3.5; - config.max_dist = 0.0; - } - all_tests.push_back(config); - config.jparams.use_flat_dc_luma_code = true; - all_tests.push_back(config); - } - for (int xsize : {640, 641, 648, 649}) { - for (int ysize : {640, 641, 648, 649}) { - for (int h_sampling : {1, 2}) { - for (int v_sampling : {1, 2}) { - if (h_sampling == 1 && v_sampling == 1) continue; - for (int progr : {0, 2}) { - TestConfig config; - config.input.xsize = xsize; - config.input.ysize = ysize; - config.input.color_space = JCS_YCbCr; - config.jparams.h_sampling = {h_sampling, 1, 1}; - config.jparams.v_sampling = {v_sampling, 1, 1}; - config.jparams.progressive_mode = progr; - config.input_mode = RAW_DATA; - config.max_bpp = 1.75; - config.max_dist = 2.0; - all_tests.push_back(config); - config.input_mode = COEFFICIENTS; - if (xsize & 1) { - config.jparams.add_marker = true; - } - config.max_bpp = 24.0; - all_tests.push_back(config); - } - } - } - } - } - for (JpegliDataType data_type : {JPEGLI_TYPE_UINT16, JPEGLI_TYPE_FLOAT}) { - for (JpegliEndianness endianness : - {JPEGLI_LITTLE_ENDIAN, JPEGLI_BIG_ENDIAN, JPEGLI_NATIVE_ENDIAN}) { - J_COLOR_SPACE colorspace[4] = {JCS_GRAYSCALE, JCS_UNKNOWN, JCS_RGB, - JCS_CMYK}; - float max_bpp[4] = {1.32, 2.7, 1.6, 4.0}; - for (int channels = 1; channels <= 4; ++channels) { - TestConfig config; - config.input.data_type = data_type; - config.input.endianness = endianness; - config.input.components = channels; - config.input.color_space = colorspace[channels - 1]; - config.max_bpp = max_bpp[channels - 1]; - config.max_dist = 2.2; - all_tests.push_back(config); - } - } - } - for (int smoothing : {1, 5, 50, 100}) { - for (int h_sampling : {1, 2}) { - for (int v_sampling : {1, 2}) { - TestConfig config; - config.input.xsize = 257; - config.input.ysize = 265; - config.jparams.smoothing_factor = smoothing; - config.jparams.h_sampling = {h_sampling, 1, 1}; - config.jparams.v_sampling = {v_sampling, 1, 1}; - config.max_bpp = 1.85; - config.max_dist = 3.05f; - all_tests.push_back(config); - } - } - } - return all_tests; -}; - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - os << c.input; - os << c.jparams; - if (c.input_mode == RAW_DATA) { - os << "RawDataIn"; - } else if (c.input_mode == COEFFICIENTS) { - os << "WriteCoeffs"; - } - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(EncodeAPITest, EncodeAPITestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc b/third_party/jpeg-xl/lib/jpegli/encode_finish.cc deleted file mode 100644 index 8cc3b8d27503b..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/encode_finish.h" - -#include -#include - -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jpegli/quant.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/encode_finish.cc" -#include -#include - -#include "lib/jpegli/dct-inl.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::GetLane; - -using D = HWY_FULL(float); -using DI = HWY_FULL(int32_t); -using DI16 = Rebind; - -void ReQuantizeBlock(int16_t* block, const float* qmc, float aq_strength, - const float* zero_bias_offset, - const float* zero_bias_mul) { - D d; - DI di; - DI16 di16; - const auto aq_mul = Set(d, aq_strength); - for (size_t k = 0; k < DCTSIZE2; k += Lanes(d)) { - const auto in = Load(di16, block + k); - const auto val = ConvertTo(d, PromoteTo(di, in)); - const auto q = Load(d, qmc + k); - const auto qval = Mul(val, q); - const auto zb_offset = Load(d, zero_bias_offset + k); - const auto zb_mul = Load(d, zero_bias_mul + k); - const auto threshold = Add(zb_offset, Mul(zb_mul, aq_mul)); - const auto nzero_mask = Ge(Abs(qval), threshold); - const auto iqval = IfThenElseZero(nzero_mask, Round(qval)); - Store(DemoteTo(di16, ConvertTo(di, iqval)), di16, block + k); - } -} - -float BlockError(const int16_t* block, const float* qmc, const float* iqmc, - const float aq_strength, const float* zero_bias_offset, - const float* zero_bias_mul) { - D d; - DI di; - DI16 di16; - auto err = Zero(d); - const auto scale = Set(d, 1.0 / 16); - const auto aq_mul = Set(d, aq_strength); - for (size_t k = 0; k < DCTSIZE2; k += Lanes(d)) { - const auto in = Load(di16, block + k); - const auto val = ConvertTo(d, PromoteTo(di, in)); - const auto q = Load(d, qmc + k); - const auto qval = Mul(val, q); - const auto zb_offset = Load(d, zero_bias_offset + k); - const auto zb_mul = Load(d, zero_bias_mul + k); - const auto threshold = Add(zb_offset, Mul(zb_mul, aq_mul)); - const auto nzero_mask = Ge(Abs(qval), threshold); - const auto iqval = IfThenElseZero(nzero_mask, Round(qval)); - const auto invq = Load(d, iqmc + k); - const auto rval = Mul(iqval, invq); - const auto diff = Mul(Sub(val, rval), scale); - err = Add(err, Mul(diff, diff)); - } - return GetLane(SumOfLanes(d, err)); -} - -void ComputeInverseWeights(const float* qmc, float* iqmc) { - for (int k = 0; k < 64; ++k) { - iqmc[k] = 1.0f / qmc[k]; - } -} - -float ComputePSNR(j_compress_ptr cinfo, int sampling) { - jpeg_comp_master* m = cinfo->master; - InitQuantizer(cinfo, QuantPass::SEARCH_SECOND_PASS); - double error = 0.0; - size_t num = 0; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - const float* qmc = m->quant_mul[c]; - const int h_factor = m->h_factor[c]; - const int v_factor = m->v_factor[c]; - const float* zero_bias_offset = m->zero_bias_offset[c]; - const float* zero_bias_mul = m->zero_bias_mul[c]; - HWY_ALIGN float iqmc[64]; - ComputeInverseWeights(qmc, iqmc); - for (JDIMENSION by = 0; by < comp->height_in_blocks; by += sampling) { - JBLOCKARRAY blocks = GetBlockRow(cinfo, c, by); - const float* qf = m->quant_field.Row(by * v_factor); - for (JDIMENSION bx = 0; bx < comp->width_in_blocks; bx += sampling) { - error += BlockError(&blocks[0][bx][0], qmc, iqmc, qf[bx * h_factor], - zero_bias_offset, zero_bias_mul); - num += DCTSIZE2; - } - } - } - return 4.3429448f * log(num / (error / 255. / 255.)); -} - -void ReQuantizeCoeffs(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - InitQuantizer(cinfo, QuantPass::SEARCH_SECOND_PASS); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - const float* qmc = m->quant_mul[c]; - const int h_factor = m->h_factor[c]; - const int v_factor = m->v_factor[c]; - const float* zero_bias_offset = m->zero_bias_offset[c]; - const float* zero_bias_mul = m->zero_bias_mul[c]; - for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY block = GetBlockRow(cinfo, c, by); - const float* qf = m->quant_field.Row(by * v_factor); - for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { - ReQuantizeBlock(&block[0][bx][0], qmc, qf[bx * h_factor], - zero_bias_offset, zero_bias_mul); - } - } - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { -namespace { -HWY_EXPORT(ComputePSNR); -HWY_EXPORT(ReQuantizeCoeffs); - -void ReQuantizeCoeffs(j_compress_ptr cinfo) { - HWY_DYNAMIC_DISPATCH(ReQuantizeCoeffs)(cinfo); -} - -float ComputePSNR(j_compress_ptr cinfo, int sampling) { - return HWY_DYNAMIC_DISPATCH(ComputePSNR)(cinfo, sampling); -} - -void UpdateDistance(j_compress_ptr cinfo, float distance) { - float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; - SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/true); -} - -float Clamp(float val, float minval, float maxval) { - return std::max(minval, std::min(maxval, val)); -} - -#define PSNR_SEARCH_DBG 0 - -float FindDistanceForPSNR(j_compress_ptr cinfo) { - constexpr int kMaxIters = 20; - const float psnr_target = cinfo->master->psnr_target; - const float tolerance = cinfo->master->psnr_tolerance; - const float min_dist = cinfo->master->min_distance; - const float max_dist = cinfo->master->max_distance; - float d = Clamp(1.0f, min_dist, max_dist); - for (int sampling : {4, 1}) { - float best_diff = std::numeric_limits::max(); - float best_distance = 0.0f; - float best_psnr = 0.0; - float dmin = min_dist; - float dmax = max_dist; - bool found_lower_bound = false; - bool found_upper_bound = false; - for (int i = 0; i < kMaxIters; ++i) { - UpdateDistance(cinfo, d); - float psnr = ComputePSNR(cinfo, sampling); - if (psnr > psnr_target) { - dmin = d; - found_lower_bound = true; - } else { - dmax = d; - found_upper_bound = true; - } -#if (PSNR_SEARCH_DBG > 1) - printf("sampling %d iter %2d d %7.4f psnr %.2f", sampling, i, d, psnr); - if (found_upper_bound && found_lower_bound) { - printf(" d-interval: [ %7.4f .. %7.4f ]", dmin, dmax); - } - printf("\n"); -#endif - float diff = std::abs(psnr - psnr_target); - if (diff < best_diff) { - best_diff = diff; - best_distance = d; - best_psnr = psnr; - } - if (diff < tolerance * psnr_target || dmin == dmax) { - break; - } - if (!found_lower_bound || !found_upper_bound) { - d *= std::exp(0.15f * (psnr - psnr_target)); - } else { - d = 0.5f * (dmin + dmax); - } - d = Clamp(d, min_dist, max_dist); - } - d = best_distance; - if (sampling == 1 && PSNR_SEARCH_DBG) { - printf("Final PSNR %.2f at distance %.4f\n", best_psnr, d); - } else { - (void)best_psnr; - } - } - return d; -} - -} // namespace - -void QuantizetoPSNR(j_compress_ptr cinfo) { - float distance = FindDistanceForPSNR(cinfo); - UpdateDistance(cinfo, distance); - ReQuantizeCoeffs(cinfo); -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/encode_finish.h b/third_party/jpeg-xl/lib/jpegli/encode_finish.h deleted file mode 100644 index f6862decb9243..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_finish.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ENCODE_FINISH_H_ -#define LIB_JPEGLI_ENCODE_FINISH_H_ - -#include "lib/jpegli/encode_internal.h" - -namespace jpegli { - -void QuantizetoPSNR(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_ENCODE_FINISH_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/encode_internal.h b/third_party/jpeg-xl/lib/jpegli/encode_internal.h deleted file mode 100644 index 4dbef97538660..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_internal.h +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ENCODE_INTERNAL_H_ -#define LIB_JPEGLI_ENCODE_INTERNAL_H_ - -#include - -#include "lib/jpegli/bit_writer.h" -#include "lib/jpegli/common.h" -#include "lib/jpegli/common_internal.h" -#include "lib/jpegli/encode.h" - -namespace jpegli { - -constexpr unsigned char kICCSignature[12] = { - 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -constexpr int kICCMarker = JPEG_APP0 + 2; - -constexpr int kDefaultProgressiveLevel = 0; - -typedef int16_t coeff_t; - -struct HuffmanCodeTable { - int depth[256]; - int code[256]; -}; - -struct Token { - uint8_t context; - uint8_t symbol; - uint16_t bits; - Token(int c, int s, int b) : context(c), symbol(s), bits(b) {} -}; - -struct TokenArray { - Token* tokens; - size_t num_tokens; -}; - -struct RefToken { - uint8_t symbol; - uint8_t refbits; -}; - -struct ScanTokenInfo { - RefToken* tokens; - size_t num_tokens; - uint8_t* refbits; - uint16_t* eobruns; - size_t* restarts; - size_t num_restarts; - size_t num_nonzeros; - size_t num_future_nonzeros; - size_t token_offset; - size_t restart_interval; - size_t MCUs_per_row; - size_t MCU_rows_in_scan; - size_t blocks_in_MCU; - size_t num_blocks; -}; - -} // namespace jpegli - -struct jpeg_comp_master { - jpegli::RowBuffer input_buffer[jpegli::kMaxComponents]; - jpegli::RowBuffer* smooth_input[jpegli::kMaxComponents]; - jpegli::RowBuffer* raw_data[jpegli::kMaxComponents]; - bool force_baseline; - bool xyb_mode; - uint8_t cicp_transfer_function; - bool use_std_tables; - bool use_adaptive_quantization; - int progressive_level; - size_t xsize_blocks; - size_t ysize_blocks; - size_t blocks_per_iMCU_row; - jpegli::ScanTokenInfo* scan_token_info; - JpegliDataType data_type; - JpegliEndianness endianness; - void (*input_method)(const uint8_t* row_in, size_t len, - float* row_out[jpegli::kMaxComponents]); - void (*color_transform)(float* row[jpegli::kMaxComponents], size_t len); - void (*downsample_method[jpegli::kMaxComponents])( - float* rows_in[MAX_SAMP_FACTOR], size_t len, float* row_out); - float* quant_mul[jpegli::kMaxComponents]; - float* zero_bias_offset[jpegli::kMaxComponents]; - float* zero_bias_mul[jpegli::kMaxComponents]; - int h_factor[jpegli::kMaxComponents]; - int v_factor[jpegli::kMaxComponents]; - // Array of Huffman tables that will be encoded in one or more DHT segments. - // In progressive mode we compute all Huffman tables that will be used in any - // of the scans, thus we can have more than 4 tables here. - JHUFF_TBL* huffman_tables; - size_t num_huffman_tables; - // Array of num_huffman_tables slot ids, where the ith element is the slot id - // of the ith Huffman table, as it appears in the DHT segment. The range of - // the slot ids is 0..3 for DC and 16..19 for AC Huffman codes. - uint8_t* slot_id_map; - // Maps context ids to an index in the huffman_tables array. Each component in - // each scan has a DC and AC context id, which are defined as follows: - // - DC context id is the component index (relative to cinfo->comp_info) of - // the scan component - // - AC context ids start at 4 and are increased for each component of each - // scan that have AC components (i.e. Se > 0) - uint8_t* context_map; - size_t num_contexts; - // Array of cinfo->num_scans context ids, where the ith element is the context - // id of the first AC component of the ith scan. - uint8_t* ac_ctx_offset; - // Array of num_huffman tables derived coding tables. - jpegli::HuffmanCodeTable* coding_tables; - float* diff_buffer; - jpegli::RowBuffer fuzzy_erosion_tmp; - jpegli::RowBuffer pre_erosion; - jpegli::RowBuffer quant_field; - jvirt_barray_ptr* coeff_buffers; - size_t next_input_row; - size_t next_iMCU_row; - size_t next_dht_index; - size_t last_restart_interval; - JCOEF last_dc_coeff[MAX_COMPS_IN_SCAN]; - jpegli::JpegBitWriter bw; - float* dct_buffer; - int32_t* block_tmp; - jpegli::TokenArray* token_arrays; - size_t cur_token_array; - jpegli::Token* next_token; - size_t num_tokens; - size_t total_num_tokens; - jpegli::RefToken* next_refinement_token; - uint8_t* next_refinement_bit; - float psnr_target; - float psnr_tolerance; - float min_distance; - float max_distance; -}; - -#endif // LIB_JPEGLI_ENCODE_INTERNAL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/encode_streaming.cc b/third_party/jpeg-xl/lib/jpegli/encode_streaming.cc deleted file mode 100644 index ff43864a47bae..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_streaming.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/encode_streaming.h" - -#include - -#include "lib/jpegli/bit_writer.h" -#include "lib/jpegli/bitstream.h" -#include "lib/jpegli/entropy_coding.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jxl/base/bits.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/encode_streaming.cc" -#include -#include - -#include "lib/jpegli/dct-inl.h" -#include "lib/jpegli/entropy_coding-inl.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -static const int kStreamingModeCoefficients = 0; -static const int kStreamingModeTokens = 1; -static const int kStreamingModeBits = 2; - -namespace { -void ZigZagShuffle(int32_t* JXL_RESTRICT block) { - // TODO(szabadka) SIMDify this. - int32_t tmp[DCTSIZE2]; - tmp[0] = block[0]; - tmp[1] = block[1]; - tmp[2] = block[8]; - tmp[3] = block[16]; - tmp[4] = block[9]; - tmp[5] = block[2]; - tmp[6] = block[3]; - tmp[7] = block[10]; - tmp[8] = block[17]; - tmp[9] = block[24]; - tmp[10] = block[32]; - tmp[11] = block[25]; - tmp[12] = block[18]; - tmp[13] = block[11]; - tmp[14] = block[4]; - tmp[15] = block[5]; - tmp[16] = block[12]; - tmp[17] = block[19]; - tmp[18] = block[26]; - tmp[19] = block[33]; - tmp[20] = block[40]; - tmp[21] = block[48]; - tmp[22] = block[41]; - tmp[23] = block[34]; - tmp[24] = block[27]; - tmp[25] = block[20]; - tmp[26] = block[13]; - tmp[27] = block[6]; - tmp[28] = block[7]; - tmp[29] = block[14]; - tmp[30] = block[21]; - tmp[31] = block[28]; - tmp[32] = block[35]; - tmp[33] = block[42]; - tmp[34] = block[49]; - tmp[35] = block[56]; - tmp[36] = block[57]; - tmp[37] = block[50]; - tmp[38] = block[43]; - tmp[39] = block[36]; - tmp[40] = block[29]; - tmp[41] = block[22]; - tmp[42] = block[15]; - tmp[43] = block[23]; - tmp[44] = block[30]; - tmp[45] = block[37]; - tmp[46] = block[44]; - tmp[47] = block[51]; - tmp[48] = block[58]; - tmp[49] = block[59]; - tmp[50] = block[52]; - tmp[51] = block[45]; - tmp[52] = block[38]; - tmp[53] = block[31]; - tmp[54] = block[39]; - tmp[55] = block[46]; - tmp[56] = block[53]; - tmp[57] = block[60]; - tmp[58] = block[61]; - tmp[59] = block[54]; - tmp[60] = block[47]; - tmp[61] = block[55]; - tmp[62] = block[62]; - tmp[63] = block[63]; - memcpy(block, tmp, DCTSIZE2 * sizeof(tmp[0])); -} -} // namespace - -template -void ProcessiMCURow(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - JpegBitWriter* bw = &m->bw; - int xsize_mcus = DivCeil(cinfo->image_width, 8 * cinfo->max_h_samp_factor); - int ysize_mcus = DivCeil(cinfo->image_height, 8 * cinfo->max_v_samp_factor); - int mcu_y = m->next_iMCU_row; - int32_t* block = m->block_tmp; - int32_t* symbols = m->block_tmp + DCTSIZE2; - int32_t* nonzero_idx = m->block_tmp + 3 * DCTSIZE2; - coeff_t* JXL_RESTRICT last_dc_coeff = m->last_dc_coeff; - bool adaptive_quant = m->use_adaptive_quantization && m->psnr_target == 0; - JBLOCKARRAY blocks[kMaxComponents]; - if (kMode == kStreamingModeCoefficients) { - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - int by0 = mcu_y * comp->v_samp_factor; - int block_rows_left = comp->height_in_blocks - by0; - int max_block_rows = std::min(comp->v_samp_factor, block_rows_left); - blocks[c] = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coeff_buffers[c], by0, - max_block_rows, true); - } - } - if (kMode == kStreamingModeTokens) { - TokenArray* ta = &m->token_arrays[m->cur_token_array]; - int max_tokens_per_mcu_row = MaxNumTokensPerMCURow(cinfo); - if (ta->num_tokens + max_tokens_per_mcu_row > m->num_tokens) { - if (ta->tokens) { - m->total_num_tokens += ta->num_tokens; - ++m->cur_token_array; - ta = &m->token_arrays[m->cur_token_array]; - } - m->num_tokens = - EstimateNumTokens(cinfo, mcu_y, ysize_mcus, m->total_num_tokens, - max_tokens_per_mcu_row); - ta->tokens = Allocate(cinfo, m->num_tokens, JPOOL_IMAGE); - m->next_token = ta->tokens; - } - } - const float* imcu_start[kMaxComponents]; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - imcu_start[c] = m->raw_data[c]->Row(mcu_y * comp->v_samp_factor * DCTSIZE); - } - const float* qf = nullptr; - if (adaptive_quant) { - qf = m->quant_field.Row(0); - } - HuffmanCodeTable* dc_code = nullptr; - HuffmanCodeTable* ac_code = nullptr; - const size_t qf_stride = m->quant_field.stride(); - for (int mcu_x = 0; mcu_x < xsize_mcus; ++mcu_x) { - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - if (kMode == kStreamingModeBits) { - dc_code = &m->coding_tables[m->context_map[c]]; - ac_code = &m->coding_tables[m->context_map[c + 4]]; - } - float* JXL_RESTRICT qmc = m->quant_mul[c]; - const size_t stride = m->raw_data[c]->stride(); - const int h_factor = m->h_factor[c]; - const float* zero_bias_offset = m->zero_bias_offset[c]; - const float* zero_bias_mul = m->zero_bias_mul[c]; - float aq_strength = 0.0f; - for (int iy = 0; iy < comp->v_samp_factor; ++iy) { - for (int ix = 0; ix < comp->h_samp_factor; ++ix) { - size_t by = mcu_y * comp->v_samp_factor + iy; - size_t bx = mcu_x * comp->h_samp_factor + ix; - if (bx >= comp->width_in_blocks || by >= comp->height_in_blocks) { - if (kMode == kStreamingModeTokens) { - *m->next_token++ = Token(c, 0, 0); - *m->next_token++ = Token(c + 4, 0, 0); - } else if (kMode == kStreamingModeBits) { - WriteBits(bw, dc_code->depth[0], dc_code->code[0]); - WriteBits(bw, ac_code->depth[0], ac_code->code[0]); - } - continue; - } - if (adaptive_quant) { - aq_strength = qf[iy * qf_stride + bx * h_factor]; - } - const float* pixels = imcu_start[c] + (iy * stride + bx) * DCTSIZE; - ComputeCoefficientBlock(pixels, stride, qmc, last_dc_coeff[c], - aq_strength, zero_bias_offset, zero_bias_mul, - m->dct_buffer, block); - if (kMode == kStreamingModeCoefficients) { - JCOEF* cblock = &blocks[c][iy][bx][0]; - for (int k = 0; k < DCTSIZE2; ++k) { - cblock[k] = block[kJPEGNaturalOrder[k]]; - } - } - block[0] -= last_dc_coeff[c]; - last_dc_coeff[c] += block[0]; - if (kMode == kStreamingModeTokens) { - ComputeTokensForBlock(block, 0, c, c + 4, - &m->next_token); - } else if (kMode == kStreamingModeBits) { - ZigZagShuffle(block); - const int num_nonzeros = CompactBlock(block, nonzero_idx); - const bool emit_eob = nonzero_idx[num_nonzeros - 1] < 1008; - ComputeSymbols(num_nonzeros, nonzero_idx, block, symbols); - WriteBlock(symbols, block, num_nonzeros, emit_eob, dc_code, ac_code, - bw); - } - } - } - } - } - if (kMode == kStreamingModeTokens) { - TokenArray* ta = &m->token_arrays[m->cur_token_array]; - ta->num_tokens = m->next_token - ta->tokens; - ScanTokenInfo* sti = &m->scan_token_info[0]; - sti->num_tokens = m->total_num_tokens + ta->num_tokens; - sti->restarts[0] = sti->num_tokens; - } -} - -void ComputeCoefficientsForiMCURow(j_compress_ptr cinfo) { - ProcessiMCURow(cinfo); -} - -void ComputeTokensForiMCURow(j_compress_ptr cinfo) { - ProcessiMCURow(cinfo); -} - -void WriteiMCURow(j_compress_ptr cinfo) { - ProcessiMCURow(cinfo); -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { -HWY_EXPORT(ComputeCoefficientsForiMCURow); -HWY_EXPORT(ComputeTokensForiMCURow); -HWY_EXPORT(WriteiMCURow); - -void ComputeCoefficientsForiMCURow(j_compress_ptr cinfo) { - HWY_DYNAMIC_DISPATCH(ComputeCoefficientsForiMCURow)(cinfo); -} - -void ComputeTokensForiMCURow(j_compress_ptr cinfo) { - HWY_DYNAMIC_DISPATCH(ComputeTokensForiMCURow)(cinfo); -} - -void WriteiMCURow(j_compress_ptr cinfo) { - HWY_DYNAMIC_DISPATCH(WriteiMCURow)(cinfo); -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/encode_streaming.h b/third_party/jpeg-xl/lib/jpegli/encode_streaming.h deleted file mode 100644 index 69acff4eaf2f2..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/encode_streaming.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ENCODE_STREAMING_H_ -#define LIB_JPEGLI_ENCODE_STREAMING_H_ - -#include "lib/jpegli/encode_internal.h" - -namespace jpegli { - -void ComputeCoefficientsForiMCURow(j_compress_ptr cinfo); - -void ComputeTokensForiMCURow(j_compress_ptr cinfo); - -void WriteiMCURow(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_ENCODE_STREAMING_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/entropy_coding-inl.h b/third_party/jpeg-xl/lib/jpegli/entropy_coding-inl.h deleted file mode 100644 index bfb436d7954da..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/entropy_coding-inl.h +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#if defined(LIB_JPEGLI_ENTROPY_CODING_INL_H_) == defined(HWY_TARGET_TOGGLE) -#ifdef LIB_JPEGLI_ENTROPY_CODING_INL_H_ -#undef LIB_JPEGLI_ENTROPY_CODING_INL_H_ -#else -#define LIB_JPEGLI_ENTROPY_CODING_INL_H_ -#endif - -#include "lib/jxl/base/compiler_specific.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { -namespace { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Abs; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::And; -using hwy::HWY_NAMESPACE::AndNot; -using hwy::HWY_NAMESPACE::Compress; -using hwy::HWY_NAMESPACE::CountTrue; -using hwy::HWY_NAMESPACE::Eq; -using hwy::HWY_NAMESPACE::GetLane; -using hwy::HWY_NAMESPACE::MaskFromVec; -using hwy::HWY_NAMESPACE::Max; -using hwy::HWY_NAMESPACE::Not; -using hwy::HWY_NAMESPACE::Or; -using hwy::HWY_NAMESPACE::ShiftRight; -using hwy::HWY_NAMESPACE::Shl; -using hwy::HWY_NAMESPACE::Sub; - -using DI = HWY_FULL(int32_t); -constexpr DI di; - -template -JXL_INLINE V NumBits(DI di, const V x) { - // TODO(szabadka) Add faster implementations for some specific architectures. - const auto b1 = And(x, Set(di, 1)); - const auto b2 = And(x, Set(di, 2)); - const auto b3 = Sub((And(x, Set(di, 4))), Set(di, 1)); - const auto b4 = Sub((And(x, Set(di, 8))), Set(di, 4)); - const auto b5 = Sub((And(x, Set(di, 16))), Set(di, 11)); - const auto b6 = Sub((And(x, Set(di, 32))), Set(di, 26)); - const auto b7 = Sub((And(x, Set(di, 64))), Set(di, 57)); - const auto b8 = Sub((And(x, Set(di, 128))), Set(di, 120)); - const auto b9 = Sub((And(x, Set(di, 256))), Set(di, 247)); - const auto b10 = Sub((And(x, Set(di, 512))), Set(di, 502)); - const auto b11 = Sub((And(x, Set(di, 1024))), Set(di, 1013)); - const auto b12 = Sub((And(x, Set(di, 2048))), Set(di, 2036)); - return Max(Max(Max(Max(b1, b2), Max(b3, b4)), Max(Max(b5, b6), Max(b7, b8))), - Max(Max(b9, b10), Max(b11, b12))); -} - -// Coefficient indexes pre-multiplied by 16 for the symbol calculation. -HWY_ALIGN constexpr int32_t kIndexes[64] = { - 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, - 208, 224, 240, 256, 272, 288, 304, 320, 336, 352, 368, 384, 400, - 416, 432, 448, 464, 480, 496, 512, 528, 544, 560, 576, 592, 608, - 624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816, - 832, 848, 864, 880, 896, 912, 928, 944, 960, 976, 992, 1008, -}; - -JXL_INLINE int CompactBlock(int32_t* JXL_RESTRICT block, - int32_t* JXL_RESTRICT nonzero_idx) { - const auto zero = Zero(di); - HWY_ALIGN constexpr int32_t dc_mask_lanes[HWY_LANES(DI)] = {-1}; - const auto dc_mask = MaskFromVec(Load(di, dc_mask_lanes)); - int num_nonzeros = 0; - int k = 0; - { - const auto coef = Load(di, block); - const auto idx = Load(di, kIndexes); - const auto nonzero_mask = Or(dc_mask, Not(Eq(coef, zero))); - const auto nzero_coef = Compress(coef, nonzero_mask); - const auto nzero_idx = Compress(idx, nonzero_mask); - StoreU(nzero_coef, di, &block[num_nonzeros]); - StoreU(nzero_idx, di, &nonzero_idx[num_nonzeros]); - num_nonzeros += CountTrue(di, nonzero_mask); - k += Lanes(di); - } - for (; k < DCTSIZE2; k += Lanes(di)) { - const auto coef = Load(di, &block[k]); - const auto idx = Load(di, &kIndexes[k]); - const auto nonzero_mask = Not(Eq(coef, zero)); - const auto nzero_coef = Compress(coef, nonzero_mask); - const auto nzero_idx = Compress(idx, nonzero_mask); - StoreU(nzero_coef, di, &block[num_nonzeros]); - StoreU(nzero_idx, di, &nonzero_idx[num_nonzeros]); - num_nonzeros += CountTrue(di, nonzero_mask); - } - return num_nonzeros; -} - -JXL_INLINE void ComputeSymbols(const int num_nonzeros, - int32_t* JXL_RESTRICT nonzero_idx, - int32_t* JXL_RESTRICT block, - int32_t* JXL_RESTRICT symbols) { - nonzero_idx[-1] = -16; - const auto one = Set(di, 1); - const auto offset = Set(di, 16); - for (int i = 0; i < num_nonzeros; i += Lanes(di)) { - const auto idx = Load(di, &nonzero_idx[i]); - const auto prev_idx = LoadU(di, &nonzero_idx[i - 1]); - const auto coeff = Load(di, &block[i]); - const auto nbits = NumBits(di, Abs(coeff)); - const auto mask = ShiftRight<8 * sizeof(int32_t) - 1>(coeff); - const auto bits = And(Add(coeff, mask), Sub(Shl(one, nbits), one)); - const auto symbol = Sub(Add(nbits, idx), Add(prev_idx, offset)); - Store(symbol, di, symbols + i); - Store(bits, di, block + i); - } -} - -template -int NumNonZero8x8ExceptDC(const T* block) { - const HWY_CAPPED(T, 8) di; - - const auto zero = Zero(di); - // Add FFFF for every zero coefficient, negate to get #zeros. - auto neg_sum_zero = zero; - { - // First row has DC, so mask - const size_t y = 0; - HWY_ALIGN const T dc_mask_lanes[8] = {-1}; - - for (size_t x = 0; x < 8; x += Lanes(di)) { - const auto dc_mask = Load(di, dc_mask_lanes + x); - - // DC counts as zero so we don't include it in nzeros. - const auto coef = AndNot(dc_mask, Load(di, &block[y * 8 + x])); - - neg_sum_zero = Add(neg_sum_zero, VecFromMask(di, Eq(coef, zero))); - } - } - // Remaining rows: no mask - for (size_t y = 1; y < 8; y++) { - for (size_t x = 0; x < 8; x += Lanes(di)) { - const auto coef = Load(di, &block[y * 8 + x]); - neg_sum_zero = Add(neg_sum_zero, VecFromMask(di, Eq(coef, zero))); - } - } - - // We want 64 - sum_zero, add because neg_sum_zero is already negated. - return kDCTBlockSize + GetLane(SumOfLanes(di, neg_sum_zero)); -} - -template -void ComputeTokensForBlock(const T* block, int last_dc, int dc_ctx, int ac_ctx, - Token** tokens_ptr) { - Token* next_token = *tokens_ptr; - coeff_t temp2; - coeff_t temp; - temp = block[0] - last_dc; - if (temp == 0) { - *next_token++ = Token(dc_ctx, 0, 0); - } else { - temp2 = temp; - if (temp < 0) { - temp = -temp; - temp2--; - } - int dc_nbits = jxl::FloorLog2Nonzero(temp) + 1; - int dc_mask = (1 << dc_nbits) - 1; - *next_token++ = Token(dc_ctx, dc_nbits, temp2 & dc_mask); - } - int num_nonzeros = NumNonZero8x8ExceptDC(block); - for (int k = 1; k < 64; ++k) { - if (num_nonzeros == 0) { - *next_token++ = Token(ac_ctx, 0, 0); - break; - } - int r = 0; - if (zig_zag_order) { - while ((temp = block[k]) == 0) { - r++; - k++; - } - } else { - while ((temp = block[kJPEGNaturalOrder[k]]) == 0) { - r++; - k++; - } - } - --num_nonzeros; - if (temp < 0) { - temp = -temp; - temp2 = ~temp; - } else { - temp2 = temp; - } - while (r > 15) { - *next_token++ = Token(ac_ctx, 0xf0, 0); - r -= 16; - } - int ac_nbits = jxl::FloorLog2Nonzero(temp) + 1; - int ac_mask = (1 << ac_nbits) - 1; - int symbol = (r << 4u) + ac_nbits; - *next_token++ = Token(ac_ctx, symbol, temp2 & ac_mask); - } - *tokens_ptr = next_token; -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); -#endif // LIB_JPEGLI_ENTROPY_CODING_INL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc b/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc deleted file mode 100644 index d7d928099b1c2..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc +++ /dev/null @@ -1,826 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/entropy_coding.h" - -#include - -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/huffman.h" -#include "lib/jxl/base/bits.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/entropy_coding.cc" -#include -#include - -#include "lib/jpegli/entropy_coding-inl.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -void ComputeTokensSequential(const coeff_t* block, int last_dc, int dc_ctx, - int ac_ctx, Token** tokens_ptr) { - ComputeTokensForBlock(block, last_dc, dc_ctx, ac_ctx, - tokens_ptr); -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -size_t MaxNumTokensPerMCURow(j_compress_ptr cinfo) { - int MCUs_per_row = DivCeil(cinfo->image_width, 8 * cinfo->max_h_samp_factor); - size_t blocks_per_mcu = 0; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - blocks_per_mcu += comp->h_samp_factor * comp->v_samp_factor; - } - return kDCTBlockSize * blocks_per_mcu * MCUs_per_row; -} - -size_t EstimateNumTokens(j_compress_ptr cinfo, size_t mcu_y, size_t ysize_mcus, - size_t num_tokens, size_t max_per_row) { - size_t estimate; - if (mcu_y == 0) { - estimate = 16 * max_per_row; - } else { - estimate = (4 * ysize_mcus * num_tokens) / (3 * mcu_y); - } - size_t mcus_left = ysize_mcus - mcu_y; - return std::min(mcus_left * max_per_row, - std::max(max_per_row, estimate - num_tokens)); -} - -namespace { -HWY_EXPORT(ComputeTokensSequential); - -void TokenizeProgressiveDC(const coeff_t* coeffs, int context, int Al, - coeff_t* last_dc_coeff, Token** next_token) { - coeff_t temp2; - coeff_t temp; - temp2 = coeffs[0] >> Al; - temp = temp2 - *last_dc_coeff; - *last_dc_coeff = temp2; - temp2 = temp; - if (temp < 0) { - temp = -temp; - temp2--; - } - int nbits = (temp == 0) ? 0 : (jxl::FloorLog2Nonzero(temp) + 1); - int bits = temp2 & ((1 << nbits) - 1); - *(*next_token)++ = Token(context, nbits, bits); -} - -void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, - int context, ScanTokenInfo* sti) { - jpeg_comp_master* m = cinfo->master; - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - const int comp_idx = scan_info->component_index[0]; - const jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - const int Al = scan_info->Al; - const int Ss = scan_info->Ss; - const int Se = scan_info->Se; - const size_t restart_interval = sti->restart_interval; - int restarts_to_go = restart_interval; - size_t num_blocks = comp->height_in_blocks * comp->width_in_blocks; - size_t num_restarts = - restart_interval > 0 ? DivCeil(num_blocks, restart_interval) : 1; - size_t restart_idx = 0; - int eob_run = 0; - TokenArray* ta = &m->token_arrays[m->cur_token_array]; - sti->token_offset = m->total_num_tokens + ta->num_tokens; - sti->restarts = Allocate(cinfo, num_restarts, JPOOL_IMAGE); - const auto emit_eob_run = [&]() { - int nbits = jxl::FloorLog2Nonzero(eob_run); - int symbol = nbits << 4u; - *m->next_token++ = Token(context, symbol, eob_run & ((1 << nbits) - 1)); - eob_run = 0; - }; - for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY blocks = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coeff_buffers[comp_idx], by, - 1, FALSE); - // Each coefficient can appear in at most one token, but we have to reserve - // one extra EOBrun token that was rolled over from the previous block-row - // and has to be flushed at the end. - int max_tokens_per_row = 1 + comp->width_in_blocks * (Se - Ss + 1); - if (ta->num_tokens + max_tokens_per_row > m->num_tokens) { - if (ta->tokens) { - m->total_num_tokens += ta->num_tokens; - ++m->cur_token_array; - ta = &m->token_arrays[m->cur_token_array]; - } - m->num_tokens = - EstimateNumTokens(cinfo, by, comp->height_in_blocks, - m->total_num_tokens, max_tokens_per_row); - ta->tokens = Allocate(cinfo, m->num_tokens, JPOOL_IMAGE); - m->next_token = ta->tokens; - } - for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { - if (restart_interval > 0 && restarts_to_go == 0) { - if (eob_run > 0) emit_eob_run(); - ta->num_tokens = m->next_token - ta->tokens; - sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens; - restarts_to_go = restart_interval; - } - const coeff_t* block = &blocks[0][bx][0]; - coeff_t temp2; - coeff_t temp; - int r = 0; - int num_nzeros = 0; - int num_future_nzeros = 0; - for (int k = Ss; k <= Se; ++k) { - temp = block[k]; - if (temp == 0) { - r++; - continue; - } - if (temp < 0) { - temp = -temp; - temp >>= Al; - temp2 = ~temp; - } else { - temp >>= Al; - temp2 = temp; - } - if (temp == 0) { - r++; - num_future_nzeros++; - continue; - } - if (eob_run > 0) emit_eob_run(); - while (r > 15) { - *m->next_token++ = Token(context, 0xf0, 0); - r -= 16; - } - int nbits = jxl::FloorLog2Nonzero(temp) + 1; - int symbol = (r << 4u) + nbits; - *m->next_token++ = Token(context, symbol, temp2 & ((1 << nbits) - 1)); - ++num_nzeros; - r = 0; - } - if (r > 0) { - ++eob_run; - if (eob_run == 0x7FFF) emit_eob_run(); - } - sti->num_nonzeros += num_nzeros; - sti->num_future_nonzeros += num_future_nzeros; - --restarts_to_go; - } - ta->num_tokens = m->next_token - ta->tokens; - } - if (eob_run > 0) { - emit_eob_run(); - ++ta->num_tokens; - } - sti->num_tokens = m->total_num_tokens + ta->num_tokens - sti->token_offset; - sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens; -} - -void TokenizeACRefinementScan(j_compress_ptr cinfo, int scan_index, - ScanTokenInfo* sti) { - jpeg_comp_master* m = cinfo->master; - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - const int comp_idx = scan_info->component_index[0]; - const jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - const int Al = scan_info->Al; - const int Ss = scan_info->Ss; - const int Se = scan_info->Se; - const size_t restart_interval = sti->restart_interval; - int restarts_to_go = restart_interval; - RefToken token; - int eob_run = 0; - int eob_refbits = 0; - size_t num_blocks = comp->height_in_blocks * comp->width_in_blocks; - size_t num_restarts = - restart_interval > 0 ? DivCeil(num_blocks, restart_interval) : 1; - sti->tokens = m->next_refinement_token; - sti->refbits = m->next_refinement_bit; - sti->eobruns = Allocate(cinfo, num_blocks / 2, JPOOL_IMAGE); - sti->restarts = Allocate(cinfo, num_restarts, JPOOL_IMAGE); - RefToken* next_token = sti->tokens; - RefToken* next_eob_token = next_token; - uint8_t* next_ref_bit = sti->refbits; - uint16_t* next_eobrun = sti->eobruns; - size_t restart_idx = 0; - for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY blocks = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coeff_buffers[comp_idx], by, - 1, FALSE); - for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { - if (restart_interval > 0 && restarts_to_go == 0) { - sti->restarts[restart_idx++] = next_token - sti->tokens; - restarts_to_go = restart_interval; - next_eob_token = next_token; - eob_run = eob_refbits = 0; - } - const coeff_t* block = &blocks[0][bx][0]; - int num_eob_refinement_bits = 0; - int num_refinement_bits = 0; - int num_nzeros = 0; - int r = 0; - for (int k = Ss; k <= Se; ++k) { - int absval = block[k]; - if (absval == 0) { - r++; - continue; - } - const int mask = absval >> (8 * sizeof(int) - 1); - absval += mask; - absval ^= mask; - absval >>= Al; - if (absval == 0) { - r++; - continue; - } - while (r > 15) { - token.symbol = 0xf0; - token.refbits = num_refinement_bits; - *next_token++ = token; - r -= 16; - num_eob_refinement_bits += num_refinement_bits; - num_refinement_bits = 0; - } - if (absval > 1) { - *next_ref_bit++ = absval & 1u; - ++num_refinement_bits; - continue; - } - int symbol = (r << 4u) + 1 + ((mask + 1) << 1); - token.symbol = symbol; - token.refbits = num_refinement_bits; - *next_token++ = token; - ++num_nzeros; - num_refinement_bits = 0; - num_eob_refinement_bits = 0; - r = 0; - next_eob_token = next_token; - eob_run = eob_refbits = 0; - } - if (r > 0 || num_eob_refinement_bits + num_refinement_bits > 0) { - ++eob_run; - eob_refbits += num_eob_refinement_bits + num_refinement_bits; - if (eob_refbits > 255) { - ++next_eob_token; - eob_refbits = num_eob_refinement_bits + num_refinement_bits; - eob_run = 1; - } - next_token = next_eob_token; - next_token->refbits = eob_refbits; - if (eob_run == 1) { - next_token->symbol = 0; - } else if (eob_run == 2) { - next_token->symbol = 16; - *next_eobrun++ = 0; - } else if ((eob_run & (eob_run - 1)) == 0) { - next_token->symbol += 16; - next_eobrun[-1] = 0; - } else { - ++next_eobrun[-1]; - } - ++next_token; - if (eob_run == 0x7fff) { - next_eob_token = next_token; - eob_run = eob_refbits = 0; - } - } - sti->num_nonzeros += num_nzeros; - --restarts_to_go; - } - } - sti->num_tokens = next_token - sti->tokens; - sti->restarts[restart_idx++] = sti->num_tokens; - m->next_refinement_token = next_token; - m->next_refinement_bit = next_ref_bit; -} - -void TokenizeScan(j_compress_ptr cinfo, size_t scan_index, int ac_ctx_offset, - ScanTokenInfo* sti) { - const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index]; - if (scan_info->Ss > 0) { - if (scan_info->Ah == 0) { - TokenizeACProgressiveScan(cinfo, scan_index, ac_ctx_offset, sti); - } else { - TokenizeACRefinementScan(cinfo, scan_index, sti); - } - return; - } - - jpeg_comp_master* m = cinfo->master; - size_t restart_interval = sti->restart_interval; - int restarts_to_go = restart_interval; - coeff_t last_dc_coeff[MAX_COMPS_IN_SCAN] = {0}; - - // "Non-interleaved" means color data comes in separate scans, in other words - // each scan can contain only one color component. - const bool is_interleaved = (scan_info->comps_in_scan > 1); - const bool is_progressive = FROM_JXL_BOOL(cinfo->progressive_mode); - const int Ah = scan_info->Ah; - const int Al = scan_info->Al; - HWY_ALIGN constexpr coeff_t kSinkBlock[DCTSIZE2] = {0}; - - size_t restart_idx = 0; - TokenArray* ta = &m->token_arrays[m->cur_token_array]; - sti->token_offset = Ah > 0 ? 0 : m->total_num_tokens + ta->num_tokens; - - if (Ah > 0) { - sti->refbits = Allocate(cinfo, sti->num_blocks, JPOOL_IMAGE); - } else if (cinfo->progressive_mode) { - if (ta->num_tokens + sti->num_blocks > m->num_tokens) { - if (ta->tokens) { - m->total_num_tokens += ta->num_tokens; - ++m->cur_token_array; - ta = &m->token_arrays[m->cur_token_array]; - } - m->num_tokens = sti->num_blocks; - ta->tokens = Allocate(cinfo, m->num_tokens, JPOOL_IMAGE); - m->next_token = ta->tokens; - } - } - - JBLOCKARRAY blocks[MAX_COMPS_IN_SCAN]; - size_t block_idx = 0; - for (size_t mcu_y = 0; mcu_y < sti->MCU_rows_in_scan; ++mcu_y) { - for (int i = 0; i < scan_info->comps_in_scan; ++i) { - int comp_idx = scan_info->component_index[i]; - jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - int n_blocks_y = is_interleaved ? comp->v_samp_factor : 1; - int by0 = mcu_y * n_blocks_y; - int block_rows_left = comp->height_in_blocks - by0; - int max_block_rows = std::min(n_blocks_y, block_rows_left); - blocks[i] = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coeff_buffers[comp_idx], - by0, max_block_rows, FALSE); - } - if (!cinfo->progressive_mode) { - int max_tokens_per_mcu_row = MaxNumTokensPerMCURow(cinfo); - if (ta->num_tokens + max_tokens_per_mcu_row > m->num_tokens) { - if (ta->tokens) { - m->total_num_tokens += ta->num_tokens; - ++m->cur_token_array; - ta = &m->token_arrays[m->cur_token_array]; - } - m->num_tokens = - EstimateNumTokens(cinfo, mcu_y, sti->MCU_rows_in_scan, - m->total_num_tokens, max_tokens_per_mcu_row); - ta->tokens = Allocate(cinfo, m->num_tokens, JPOOL_IMAGE); - m->next_token = ta->tokens; - } - } - for (size_t mcu_x = 0; mcu_x < sti->MCUs_per_row; ++mcu_x) { - // Possibly emit a restart marker. - if (restart_interval > 0 && restarts_to_go == 0) { - restarts_to_go = restart_interval; - memset(last_dc_coeff, 0, sizeof(last_dc_coeff)); - ta->num_tokens = m->next_token - ta->tokens; - sti->restarts[restart_idx++] = - Ah > 0 ? block_idx : m->total_num_tokens + ta->num_tokens; - } - // Encode one MCU - for (int i = 0; i < scan_info->comps_in_scan; ++i) { - int comp_idx = scan_info->component_index[i]; - jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - int n_blocks_y = is_interleaved ? comp->v_samp_factor : 1; - int n_blocks_x = is_interleaved ? comp->h_samp_factor : 1; - for (int iy = 0; iy < n_blocks_y; ++iy) { - for (int ix = 0; ix < n_blocks_x; ++ix) { - size_t block_y = mcu_y * n_blocks_y + iy; - size_t block_x = mcu_x * n_blocks_x + ix; - const coeff_t* block; - if (block_x >= comp->width_in_blocks || - block_y >= comp->height_in_blocks) { - block = kSinkBlock; - } else { - block = &blocks[i][iy][block_x][0]; - } - if (!is_progressive) { - HWY_DYNAMIC_DISPATCH(ComputeTokensSequential) - (block, last_dc_coeff[i], comp_idx, ac_ctx_offset + i, - &m->next_token); - last_dc_coeff[i] = block[0]; - } else { - if (Ah == 0) { - TokenizeProgressiveDC(block, comp_idx, Al, last_dc_coeff + i, - &m->next_token); - } else { - sti->refbits[block_idx] = (block[0] >> Al) & 1; - } - } - ++block_idx; - } - } - } - --restarts_to_go; - } - ta->num_tokens = m->next_token - ta->tokens; - } - JXL_DASSERT(block_idx == sti->num_blocks); - sti->num_tokens = - Ah > 0 ? sti->num_blocks - : m->total_num_tokens + ta->num_tokens - sti->token_offset; - sti->restarts[restart_idx++] = - Ah > 0 ? sti->num_blocks : m->total_num_tokens + ta->num_tokens; - if (Ah == 0 && cinfo->progressive_mode) { - JXL_DASSERT(sti->num_blocks == sti->num_tokens); - } -} - -} // namespace - -void TokenizeJpeg(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - std::vector processed(cinfo->num_scans); - size_t max_refinement_tokens = 0; - size_t num_refinement_bits = 0; - int num_refinement_scans[kMaxComponents][DCTSIZE2] = {}; - int max_num_refinement_scans = 0; - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info* si = &cinfo->scan_info[i]; - ScanTokenInfo* sti = &m->scan_token_info[i]; - if (si->Ss > 0 && si->Ah == 0 && si->Al > 0) { - int offset = m->ac_ctx_offset[i]; - int comp_idx = si->component_index[0]; - TokenizeScan(cinfo, i, offset, sti); - processed[i] = 1; - max_refinement_tokens += sti->num_future_nonzeros; - for (int k = si->Ss; k <= si->Se; ++k) { - num_refinement_scans[comp_idx][k] = si->Al; - } - max_num_refinement_scans = std::max(max_num_refinement_scans, si->Al); - num_refinement_bits += sti->num_nonzeros; - } - if (si->Ss > 0 && si->Ah > 0) { - int comp_idx = si->component_index[0]; - const jpeg_component_info* comp = &cinfo->comp_info[comp_idx]; - size_t num_blocks = comp->width_in_blocks * comp->height_in_blocks; - max_refinement_tokens += (1 + (si->Se - si->Ss) / 16) * num_blocks; - } - } - if (max_refinement_tokens > 0) { - m->next_refinement_token = - Allocate(cinfo, max_refinement_tokens, JPOOL_IMAGE); - } - for (int j = 0; j < max_num_refinement_scans; ++j) { - uint8_t* refinement_bits = - Allocate(cinfo, num_refinement_bits, JPOOL_IMAGE); - m->next_refinement_bit = refinement_bits; - size_t new_refinement_bits = 0; - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info* si = &cinfo->scan_info[i]; - int comp_idx = si->component_index[0]; - ScanTokenInfo* sti = &m->scan_token_info[i]; - if (si->Ss > 0 && si->Ah > 0 && - si->Ah == num_refinement_scans[comp_idx][si->Ss] - j) { - int offset = m->ac_ctx_offset[i]; - TokenizeScan(cinfo, i, offset, sti); - processed[i] = 1; - new_refinement_bits += sti->num_nonzeros; - } - } - JXL_DASSERT(m->next_refinement_bit <= - refinement_bits + num_refinement_bits); - num_refinement_bits += new_refinement_bits; - } - for (int i = 0; i < cinfo->num_scans; ++i) { - if (processed[i]) { - continue; - } - int offset = m->ac_ctx_offset[i]; - TokenizeScan(cinfo, i, offset, &m->scan_token_info[i]); - processed[i] = 1; - } -} - -namespace { - -struct Histogram { - int count[kJpegHuffmanAlphabetSize]; - Histogram() { memset(count, 0, sizeof(count)); } -}; - -void BuildHistograms(j_compress_ptr cinfo, Histogram* histograms) { - jpeg_comp_master* m = cinfo->master; - size_t num_token_arrays = m->cur_token_array + 1; - for (size_t i = 0; i < num_token_arrays; ++i) { - Token* tokens = m->token_arrays[i].tokens; - size_t num_tokens = m->token_arrays[i].num_tokens; - for (size_t j = 0; j < num_tokens; ++j) { - Token t = tokens[j]; - ++histograms[t.context].count[t.symbol]; - } - } - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info& si = cinfo->scan_info[i]; - const ScanTokenInfo& sti = m->scan_token_info[i]; - if (si.Ss > 0 && si.Ah > 0) { - int context = m->ac_ctx_offset[i]; - int* ac_histo = &histograms[context].count[0]; - for (size_t j = 0; j < sti.num_tokens; ++j) { - ++ac_histo[sti.tokens[j].symbol & 253]; - } - } - } -} - -struct JpegClusteredHistograms { - std::vector histograms; - std::vector histogram_indexes; - std::vector slot_ids; -}; - -float HistogramCost(const Histogram& histo) { - std::vector counts(kJpegHuffmanAlphabetSize + 1); - std::vector depths(kJpegHuffmanAlphabetSize + 1); - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - counts[i] = histo.count[i]; - } - counts[kJpegHuffmanAlphabetSize] = 1; - CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength, - depths.data()); - size_t header_bits = (1 + kJpegHuffmanMaxBitLength) * 8; - size_t data_bits = 0; - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - if (depths[i] > 0) { - header_bits += 8; - data_bits += counts[i] * depths[i]; - } - } - return header_bits + data_bits; -} - -void AddHistograms(const Histogram& a, const Histogram& b, Histogram* c) { - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - c->count[i] = a.count[i] + b.count[i]; - } -} - -bool IsEmptyHistogram(const Histogram& histo) { - for (int count : histo.count) { - if (count) return false; - } - return true; -} - -void ClusterJpegHistograms(j_compress_ptr cinfo, const Histogram* histograms, - size_t num, JpegClusteredHistograms* clusters) { - clusters->histogram_indexes.resize(num); - std::vector slot_histograms; - std::vector slot_costs; - for (size_t i = 0; i < num; ++i) { - const Histogram& cur = histograms[i]; - if (IsEmptyHistogram(cur)) { - continue; - } - float best_cost = HistogramCost(cur); - size_t best_slot = slot_histograms.size(); - for (size_t j = 0; j < slot_histograms.size(); ++j) { - size_t prev_idx = slot_histograms[j]; - const Histogram& prev = clusters->histograms[prev_idx]; - Histogram combined; - AddHistograms(prev, cur, &combined); - float combined_cost = HistogramCost(combined); - float cost = combined_cost - slot_costs[j]; - if (cost < best_cost) { - best_cost = cost; - best_slot = j; - } - } - if (best_slot == slot_histograms.size()) { - // Create new histogram. - size_t histogram_index = clusters->histograms.size(); - clusters->histograms.push_back(cur); - clusters->histogram_indexes[i] = histogram_index; - if (best_slot < 4) { - // We have a free slot, so we put the new histogram there. - slot_histograms.push_back(histogram_index); - slot_costs.push_back(best_cost); - } else { - // TODO(szabadka) Find the best histogram to replce. - best_slot = (clusters->slot_ids.back() + 1) % 4; - } - slot_histograms[best_slot] = histogram_index; - slot_costs[best_slot] = best_cost; - clusters->slot_ids.push_back(best_slot); - } else { - // Merge this histogram with a previous one. - size_t histogram_index = slot_histograms[best_slot]; - const Histogram& prev = clusters->histograms[histogram_index]; - AddHistograms(prev, cur, &clusters->histograms[histogram_index]); - clusters->histogram_indexes[i] = histogram_index; - JPEGLI_CHECK(clusters->slot_ids[histogram_index] == best_slot); - slot_costs[best_slot] += best_cost; - } - } -} - -void CopyHuffmanTable(j_compress_ptr cinfo, int index, bool is_dc, - int* inv_slot_map, uint8_t* slot_id_map, - JHUFF_TBL* huffman_tables, size_t* num_huffman_tables) { - const char* type = is_dc ? "DC" : "AC"; - if (index < 0 || index >= NUM_HUFF_TBLS) { - JPEGLI_ERROR("Invalid %s Huffman table index %d", type, index); - } - // Check if we have already copied this Huffman table. - int slot_idx = index + (is_dc ? 0 : NUM_HUFF_TBLS); - if (inv_slot_map[slot_idx] != -1) { - return; - } - inv_slot_map[slot_idx] = *num_huffman_tables; - // Look up and validate Huffman table. - JHUFF_TBL* table = - is_dc ? cinfo->dc_huff_tbl_ptrs[index] : cinfo->ac_huff_tbl_ptrs[index]; - if (table == nullptr) { - JPEGLI_ERROR("Missing %s Huffman table %d", type, index); - } - ValidateHuffmanTable(reinterpret_cast(cinfo), table, is_dc); - // Copy Huffman table to the end of the list and save slot id. - slot_id_map[*num_huffman_tables] = index + (is_dc ? 0 : 0x10); - memcpy(&huffman_tables[*num_huffman_tables], table, sizeof(JHUFF_TBL)); - ++(*num_huffman_tables); -} - -void BuildJpegHuffmanTable(const Histogram& histo, JHUFF_TBL* table) { - std::vector counts(kJpegHuffmanAlphabetSize + 1); - std::vector depths(kJpegHuffmanAlphabetSize + 1); - for (size_t j = 0; j < kJpegHuffmanAlphabetSize; ++j) { - counts[j] = histo.count[j]; - } - counts[kJpegHuffmanAlphabetSize] = 1; - CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength, - depths.data()); - memset(table, 0, sizeof(JHUFF_TBL)); - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - if (depths[i] > 0) { - ++table->bits[depths[i]]; - } - } - int offset[kJpegHuffmanMaxBitLength + 1] = {0}; - for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) { - offset[i] = offset[i - 1] + table->bits[i - 1]; - } - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - if (depths[i] > 0) { - table->huffval[offset[depths[i]]++] = i; - } - } -} - -} // namespace - -void CopyHuffmanTables(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - size_t max_huff_tables = 2 * cinfo->num_components; - // Copy Huffman tables and save slot ids. - m->huffman_tables = Allocate(cinfo, max_huff_tables, JPOOL_IMAGE); - m->slot_id_map = Allocate(cinfo, max_huff_tables, JPOOL_IMAGE); - m->num_huffman_tables = 0; - int inv_slot_map[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - CopyHuffmanTable(cinfo, comp->dc_tbl_no, /*is_dc=*/true, &inv_slot_map[0], - m->slot_id_map, m->huffman_tables, &m->num_huffman_tables); - CopyHuffmanTable(cinfo, comp->ac_tbl_no, /*is_dc=*/false, &inv_slot_map[0], - m->slot_id_map, m->huffman_tables, &m->num_huffman_tables); - } - // Compute context map. - m->context_map = Allocate(cinfo, 8, JPOOL_IMAGE); - memset(m->context_map, 0, 8); - for (int c = 0; c < cinfo->num_components; ++c) { - m->context_map[c] = inv_slot_map[cinfo->comp_info[c].dc_tbl_no]; - } - int ac_ctx = 4; - for (int i = 0; i < cinfo->num_scans; ++i) { - const jpeg_scan_info* si = &cinfo->scan_info[i]; - if (si->Se > 0) { - for (int j = 0; j < si->comps_in_scan; ++j) { - int c = si->component_index[j]; - jpeg_component_info* comp = &cinfo->comp_info[c]; - m->context_map[ac_ctx++] = inv_slot_map[comp->ac_tbl_no + 4]; - } - } - } -} - -void OptimizeHuffmanCodes(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - // Build DC and AC histograms. - std::vector histograms(m->num_contexts); - BuildHistograms(cinfo, histograms.data()); - - // Cluster DC histograms. - JpegClusteredHistograms dc_clusters; - ClusterJpegHistograms(cinfo, histograms.data(), cinfo->num_components, - &dc_clusters); - - // Cluster AC histograms. - JpegClusteredHistograms ac_clusters; - ClusterJpegHistograms(cinfo, histograms.data() + 4, m->num_contexts - 4, - &ac_clusters); - - // Create Huffman tables and slot ids clusters. - size_t num_dc_huff = dc_clusters.histograms.size(); - m->num_huffman_tables = num_dc_huff + ac_clusters.histograms.size(); - m->huffman_tables = - Allocate(cinfo, m->num_huffman_tables, JPOOL_IMAGE); - m->slot_id_map = Allocate(cinfo, m->num_huffman_tables, JPOOL_IMAGE); - for (size_t i = 0; i < m->num_huffman_tables; ++i) { - JHUFF_TBL huff_table = {}; - if (i < dc_clusters.histograms.size()) { - m->slot_id_map[i] = i; - BuildJpegHuffmanTable(dc_clusters.histograms[i], &huff_table); - } else { - m->slot_id_map[i] = 16 + ac_clusters.slot_ids[i - num_dc_huff]; - BuildJpegHuffmanTable(ac_clusters.histograms[i - num_dc_huff], - &huff_table); - } - memcpy(&m->huffman_tables[i], &huff_table, sizeof(huff_table)); - } - - // Create context map from clustered histogram indexes. - m->context_map = Allocate(cinfo, m->num_contexts, JPOOL_IMAGE); - memset(m->context_map, 0, m->num_contexts); - for (size_t i = 0; i < m->num_contexts; ++i) { - if (i < static_cast(cinfo->num_components)) { - m->context_map[i] = dc_clusters.histogram_indexes[i]; - } else if (i >= 4) { - m->context_map[i] = num_dc_huff + ac_clusters.histogram_indexes[i - 4]; - } - } -} - -namespace { - -constexpr uint8_t kNumExtraBits[256] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 7, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // -}; - -void BuildHuffmanCodeTable(const JHUFF_TBL& table, HuffmanCodeTable* code) { - int huff_code[kJpegHuffmanAlphabetSize]; - // +1 for a sentinel element. - uint32_t huff_size[kJpegHuffmanAlphabetSize + 1]; - int p = 0; - for (size_t l = 1; l <= kJpegHuffmanMaxBitLength; ++l) { - int i = table.bits[l]; - while (i--) huff_size[p++] = l; - } - - // Reuse sentinel element. - int last_p = p; - huff_size[last_p] = 0; - - int next_code = 0; - uint32_t si = huff_size[0]; - p = 0; - while (huff_size[p]) { - while ((huff_size[p]) == si) { - huff_code[p++] = next_code; - next_code++; - } - next_code <<= 1; - si++; - } - for (p = 0; p < last_p; p++) { - int i = table.huffval[p]; - int nbits = kNumExtraBits[i]; - code->depth[i] = huff_size[p] + nbits; - code->code[i] = huff_code[p] << nbits; - } -} - -} // namespace - -void InitEntropyCoder(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - m->coding_tables = - Allocate(cinfo, m->num_huffman_tables, JPOOL_IMAGE); - for (size_t i = 0; i < m->num_huffman_tables; ++i) { - BuildHuffmanCodeTable(m->huffman_tables[i], &m->coding_tables[i]); - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/entropy_coding.h b/third_party/jpeg-xl/lib/jpegli/entropy_coding.h deleted file mode 100644 index a552219ec388e..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/entropy_coding.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ENTROPY_CODING_H_ -#define LIB_JPEGLI_ENTROPY_CODING_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -size_t MaxNumTokensPerMCURow(j_compress_ptr cinfo); - -size_t EstimateNumTokens(j_compress_ptr cinfo, size_t mcu_y, size_t ysize_mcus, - size_t num_tokens, size_t max_per_row); - -void TokenizeJpeg(j_compress_ptr cinfo); - -void CopyHuffmanTables(j_compress_ptr cinfo); - -void OptimizeHuffmanCodes(j_compress_ptr cinfo); - -void InitEntropyCoder(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_ENTROPY_CODING_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/error.cc b/third_party/jpeg-xl/lib/jpegli/error.cc deleted file mode 100644 index b3ccab0452e74..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/error.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/error.h" - -#include -#include -#include - -#include - -#include "lib/jpegli/common.h" - -namespace jpegli { - -const char* const kErrorMessageTable[] = { - "Message codes are not supported, error message is in msg_parm.s string", -}; - -bool FormatString(char* buffer, const char* format, ...) { - va_list args; - va_start(args, format); - vsnprintf(buffer, JMSG_STR_PARM_MAX, format, args); // notypo - va_end(args); - return false; -} - -void ExitWithAbort(j_common_ptr cinfo) { - (*cinfo->err->output_message)(cinfo); - jpegli_destroy(cinfo); - exit(EXIT_FAILURE); -} - -void EmitMessage(j_common_ptr cinfo, int msg_level) { - if (msg_level < 0) { - if (cinfo->err->num_warnings <= 5 || cinfo->err->trace_level >= 3) { - (*cinfo->err->output_message)(cinfo); - } - ++cinfo->err->num_warnings; - } else if (cinfo->err->trace_level >= msg_level) { - (*cinfo->err->output_message)(cinfo); - } -} - -void OutputMessage(j_common_ptr cinfo) { - char buffer[JMSG_LENGTH_MAX]; - (*cinfo->err->format_message)(cinfo, buffer); - fprintf(stderr, "%s\n", buffer); -} - -void FormatMessage(j_common_ptr cinfo, char* buffer) { - jpeg_error_mgr* err = cinfo->err; - int code = err->msg_code; - if (code == 0) { - memcpy(buffer, cinfo->err->msg_parm.s, JMSG_STR_PARM_MAX); - } else if (err->addon_message_table != nullptr && - code >= err->first_addon_message && - code <= err->last_addon_message) { - std::string msg(err->addon_message_table[code - err->first_addon_message]); - if (msg.find("%s") != std::string::npos) { - snprintf(buffer, JMSG_LENGTH_MAX, msg.data(), err->msg_parm.s); - } else { - snprintf(buffer, JMSG_LENGTH_MAX, msg.data(), err->msg_parm.i[0], - err->msg_parm.i[1], err->msg_parm.i[2], err->msg_parm.i[3], - err->msg_parm.i[4], err->msg_parm.i[5], err->msg_parm.i[6], - err->msg_parm.i[7]); - } - } else { - snprintf(buffer, JMSG_LENGTH_MAX, "%s", kErrorMessageTable[0]); - } -} - -void ResetErrorManager(j_common_ptr cinfo) { - memset(cinfo->err->msg_parm.s, 0, JMSG_STR_PARM_MAX); - cinfo->err->msg_code = 0; - cinfo->err->num_warnings = 0; -} - -} // namespace jpegli - -struct jpeg_error_mgr* jpegli_std_error(struct jpeg_error_mgr* err) { - err->error_exit = jpegli::ExitWithAbort; - err->emit_message = jpegli::EmitMessage; - err->output_message = jpegli::OutputMessage; - err->format_message = jpegli::FormatMessage; - err->reset_error_mgr = jpegli::ResetErrorManager; - memset(err->msg_parm.s, 0, JMSG_STR_PARM_MAX); - err->trace_level = 0; - err->num_warnings = 0; - // We don't support message codes and message table, but we define one here - // in case the application has a custom format_message and tries to access - // these fields there. - err->msg_code = 0; - err->jpeg_message_table = jpegli::kErrorMessageTable; - err->last_jpeg_message = 0; - err->addon_message_table = nullptr; - err->first_addon_message = 0; - err->last_addon_message = 0; - return err; -} diff --git a/third_party/jpeg-xl/lib/jpegli/error.h b/third_party/jpeg-xl/lib/jpegli/error.h deleted file mode 100644 index fb5dc411a8b6d..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/error.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_ERROR_H_ -#define LIB_JPEGLI_ERROR_H_ - -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jpegli { - -bool FormatString(char* buffer, const char* format, ...); - -} // namespace jpegli - -// `error_exit` should be no-return; but let's add some guarantees on our side. -#define JPEGLI_ERROR(format, ...) \ - jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \ - __LINE__, ##__VA_ARGS__), \ - (*cinfo->err->error_exit)(reinterpret_cast(cinfo)), \ - JXL_CRASH() - -#define JPEGLI_WARN(format, ...) \ - jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \ - __LINE__, ##__VA_ARGS__), \ - (*cinfo->err->emit_message)(reinterpret_cast(cinfo), -1) - -#define JPEGLI_TRACE(level, format, ...) \ - if (cinfo->err->trace_level >= (level)) \ - jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \ - __LINE__, ##__VA_ARGS__), \ - (*cinfo->err->emit_message)(reinterpret_cast(cinfo), \ - (level)) - -#define JPEGLI_CHECK(condition) \ - do { \ - if (!(condition)) { \ - JPEGLI_ERROR("JPEGLI_CHECK: %s", #condition); \ - } \ - } while (0) - -#endif // LIB_JPEGLI_ERROR_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc b/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc deleted file mode 100644 index da85dbcb0b3db..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc +++ /dev/null @@ -1,1284 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" - -namespace jpegli { -namespace { - -TEST(EncoderErrorHandlingTest, MinimalSuccess) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - } - TestImage output; - DecodeWithLibjpeg(CompressParams(), DecompressParams(), nullptr, 0, buffer, - buffer_size, &output); - EXPECT_EQ(1, output.xsize); - EXPECT_EQ(1, output.ysize); - EXPECT_EQ(1, output.components); - EXPECT_EQ(0, output.pixels[0]); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoDestination) { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); -} - -TEST(EncoderErrorHandlingTest, NoImageDimensions) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, ImageTooBig) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 100000; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoInputComponents) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, TooManyInputComponents) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1000; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoSetDefaults) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoStartCompress) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoWriteScanlines) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NoWriteAllScanlines) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 2; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidQuantValue) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.quant_tbl_ptrs[0] = - jpegli_alloc_quant_table(reinterpret_cast(&cinfo)); - for (UINT16& q : cinfo.quant_tbl_ptrs[0]->quantval) { - q = 0; - } - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidQuantTableIndex) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].quant_tbl_no = 3; - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch1) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.num_components = 100; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch2) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.num_components = 2; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch3) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.num_components = 2; - cinfo.comp_info[1].h_samp_factor = cinfo.comp_info[1].v_samp_factor = 1; - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch4) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[1] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch5) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_GRAYSCALE; - jpegli_set_defaults(&cinfo); - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NumberOfComponentsMismatch6) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - cinfo.num_components = 2; - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidColorTransform) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_YCbCr; - jpegli_set_defaults(&cinfo); - cinfo.jpeg_color_space = JCS_RGB; - jpegli_start_compress(&cinfo, TRUE); - JSAMPLE image[3] = {0}; - JSAMPROW row[] = {image}; - jpegli_write_scanlines(&cinfo, row, 1); - jpegli_finish_compress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, DuplicateComponentIds) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].component_id = 0; - cinfo.comp_info[1].component_id = 0; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidComponentIndex) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].component_index = 17; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, ArithmeticCoding) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.arith_code = TRUE; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, CCIR601Sampling) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.CCIR601_sampling = TRUE; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript1) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{1, {0}, 0, 63, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = 0; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript2) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{2, {0, 1}, 0, 63, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript3) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{5, {0}, 0, 63, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript4) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 2; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{2, {0, 0}, 0, 63, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript5) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 2; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{2, {1, 0}, 0, 63, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript6) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{1, {0}, 0, 64, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript7) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = {{1, {0}, 2, 1, 0, 0}}; // - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript8) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 2; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {1, {0}, 0, 63, 0, 0}, {1, {1}, 0, 0, 0, 0}, {1, {1}, 1, 63, 0, 0} // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript9) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {1, {0}, 0, 1, 0, 0}, {1, {0}, 2, 63, 0, 0}, // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript10) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 2; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {2, {0, 1}, 0, 0, 0, 0}, {2, {0, 1}, 1, 63, 0, 0} // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript11) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {1, {0}, 1, 63, 0, 0}, {1, {0}, 0, 0, 0, 0} // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript12) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {1, {0}, 0, 0, 10, 1}, {1, {0}, 0, 0, 1, 0}, {1, {0}, 1, 63, 0, 0} // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, InvalidScanScript13) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - static constexpr jpeg_scan_info kScript[] = { - {1, {0}, 0, 0, 0, 2}, - {1, {0}, 0, 0, 1, 0}, - {1, {0}, 0, 0, 2, 1}, // - {1, {0}, 1, 63, 0, 0} // - }; - cinfo.scan_info = kScript; - cinfo.num_scans = ARRAY_SIZE(kScript); - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, MCUSizeTooBig) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - jpegli_set_progressive_level(&cinfo, 0); - cinfo.comp_info[0].h_samp_factor = 3; - cinfo.comp_info[0].v_samp_factor = 3; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, RestartIntervalTooBig) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 1; - jpegli_set_defaults(&cinfo); - cinfo.restart_interval = 1000000; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, SamplingFactorTooBig) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].h_samp_factor = 5; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -TEST(EncoderErrorHandlingTest, NonIntegralSamplingRatio) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - cinfo.image_width = 1; - cinfo.image_height = 1; - cinfo.input_components = 3; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].h_samp_factor = 3; - cinfo.comp_info[1].h_samp_factor = 2; - jpegli_start_compress(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - if (buffer) free(buffer); -} - -constexpr const char* kAddOnTable[] = {"First message", - "Second message with int param %d", - "Third message with string param %s"}; - -TEST(EncoderErrorHandlingTest, AddOnTableNoParam) { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.err->addon_message_table = kAddOnTable; - cinfo.err->first_addon_message = 10000; - cinfo.err->last_addon_message = 10002; - cinfo.err->msg_code = 10000; - (*cinfo.err->error_exit)(reinterpret_cast(&cinfo)); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); -} - -TEST(EncoderErrorHandlingTest, AddOnTableIntParam) { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.err->addon_message_table = kAddOnTable; - cinfo.err->first_addon_message = 10000; - cinfo.err->last_addon_message = 10002; - cinfo.err->msg_code = 10001; - cinfo.err->msg_parm.i[0] = 17; - (*cinfo.err->error_exit)(reinterpret_cast(&cinfo)); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); -} - -TEST(EncoderErrorHandlingTest, AddOnTableNoStringParam) { - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.err->addon_message_table = kAddOnTable; - cinfo.err->first_addon_message = 10000; - cinfo.err->last_addon_message = 10002; - cinfo.err->msg_code = 10002; - memcpy(cinfo.err->msg_parm.s, "MESSAGE PARAM", 14); - (*cinfo.err->error_exit)(reinterpret_cast(&cinfo)); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_compress(&cinfo); -} - -const uint8_t kCompressed0[] = { - // SOI - 0xff, 0xd8, // - // SOF - 0xff, 0xc0, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, // - 0x01, 0x11, 0x00, // - // DQT - 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, // - 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, // - 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, // - 0x08, 0x0c, 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, // - 0x0e, 0x12, 0x10, 0x0d, 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, // - 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0c, 0x0f, // - 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, // - // DHT - 0xff, 0xc4, 0x00, 0xd2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, // - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // - 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, // - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, // - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, // - 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, // - 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, // - 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, // - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, // - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, // - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, // - 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, // - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, // - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, // - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, // - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, // - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, // - 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, // - 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, // - 0xf9, 0xfa, // - // SOS - 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, // - // entropy coded data - 0xfc, 0xaa, 0xaf, // - // EOI - 0xff, 0xd9, // -}; -const size_t kLen0 = sizeof(kCompressed0); - -const size_t kSOFOffset = 2; -const size_t kDQTOffset = 15; -const size_t kDHTOffset = 84; -const size_t kSOSOffset = 296; - -TEST(DecoderErrorHandlingTest, MinimalSuccess) { - ASSERT_TRUE(kCompressed0[kDQTOffset] == 0xff); - ASSERT_TRUE(kCompressed0[kSOFOffset] == 0xff); - ASSERT_TRUE(kCompressed0[kDHTOffset] == 0xff); - ASSERT_TRUE(kCompressed0[kSOSOffset] == 0xff); - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, kCompressed0, kLen0); - jpegli_read_header(&cinfo, TRUE); - EXPECT_EQ(1, cinfo.image_width); - EXPECT_EQ(1, cinfo.image_height); - jpegli_start_decompress(&cinfo); - JSAMPLE image[1]; - JSAMPROW row[] = {image}; - jpegli_read_scanlines(&cinfo, row, 1); - EXPECT_EQ(0, image[0]); - jpegli_finish_decompress(&cinfo); - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -TEST(DecoderErrorHandlingTest, NoSource) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_read_header(&cinfo, TRUE); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -TEST(DecoderErrorHandlingTest, NoReadHeader) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, kCompressed0, kLen0); - jpegli_start_decompress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -TEST(DecoderErrorHandlingTest, NoStartDecompress) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, kCompressed0, kLen0); - jpegli_read_header(&cinfo, TRUE); - EXPECT_EQ(1, cinfo.image_width); - EXPECT_EQ(1, cinfo.image_height); - JSAMPLE image[1]; - JSAMPROW row[] = {image}; - jpegli_read_scanlines(&cinfo, row, 1); - EXPECT_EQ(0, image[0]); - jpegli_finish_decompress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -TEST(DecoderErrorHandlingTest, NoReadScanlines) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, kCompressed0, kLen0); - jpegli_read_header(&cinfo, TRUE); - EXPECT_EQ(1, cinfo.image_width); - EXPECT_EQ(1, cinfo.image_height); - jpegli_start_decompress(&cinfo); - jpegli_finish_decompress(&cinfo); - return true; - }; - EXPECT_FALSE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); -} - -const size_t kMaxImageWidth = 0xffff; -JSAMPLE kOutputBuffer[MAX_COMPONENTS * kMaxImageWidth]; - -bool ParseCompressed(const std::vector& compressed) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, compressed.data(), compressed.size()); - jpegli_read_header(&cinfo, TRUE); - jpegli_start_decompress(&cinfo); - for (JDIMENSION i = 0; i < cinfo.output_height; ++i) { - JSAMPROW row[] = {kOutputBuffer}; - jpegli_read_scanlines(&cinfo, row, 1); - } - jpegli_finish_decompress(&cinfo); - return true; - }; - bool retval = try_catch_block(); - jpegli_destroy_decompress(&cinfo); - return retval; -} - -TEST(DecoderErrorHandlingTest, NoSOI) { - for (int pos : {0, 1}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[pos] = 0; - EXPECT_FALSE(ParseCompressed(compressed)); - } -} - -TEST(DecoderErrorHandlingTest, InvalidDQT) { - // Bad marker length - for (int diff : {-2, -1, 1, 2}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDQTOffset + 3] += diff; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid table index / precision - for (int val : {0x20, 0x05}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDQTOffset + 4] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // zero quant value - for (int k : {0, 1, 17, 63}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDQTOffset + 5 + k] = 0; - EXPECT_FALSE(ParseCompressed(compressed)); - } -} - -TEST(DecoderErrorHandlingTest, InvalidSOF) { - // Bad marker length - for (int diff : {-2, -1, 1, 2}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + 3] += diff; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // zero width, height or num_components - for (int pos : {6, 8, 9}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + pos] = 0; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid data precision - for (int val : {0, 1, 127}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + 4] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // too many num_components - for (int val : {5, 255}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + 9] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid sampling factors - for (int val : {0x00, 0x01, 0x10, 0x15, 0x51}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + 11] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid quant table index - for (int val : {5, 17}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOFOffset + 12] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } -} - -TEST(DecoderErrorHandlingTest, InvalidDHT) { - // Bad marker length - for (int diff : {-2, -1, 1, 2}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDHTOffset + 3] += diff; - EXPECT_FALSE(ParseCompressed(compressed)); - } - { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDHTOffset + 2] += 17; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid table slot_id - for (int val : {0x05, 0x15, 0x20}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kDHTOffset + 4] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } -} - -TEST(DecoderErrorHandlingTest, InvalidSOS) { - // Invalid comps_in_scan - for (int val : {2, 5, 17}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOSOffset + 4] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid Huffman table indexes - for (int val : {0x05, 0x50, 0x15, 0x51}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOSOffset + 6] = val; - EXPECT_FALSE(ParseCompressed(compressed)); - } - // invalid Ss/Se - for (int pos : {7, 8}) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - compressed[kSOSOffset + pos] = 64; - EXPECT_FALSE(ParseCompressed(compressed)); - } -} - -TEST(DecoderErrorHandlingTest, MutateSingleBytes) { - for (size_t pos = 0; pos < kLen0; ++pos) { - std::vector compressed(kCompressed0, kCompressed0 + kLen0); - for (int val : {0x00, 0x0f, 0xf0, 0xff}) { - compressed[pos] = val; - ParseCompressed(compressed); - } - } -} - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/fuzztest.h b/third_party/jpeg-xl/lib/jpegli/fuzztest.h deleted file mode 100644 index d5cde97713e67..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/fuzztest.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_FUZZTEST_H_ -#define LIB_JPEGLI_FUZZTEST_H_ - -#include "lib/jxl/base/compiler_specific.h" - -#if !defined(FUZZ_TEST) -struct FuzzTestSink { - template - FuzzTestSink WithSeeds(F /*f*/) { - return *this; - } -}; -#define FUZZ_TEST(A, B) \ - const JXL_MAYBE_UNUSED FuzzTestSink unused##A##B = FuzzTestSink() -#endif - -#endif // LIB_JPEGLI_FUZZTEST_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/huffman.cc b/third_party/jpeg-xl/lib/jpegli/huffman.cc deleted file mode 100644 index 6b068ab64f6ad..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/huffman.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/huffman.h" - -#include -#include - -#include "lib/jpegli/common.h" -#include "lib/jpegli/error.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { - -// Returns the table width of the next 2nd level table, count is the histogram -// of bit lengths for the remaining symbols, len is the code length of the next -// processed symbol. -static inline int NextTableBitSize(const int* count, int len) { - int left = 1 << (len - kJpegHuffmanRootTableBits); - while (len < static_cast(kJpegHuffmanMaxBitLength)) { - left -= count[len]; - if (left <= 0) break; - ++len; - left <<= 1; - } - return len - kJpegHuffmanRootTableBits; -} - -void BuildJpegHuffmanTable(const uint32_t* count, const uint32_t* symbols, - HuffmanTableEntry* lut) { - HuffmanTableEntry code; // current table entry - HuffmanTableEntry* table; // next available space in table - int len; // current code length - int idx; // symbol index - int key; // prefix code - int reps; // number of replicate key values in current table - int low; // low bits for current root entry - int table_bits; // key length of current table - int table_size; // size of current table - - // Make a local copy of the input bit length histogram. - int tmp_count[kJpegHuffmanMaxBitLength + 1] = {0}; - int total_count = 0; - for (len = 1; len <= static_cast(kJpegHuffmanMaxBitLength); ++len) { - tmp_count[len] = count[len]; - total_count += tmp_count[len]; - } - - table = lut; - table_bits = kJpegHuffmanRootTableBits; - table_size = 1 << table_bits; - - // Special case code with only one value. - if (total_count == 1) { - code.bits = 0; - code.value = symbols[0]; - for (key = 0; key < table_size; ++key) { - table[key] = code; - } - return; - } - - // Fill in root table. - key = 0; - idx = 0; - for (len = 1; len <= kJpegHuffmanRootTableBits; ++len) { - for (; tmp_count[len] > 0; --tmp_count[len]) { - code.bits = len; - code.value = symbols[idx++]; - reps = 1 << (kJpegHuffmanRootTableBits - len); - while (reps--) { - table[key++] = code; - } - } - } - - // Fill in 2nd level tables and add pointers to root table. - table += table_size; - table_size = 0; - low = 0; - for (len = kJpegHuffmanRootTableBits + 1; - len <= static_cast(kJpegHuffmanMaxBitLength); ++len) { - for (; tmp_count[len] > 0; --tmp_count[len]) { - // Start a new sub-table if the previous one is full. - if (low >= table_size) { - table += table_size; - table_bits = NextTableBitSize(tmp_count, len); - table_size = 1 << table_bits; - low = 0; - lut[key].bits = table_bits + kJpegHuffmanRootTableBits; - lut[key].value = (table - lut) - key; - ++key; - } - code.bits = len - kJpegHuffmanRootTableBits; - code.value = symbols[idx++]; - reps = 1 << (table_bits - code.bits); - while (reps--) { - table[low++] = code; - } - } - } -} - -// A node of a Huffman tree. -struct HuffmanTree { - HuffmanTree(uint32_t count, int16_t left, int16_t right) - : total_count(count), index_left(left), index_right_or_value(right) {} - uint32_t total_count; - int16_t index_left; - int16_t index_right_or_value; -}; - -void SetDepth(const HuffmanTree& p, HuffmanTree* pool, uint8_t* depth, - uint8_t level) { - if (p.index_left >= 0) { - ++level; - SetDepth(pool[p.index_left], pool, depth, level); - SetDepth(pool[p.index_right_or_value], pool, depth, level); - } else { - depth[p.index_right_or_value] = level; - } -} - -// Sort the root nodes, least popular first. -static JXL_INLINE bool Compare(const HuffmanTree& v0, const HuffmanTree& v1) { - return v0.total_count < v1.total_count; -} - -// This function will create a Huffman tree. -// -// The catch here is that the tree cannot be arbitrarily deep. -// Brotli specifies a maximum depth of 15 bits for "code trees" -// and 7 bits for "code length code trees." -// -// count_limit is the value that is to be faked as the minimum value -// and this minimum value is raised until the tree matches the -// maximum length requirement. -// -// This algorithm is not of excellent performance for very long data blocks, -// especially when population counts are longer than 2**tree_limit, but -// we are not planning to use this with extremely long blocks. -// -// See http://en.wikipedia.org/wiki/Huffman_coding -void CreateHuffmanTree(const uint32_t* data, const size_t length, - const int tree_limit, uint8_t* depth) { - // For block sizes below 64 kB, we never need to do a second iteration - // of this loop. Probably all of our block sizes will be smaller than - // that, so this loop is mostly of academic interest. If we actually - // would need this, we would be better off with the Katajainen algorithm. - for (uint32_t count_limit = 1;; count_limit *= 2) { - std::vector tree; - tree.reserve(2 * length + 1); - - for (size_t i = length; i != 0;) { - --i; - if (data[i]) { - const uint32_t count = std::max(data[i], count_limit - 1); - tree.emplace_back(count, -1, static_cast(i)); - } - } - - const size_t n = tree.size(); - if (n == 1) { - // Fake value; will be fixed on upper level. - depth[tree[0].index_right_or_value] = 1; - break; - } - - std::stable_sort(tree.begin(), tree.end(), Compare); - - // The nodes are: - // [0, n): the sorted leaf nodes that we start with. - // [n]: we add a sentinel here. - // [n + 1, 2n): new parent nodes are added here, starting from - // (n+1). These are naturally in ascending order. - // [2n]: we add a sentinel at the end as well. - // There will be (2n+1) elements at the end. - const HuffmanTree sentinel(std::numeric_limits::max(), -1, -1); - tree.push_back(sentinel); - tree.push_back(sentinel); - - size_t i = 0; // Points to the next leaf node. - size_t j = n + 1; // Points to the next non-leaf node. - for (size_t k = n - 1; k != 0; --k) { - size_t left; - size_t right; - if (tree[i].total_count <= tree[j].total_count) { - left = i; - ++i; - } else { - left = j; - ++j; - } - if (tree[i].total_count <= tree[j].total_count) { - right = i; - ++i; - } else { - right = j; - ++j; - } - - // The sentinel node becomes the parent node. - size_t j_end = tree.size() - 1; - tree[j_end].total_count = - tree[left].total_count + tree[right].total_count; - tree[j_end].index_left = static_cast(left); - tree[j_end].index_right_or_value = static_cast(right); - - // Add back the last sentinel node. - tree.push_back(sentinel); - } - JXL_DASSERT(tree.size() == 2 * n + 1); - SetDepth(tree[2 * n - 1], tree.data(), depth, 0); - - // We need to pack the Huffman tree in tree_limit bits. - // If this was not successful, add fake entities to the lowest values - // and retry. - if (*std::max_element(&depth[0], &depth[length]) <= tree_limit) { - break; - } - } -} - -void ValidateHuffmanTable(j_common_ptr cinfo, const JHUFF_TBL* table, - bool is_dc) { - size_t total_symbols = 0; - size_t total_p = 0; - size_t max_depth = 0; - for (size_t d = 1; d <= kJpegHuffmanMaxBitLength; ++d) { - uint8_t count = table->bits[d]; - if (count) { - total_symbols += count; - total_p += (1u << (kJpegHuffmanMaxBitLength - d)) * count; - max_depth = d; - } - } - total_p += 1u << (kJpegHuffmanMaxBitLength - max_depth); // sentinel symbol - if (total_symbols == 0) { - JPEGLI_ERROR("Empty Huffman table"); - } - if (total_symbols > kJpegHuffmanAlphabetSize) { - JPEGLI_ERROR("Too many symbols in Huffman table"); - } - if (total_p != (1u << kJpegHuffmanMaxBitLength)) { - JPEGLI_ERROR("Invalid bit length distribution"); - } - uint8_t symbol_seen[kJpegHuffmanAlphabetSize] = {}; - for (size_t i = 0; i < total_symbols; ++i) { - uint8_t symbol = table->huffval[i]; - if (symbol_seen[symbol]) { - JPEGLI_ERROR("Duplicate symbol %d in Huffman table", symbol); - } - symbol_seen[symbol] = 1; - } -} - -void AddStandardHuffmanTables(j_common_ptr cinfo, bool is_dc) { - // Huffman tables from the JPEG standard. - static constexpr JHUFF_TBL kStandardDCTables[2] = { - // DC luma - {{0, 0, 1, 5, 1, 1, 1, 1, 1, 1}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - FALSE}, - // DC chroma - {{0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - FALSE}}; - static constexpr JHUFF_TBL kStandardACTables[2] = { - // AC luma - {{0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125}, - {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, - 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, - 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, - 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}, - FALSE}, - // AC chroma - {{0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119}, - {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, - 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, - 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}, - FALSE}}; - const JHUFF_TBL* std_tables = is_dc ? kStandardDCTables : kStandardACTables; - JHUFF_TBL** tables; - if (cinfo->is_decompressor) { - j_decompress_ptr cinfo_d = reinterpret_cast(cinfo); - tables = is_dc ? cinfo_d->dc_huff_tbl_ptrs : cinfo_d->ac_huff_tbl_ptrs; - } else { - j_compress_ptr cinfo_c = reinterpret_cast(cinfo); - tables = is_dc ? cinfo_c->dc_huff_tbl_ptrs : cinfo_c->ac_huff_tbl_ptrs; - } - for (int i = 0; i < 2; ++i) { - if (tables[i] == nullptr) { - tables[i] = jpegli_alloc_huff_table(cinfo); - memcpy(tables[i], &std_tables[i], sizeof(JHUFF_TBL)); - ValidateHuffmanTable(cinfo, tables[i], is_dc); - } - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/huffman.h b/third_party/jpeg-xl/lib/jpegli/huffman.h deleted file mode 100644 index 99549668cffe7..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/huffman.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_HUFFMAN_H_ -#define LIB_JPEGLI_HUFFMAN_H_ - -#include -#include - -#include "lib/jpegli/common_internal.h" - -namespace jpegli { - -constexpr int kJpegHuffmanRootTableBits = 8; -// Maximum huffman lookup table size. -// Requirements: alphabet of 257 symbols (256 + 1 special symbol for the all 1s -// code) and max bit length 16, the root table has 8 bits. -// zlib/examples/enough.c works with an assumption that Huffman code is -// "complete". Input JPEGs might have this assumption broken, hence the -// following sum is used as estimate: -// + number of 1-st level cells -// + number of symbols -// + asymptotic amount of repeated 2nd level cells -// The third number is 1 + 3 + ... + 255 i.e. it is assumed that sub-table of -// each "size" might be almost completely be filled with repetitions. -// Total sum is slightly less than 1024,... -constexpr int kJpegHuffmanLutSize = 1024; - -struct HuffmanTableEntry { - uint8_t bits; // number of bits used for this symbol - uint16_t value; // symbol value or table offset -}; - -void BuildJpegHuffmanTable(const uint32_t* count, const uint32_t* symbols, - HuffmanTableEntry* lut); - -// This function will create a Huffman tree. -// -// The (data,length) contains the population counts. -// The tree_limit is the maximum bit depth of the Huffman codes. -// -// The depth contains the tree, i.e., how many bits are used for -// the symbol. -// -// See http://en.wikipedia.org/wiki/Huffman_coding -void CreateHuffmanTree(const uint32_t* data, size_t length, int tree_limit, - uint8_t* depth); - -void ValidateHuffmanTable(j_common_ptr cinfo, const JHUFF_TBL* table, - bool is_dc); - -void AddStandardHuffmanTables(j_common_ptr cinfo, bool is_dc); - -} // namespace jpegli - -#endif // LIB_JPEGLI_HUFFMAN_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/idct.cc b/third_party/jpeg-xl/lib/jpegli/idct.cc deleted file mode 100644 index 665a00d826de0..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/idct.cc +++ /dev/null @@ -1,703 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/idct.h" - -#include - -#include "lib/jpegli/decode_internal.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/idct.cc" -#include -#include - -#include "lib/jpegli/transpose-inl.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Abs; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::Gt; -using hwy::HWY_NAMESPACE::IfThenElseZero; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::NegMulAdd; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::Sub; -using hwy::HWY_NAMESPACE::Vec; -using hwy::HWY_NAMESPACE::Xor; - -using D = HWY_FULL(float); -using DI = HWY_FULL(int32_t); -constexpr D d; -constexpr DI di; - -using D8 = HWY_CAPPED(float, 8); -constexpr D8 d8; - -void DequantBlock(const int16_t* JXL_RESTRICT qblock, - const float* JXL_RESTRICT dequant, - const float* JXL_RESTRICT biases, float* JXL_RESTRICT block) { - for (size_t k = 0; k < 64; k += Lanes(d)) { - const auto mul = Load(d, dequant + k); - const auto bias = Load(d, biases + k); - const Rebind di16; - const Vec quant_i = PromoteTo(di, Load(di16, qblock + k)); - const Rebind df; - const auto quant = ConvertTo(df, quant_i); - const auto abs_quant = Abs(quant); - const auto not_0 = Gt(abs_quant, Zero(df)); - const auto sign_quant = Xor(quant, abs_quant); - const auto biased_quant = Sub(quant, Xor(bias, sign_quant)); - const auto dequant = IfThenElseZero(not_0, Mul(biased_quant, mul)); - Store(dequant, d, block + k); - } -} - -template -void ForwardEvenOdd(const float* JXL_RESTRICT a_in, size_t a_in_stride, - float* JXL_RESTRICT a_out) { - for (size_t i = 0; i < N / 2; i++) { - auto in1 = LoadU(d8, a_in + 2 * i * a_in_stride); - Store(in1, d8, a_out + i * 8); - } - for (size_t i = N / 2; i < N; i++) { - auto in1 = LoadU(d8, a_in + (2 * (i - N / 2) + 1) * a_in_stride); - Store(in1, d8, a_out + i * 8); - } -} - -template -void BTranspose(float* JXL_RESTRICT coeff) { - for (size_t i = N - 1; i > 0; i--) { - auto in1 = Load(d8, coeff + i * 8); - auto in2 = Load(d8, coeff + (i - 1) * 8); - Store(Add(in1, in2), d8, coeff + i * 8); - } - constexpr float kSqrt2 = 1.41421356237f; - auto sqrt2 = Set(d8, kSqrt2); - auto in1 = Load(d8, coeff); - Store(Mul(in1, sqrt2), d8, coeff); -} - -// Constants for DCT implementation. Generated by the following snippet: -// for i in range(N // 2): -// print(1.0 / (2 * math.cos((i + 0.5) * math.pi / N)), end=", ") -template -struct WcMultipliers; - -template <> -struct WcMultipliers<4> { - static constexpr float kMultipliers[] = { - 0.541196100146197, - 1.3065629648763764, - }; -}; - -template <> -struct WcMultipliers<8> { - static constexpr float kMultipliers[] = { - 0.5097955791041592, - 0.6013448869350453, - 0.8999762231364156, - 2.5629154477415055, - }; -}; - -#if JXL_CXX_LANG < JXL_CXX_17 -constexpr float WcMultipliers<4>::kMultipliers[]; -constexpr float WcMultipliers<8>::kMultipliers[]; -#endif - -template -void MultiplyAndAdd(const float* JXL_RESTRICT coeff, float* JXL_RESTRICT out, - size_t out_stride) { - for (size_t i = 0; i < N / 2; i++) { - auto mul = Set(d8, WcMultipliers::kMultipliers[i]); - auto in1 = Load(d8, coeff + i * 8); - auto in2 = Load(d8, coeff + (N / 2 + i) * 8); - auto out1 = MulAdd(mul, in2, in1); - auto out2 = NegMulAdd(mul, in2, in1); - StoreU(out1, d8, out + i * out_stride); - StoreU(out2, d8, out + (N - i - 1) * out_stride); - } -} - -template -struct IDCT1DImpl; - -template <> -struct IDCT1DImpl<1> { - JXL_INLINE void operator()(const float* from, size_t from_stride, float* to, - size_t to_stride) { - StoreU(LoadU(d8, from), d8, to); - } -}; - -template <> -struct IDCT1DImpl<2> { - JXL_INLINE void operator()(const float* from, size_t from_stride, float* to, - size_t to_stride) { - JXL_DASSERT(from_stride >= 8); - JXL_DASSERT(to_stride >= 8); - auto in1 = LoadU(d8, from); - auto in2 = LoadU(d8, from + from_stride); - StoreU(Add(in1, in2), d8, to); - StoreU(Sub(in1, in2), d8, to + to_stride); - } -}; - -template -struct IDCT1DImpl { - void operator()(const float* from, size_t from_stride, float* to, - size_t to_stride) { - JXL_DASSERT(from_stride >= 8); - JXL_DASSERT(to_stride >= 8); - HWY_ALIGN float tmp[64]; - ForwardEvenOdd(from, from_stride, tmp); - IDCT1DImpl()(tmp, 8, tmp, 8); - BTranspose(tmp + N * 4); - IDCT1DImpl()(tmp + N * 4, 8, tmp + N * 4, 8); - MultiplyAndAdd(tmp, to, to_stride); - } -}; - -template -void IDCT1D(float* JXL_RESTRICT from, float* JXL_RESTRICT output, - size_t output_stride) { - for (size_t i = 0; i < 8; i += Lanes(d8)) { - IDCT1DImpl()(from + i, 8, output + i, output_stride); - } -} - -void ComputeScaledIDCT(float* JXL_RESTRICT block0, float* JXL_RESTRICT block1, - float* JXL_RESTRICT output, size_t output_stride) { - Transpose8x8Block(block0, block1); - IDCT1D<8>(block1, block0, 8); - Transpose8x8Block(block0, block1); - IDCT1D<8>(block1, output, output_stride); -} - -void InverseTransformBlock8x8(const int16_t* JXL_RESTRICT qblock, - const float* JXL_RESTRICT dequant, - const float* JXL_RESTRICT biases, - float* JXL_RESTRICT scratch_space, - float* JXL_RESTRICT output, size_t output_stride, - size_t dctsize) { - float* JXL_RESTRICT block0 = scratch_space; - float* JXL_RESTRICT block1 = scratch_space + DCTSIZE2; - DequantBlock(qblock, dequant, biases, block0); - ComputeScaledIDCT(block0, block1, output, output_stride); -} - -// Computes the N-point IDCT of in[], and stores the result in out[]. The in[] -// array is at most 8 values long, values in[8:N-1] are assumed to be 0. -void Compute1dIDCT(const float* in, float* out, size_t N) { - switch (N) { - case 3: { - static constexpr float kC3[3] = { - 1.414213562373, - 1.224744871392, - 0.707106781187, - }; - float even0 = in[0] + kC3[2] * in[2]; - float even1 = in[0] - kC3[0] * in[2]; - float odd0 = kC3[1] * in[1]; - out[0] = even0 + odd0; - out[2] = even0 - odd0; - out[1] = even1; - break; - } - case 5: { - static constexpr float kC5[5] = { - 1.414213562373, 1.344997023928, 1.144122805635, - 0.831253875555, 0.437016024449, - }; - float even0 = in[0] + kC5[2] * in[2] + kC5[4] * in[4]; - float even1 = in[0] - kC5[4] * in[2] - kC5[2] * in[4]; - float even2 = in[0] - kC5[0] * in[2] + kC5[0] * in[4]; - float odd0 = kC5[1] * in[1] + kC5[3] * in[3]; - float odd1 = kC5[3] * in[1] - kC5[1] * in[3]; - out[0] = even0 + odd0; - out[4] = even0 - odd0; - out[1] = even1 + odd1; - out[3] = even1 - odd1; - out[2] = even2; - break; - } - case 6: { - static constexpr float kC6[6] = { - 1.414213562373, 1.366025403784, 1.224744871392, - 1.000000000000, 0.707106781187, 0.366025403784, - }; - float even0 = in[0] + kC6[2] * in[2] + kC6[4] * in[4]; - float even1 = in[0] - kC6[0] * in[4]; - float even2 = in[0] - kC6[2] * in[2] + kC6[4] * in[4]; - float odd0 = kC6[1] * in[1] + kC6[3] * in[3] + kC6[5] * in[5]; - float odd1 = kC6[3] * in[1] - kC6[3] * in[3] - kC6[3] * in[5]; - float odd2 = kC6[5] * in[1] - kC6[3] * in[3] + kC6[1] * in[5]; - out[0] = even0 + odd0; - out[5] = even0 - odd0; - out[1] = even1 + odd1; - out[4] = even1 - odd1; - out[2] = even2 + odd2; - out[3] = even2 - odd2; - break; - } - case 7: { - static constexpr float kC7[7] = { - 1.414213562373, 1.378756275744, 1.274162392264, 1.105676685997, - 0.881747733790, 0.613604268353, 0.314692122713, - }; - float even0 = in[0] + kC7[2] * in[2] + kC7[4] * in[4] + kC7[6] * in[6]; - float even1 = in[0] + kC7[6] * in[2] - kC7[2] * in[4] - kC7[4] * in[6]; - float even2 = in[0] - kC7[4] * in[2] - kC7[6] * in[4] + kC7[2] * in[6]; - float even3 = in[0] - kC7[0] * in[2] + kC7[0] * in[4] - kC7[0] * in[6]; - float odd0 = kC7[1] * in[1] + kC7[3] * in[3] + kC7[5] * in[5]; - float odd1 = kC7[3] * in[1] - kC7[5] * in[3] - kC7[1] * in[5]; - float odd2 = kC7[5] * in[1] - kC7[1] * in[3] + kC7[3] * in[5]; - out[0] = even0 + odd0; - out[6] = even0 - odd0; - out[1] = even1 + odd1; - out[5] = even1 - odd1; - out[2] = even2 + odd2; - out[4] = even2 - odd2; - out[3] = even3; - break; - } - case 9: { - static constexpr float kC9[9] = { - 1.414213562373, 1.392728480640, 1.328926048777, - 1.224744871392, 1.083350440839, 0.909038955344, - 0.707106781187, 0.483689525296, 0.245575607938, - }; - float even0 = in[0] + kC9[2] * in[2] + kC9[4] * in[4] + kC9[6] * in[6]; - float even1 = in[0] + kC9[6] * in[2] - kC9[6] * in[4] - kC9[0] * in[6]; - float even2 = in[0] - kC9[8] * in[2] - kC9[2] * in[4] + kC9[6] * in[6]; - float even3 = in[0] - kC9[4] * in[2] + kC9[8] * in[4] + kC9[6] * in[6]; - float even4 = in[0] - kC9[0] * in[2] + kC9[0] * in[4] - kC9[0] * in[6]; - float odd0 = - kC9[1] * in[1] + kC9[3] * in[3] + kC9[5] * in[5] + kC9[7] * in[7]; - float odd1 = kC9[3] * in[1] - kC9[3] * in[5] - kC9[3] * in[7]; - float odd2 = - kC9[5] * in[1] - kC9[3] * in[3] - kC9[7] * in[5] + kC9[1] * in[7]; - float odd3 = - kC9[7] * in[1] - kC9[3] * in[3] + kC9[1] * in[5] - kC9[5] * in[7]; - out[0] = even0 + odd0; - out[8] = even0 - odd0; - out[1] = even1 + odd1; - out[7] = even1 - odd1; - out[2] = even2 + odd2; - out[6] = even2 - odd2; - out[3] = even3 + odd3; - out[5] = even3 - odd3; - out[4] = even4; - break; - } - case 10: { - static constexpr float kC10[10] = { - 1.414213562373, 1.396802246667, 1.344997023928, 1.260073510670, - 1.144122805635, 1.000000000000, 0.831253875555, 0.642039521920, - 0.437016024449, 0.221231742082, - }; - float even0 = in[0] + kC10[2] * in[2] + kC10[4] * in[4] + kC10[6] * in[6]; - float even1 = in[0] + kC10[6] * in[2] - kC10[8] * in[4] - kC10[2] * in[6]; - float even2 = in[0] - kC10[0] * in[4]; - float even3 = in[0] - kC10[6] * in[2] - kC10[8] * in[4] + kC10[2] * in[6]; - float even4 = in[0] - kC10[2] * in[2] + kC10[4] * in[4] - kC10[6] * in[6]; - float odd0 = - kC10[1] * in[1] + kC10[3] * in[3] + kC10[5] * in[5] + kC10[7] * in[7]; - float odd1 = - kC10[3] * in[1] + kC10[9] * in[3] - kC10[5] * in[5] - kC10[1] * in[7]; - float odd2 = - kC10[5] * in[1] - kC10[5] * in[3] - kC10[5] * in[5] + kC10[5] * in[7]; - float odd3 = - kC10[7] * in[1] - kC10[1] * in[3] + kC10[5] * in[5] + kC10[9] * in[7]; - float odd4 = - kC10[9] * in[1] - kC10[7] * in[3] + kC10[5] * in[5] - kC10[3] * in[7]; - out[0] = even0 + odd0; - out[9] = even0 - odd0; - out[1] = even1 + odd1; - out[8] = even1 - odd1; - out[2] = even2 + odd2; - out[7] = even2 - odd2; - out[3] = even3 + odd3; - out[6] = even3 - odd3; - out[4] = even4 + odd4; - out[5] = even4 - odd4; - break; - } - case 11: { - static constexpr float kC11[11] = { - 1.414213562373, 1.399818907436, 1.356927976287, 1.286413904599, - 1.189712155524, 1.068791297809, 0.926112931411, 0.764581576418, - 0.587485545401, 0.398430002847, 0.201263574413, - }; - float even0 = in[0] + kC11[2] * in[2] + kC11[4] * in[4] + kC11[6] * in[6]; - float even1 = - in[0] + kC11[6] * in[2] - kC11[10] * in[4] - kC11[4] * in[6]; - float even2 = - in[0] + kC11[10] * in[2] - kC11[2] * in[4] - kC11[8] * in[6]; - float even3 = in[0] - kC11[8] * in[2] - kC11[6] * in[4] + kC11[2] * in[6]; - float even4 = - in[0] - kC11[4] * in[2] + kC11[8] * in[4] + kC11[10] * in[6]; - float even5 = in[0] - kC11[0] * in[2] + kC11[0] * in[4] - kC11[0] * in[6]; - float odd0 = - kC11[1] * in[1] + kC11[3] * in[3] + kC11[5] * in[5] + kC11[7] * in[7]; - float odd1 = - kC11[3] * in[1] + kC11[9] * in[3] - kC11[7] * in[5] - kC11[1] * in[7]; - float odd2 = - kC11[5] * in[1] - kC11[7] * in[3] - kC11[3] * in[5] + kC11[9] * in[7]; - float odd3 = - kC11[7] * in[1] - kC11[1] * in[3] + kC11[9] * in[5] + kC11[5] * in[7]; - float odd4 = - kC11[9] * in[1] - kC11[5] * in[3] + kC11[1] * in[5] - kC11[3] * in[7]; - out[0] = even0 + odd0; - out[10] = even0 - odd0; - out[1] = even1 + odd1; - out[9] = even1 - odd1; - out[2] = even2 + odd2; - out[8] = even2 - odd2; - out[3] = even3 + odd3; - out[7] = even3 - odd3; - out[4] = even4 + odd4; - out[6] = even4 - odd4; - out[5] = even5; - break; - } - case 12: { - static constexpr float kC12[12] = { - 1.414213562373, 1.402114769300, 1.366025403784, 1.306562964876, - 1.224744871392, 1.121971053594, 1.000000000000, 0.860918669154, - 0.707106781187, 0.541196100146, 0.366025403784, 0.184591911283, - }; - float even0 = in[0] + kC12[2] * in[2] + kC12[4] * in[4] + kC12[6] * in[6]; - float even1 = in[0] + kC12[6] * in[2] - kC12[6] * in[6]; - float even2 = - in[0] + kC12[10] * in[2] - kC12[4] * in[4] - kC12[6] * in[6]; - float even3 = - in[0] - kC12[10] * in[2] - kC12[4] * in[4] + kC12[6] * in[6]; - float even4 = in[0] - kC12[6] * in[2] + kC12[6] * in[6]; - float even5 = in[0] - kC12[2] * in[2] + kC12[4] * in[4] - kC12[6] * in[6]; - float odd0 = - kC12[1] * in[1] + kC12[3] * in[3] + kC12[5] * in[5] + kC12[7] * in[7]; - float odd1 = - kC12[3] * in[1] + kC12[9] * in[3] - kC12[9] * in[5] - kC12[3] * in[7]; - float odd2 = kC12[5] * in[1] - kC12[9] * in[3] - kC12[1] * in[5] - - kC12[11] * in[7]; - float odd3 = kC12[7] * in[1] - kC12[3] * in[3] - kC12[11] * in[5] + - kC12[1] * in[7]; - float odd4 = - kC12[9] * in[1] - kC12[3] * in[3] + kC12[3] * in[5] - kC12[9] * in[7]; - float odd5 = kC12[11] * in[1] - kC12[9] * in[3] + kC12[7] * in[5] - - kC12[5] * in[7]; - out[0] = even0 + odd0; - out[11] = even0 - odd0; - out[1] = even1 + odd1; - out[10] = even1 - odd1; - out[2] = even2 + odd2; - out[9] = even2 - odd2; - out[3] = even3 + odd3; - out[8] = even3 - odd3; - out[4] = even4 + odd4; - out[7] = even4 - odd4; - out[5] = even5 + odd5; - out[6] = even5 - odd5; - break; - } - case 13: { - static constexpr float kC13[13] = { - 1.414213562373, 1.403902353238, 1.373119086479, 1.322312651445, - 1.252223920364, 1.163874944761, 1.058554051646, 0.937797056801, - 0.803364869133, 0.657217812653, 0.501487040539, 0.338443458124, - 0.170464607981, - }; - float even0 = in[0] + kC13[2] * in[2] + kC13[4] * in[4] + kC13[6] * in[6]; - float even1 = - in[0] + kC13[6] * in[2] + kC13[12] * in[4] - kC13[8] * in[6]; - float even2 = - in[0] + kC13[10] * in[2] - kC13[6] * in[4] - kC13[4] * in[6]; - float even3 = - in[0] - kC13[12] * in[2] - kC13[2] * in[4] + kC13[10] * in[6]; - float even4 = - in[0] - kC13[8] * in[2] - kC13[10] * in[4] + kC13[2] * in[6]; - float even5 = - in[0] - kC13[4] * in[2] + kC13[8] * in[4] - kC13[12] * in[6]; - float even6 = in[0] - kC13[0] * in[2] + kC13[0] * in[4] - kC13[0] * in[6]; - float odd0 = - kC13[1] * in[1] + kC13[3] * in[3] + kC13[5] * in[5] + kC13[7] * in[7]; - float odd1 = kC13[3] * in[1] + kC13[9] * in[3] - kC13[11] * in[5] - - kC13[5] * in[7]; - float odd2 = kC13[5] * in[1] - kC13[11] * in[3] - kC13[1] * in[5] - - kC13[9] * in[7]; - float odd3 = - kC13[7] * in[1] - kC13[5] * in[3] - kC13[9] * in[5] + kC13[3] * in[7]; - float odd4 = kC13[9] * in[1] - kC13[1] * in[3] + kC13[7] * in[5] + - kC13[11] * in[7]; - float odd5 = kC13[11] * in[1] - kC13[7] * in[3] + kC13[3] * in[5] - - kC13[1] * in[7]; - out[0] = even0 + odd0; - out[12] = even0 - odd0; - out[1] = even1 + odd1; - out[11] = even1 - odd1; - out[2] = even2 + odd2; - out[10] = even2 - odd2; - out[3] = even3 + odd3; - out[9] = even3 - odd3; - out[4] = even4 + odd4; - out[8] = even4 - odd4; - out[5] = even5 + odd5; - out[7] = even5 - odd5; - out[6] = even6; - break; - } - case 14: { - static constexpr float kC14[14] = { - 1.414213562373, 1.405321284327, 1.378756275744, 1.334852607020, - 1.274162392264, 1.197448846138, 1.105676685997, 1.000000000000, - 0.881747733790, 0.752406978226, 0.613604268353, 0.467085128785, - 0.314692122713, 0.158341680609, - }; - float even0 = in[0] + kC14[2] * in[2] + kC14[4] * in[4] + kC14[6] * in[6]; - float even1 = - in[0] + kC14[6] * in[2] + kC14[12] * in[4] - kC14[10] * in[6]; - float even2 = - in[0] + kC14[10] * in[2] - kC14[8] * in[4] - kC14[2] * in[6]; - float even3 = in[0] - kC14[0] * in[4]; - float even4 = - in[0] - kC14[10] * in[2] - kC14[8] * in[4] + kC14[2] * in[6]; - float even5 = - in[0] - kC14[6] * in[2] + kC14[12] * in[4] + kC14[10] * in[6]; - float even6 = in[0] - kC14[2] * in[2] + kC14[4] * in[4] - kC14[6] * in[6]; - float odd0 = - kC14[1] * in[1] + kC14[3] * in[3] + kC14[5] * in[5] + kC14[7] * in[7]; - float odd1 = kC14[3] * in[1] + kC14[9] * in[3] - kC14[13] * in[5] - - kC14[7] * in[7]; - float odd2 = kC14[5] * in[1] - kC14[13] * in[3] - kC14[3] * in[5] - - kC14[7] * in[7]; - float odd3 = - kC14[7] * in[1] - kC14[7] * in[3] - kC14[7] * in[5] + kC14[7] * in[7]; - float odd4 = kC14[9] * in[1] - kC14[1] * in[3] + kC14[11] * in[5] + - kC14[7] * in[7]; - float odd5 = kC14[11] * in[1] - kC14[5] * in[3] + kC14[1] * in[5] - - kC14[7] * in[7]; - float odd6 = kC14[13] * in[1] - kC14[11] * in[3] + kC14[9] * in[5] - - kC14[7] * in[7]; - out[0] = even0 + odd0; - out[13] = even0 - odd0; - out[1] = even1 + odd1; - out[12] = even1 - odd1; - out[2] = even2 + odd2; - out[11] = even2 - odd2; - out[3] = even3 + odd3; - out[10] = even3 - odd3; - out[4] = even4 + odd4; - out[9] = even4 - odd4; - out[5] = even5 + odd5; - out[8] = even5 - odd5; - out[6] = even6 + odd6; - out[7] = even6 - odd6; - break; - } - case 15: { - static constexpr float kC15[15] = { - 1.414213562373, 1.406466352507, 1.383309602960, 1.344997023928, - 1.291948376043, 1.224744871392, 1.144122805635, 1.050965490998, - 0.946293578512, 0.831253875555, 0.707106781187, 0.575212476952, - 0.437016024449, 0.294031532930, 0.147825570407, - }; - float even0 = in[0] + kC15[2] * in[2] + kC15[4] * in[4] + kC15[6] * in[6]; - float even1 = - in[0] + kC15[6] * in[2] + kC15[12] * in[4] - kC15[12] * in[6]; - float even2 = - in[0] + kC15[10] * in[2] - kC15[10] * in[4] - kC15[0] * in[6]; - float even3 = - in[0] + kC15[14] * in[2] - kC15[2] * in[4] - kC15[12] * in[6]; - float even4 = - in[0] - kC15[12] * in[2] - kC15[6] * in[4] + kC15[6] * in[6]; - float even5 = - in[0] - kC15[8] * in[2] - kC15[14] * in[4] + kC15[6] * in[6]; - float even6 = - in[0] - kC15[4] * in[2] + kC15[8] * in[4] - kC15[12] * in[6]; - float even7 = in[0] - kC15[0] * in[2] + kC15[0] * in[4] - kC15[0] * in[6]; - float odd0 = - kC15[1] * in[1] + kC15[3] * in[3] + kC15[5] * in[5] + kC15[7] * in[7]; - float odd1 = kC15[3] * in[1] + kC15[9] * in[3] - kC15[9] * in[7]; - float odd2 = kC15[5] * in[1] - kC15[5] * in[5] - kC15[5] * in[7]; - float odd3 = kC15[7] * in[1] - kC15[9] * in[3] - kC15[5] * in[5] + - kC15[11] * in[7]; - float odd4 = kC15[9] * in[1] - kC15[3] * in[3] + kC15[3] * in[7]; - float odd5 = kC15[11] * in[1] - kC15[3] * in[3] + kC15[5] * in[5] - - kC15[13] * in[7]; - float odd6 = kC15[13] * in[1] - kC15[9] * in[3] + kC15[5] * in[5] - - kC15[1] * in[7]; - out[0] = even0 + odd0; - out[14] = even0 - odd0; - out[1] = even1 + odd1; - out[13] = even1 - odd1; - out[2] = even2 + odd2; - out[12] = even2 - odd2; - out[3] = even3 + odd3; - out[11] = even3 - odd3; - out[4] = even4 + odd4; - out[10] = even4 - odd4; - out[5] = even5 + odd5; - out[9] = even5 - odd5; - out[6] = even6 + odd6; - out[8] = even6 - odd6; - out[7] = even7; - break; - } - case 16: { - static constexpr float kC16[16] = { - 1.414213562373, 1.407403737526, 1.387039845322, 1.353318001174, - 1.306562964876, 1.247225012987, 1.175875602419, 1.093201867002, - 1.000000000000, 0.897167586343, 0.785694958387, 0.666655658478, - 0.541196100146, 0.410524527522, 0.275899379283, 0.138617169199, - }; - float even0 = in[0] + kC16[2] * in[2] + kC16[4] * in[4] + kC16[6] * in[6]; - float even1 = - in[0] + kC16[6] * in[2] + kC16[12] * in[4] - kC16[14] * in[6]; - float even2 = - in[0] + kC16[10] * in[2] - kC16[12] * in[4] - kC16[2] * in[6]; - float even3 = - in[0] + kC16[14] * in[2] - kC16[4] * in[4] - kC16[10] * in[6]; - float even4 = - in[0] - kC16[14] * in[2] - kC16[4] * in[4] + kC16[10] * in[6]; - float even5 = - in[0] - kC16[10] * in[2] - kC16[12] * in[4] + kC16[2] * in[6]; - float even6 = - in[0] - kC16[6] * in[2] + kC16[12] * in[4] + kC16[14] * in[6]; - float even7 = in[0] - kC16[2] * in[2] + kC16[4] * in[4] - kC16[6] * in[6]; - float odd0 = (kC16[1] * in[1] + kC16[3] * in[3] + kC16[5] * in[5] + - kC16[7] * in[7]); - float odd1 = (kC16[3] * in[1] + kC16[9] * in[3] + kC16[15] * in[5] - - kC16[11] * in[7]); - float odd2 = (kC16[5] * in[1] + kC16[15] * in[3] - kC16[7] * in[5] - - kC16[3] * in[7]); - float odd3 = (kC16[7] * in[1] - kC16[11] * in[3] - kC16[3] * in[5] + - kC16[15] * in[7]); - float odd4 = (kC16[9] * in[1] - kC16[5] * in[3] - kC16[13] * in[5] + - kC16[1] * in[7]); - float odd5 = (kC16[11] * in[1] - kC16[1] * in[3] + kC16[9] * in[5] + - kC16[13] * in[7]); - float odd6 = (kC16[13] * in[1] - kC16[7] * in[3] + kC16[1] * in[5] - - kC16[5] * in[7]); - float odd7 = (kC16[15] * in[1] - kC16[13] * in[3] + kC16[11] * in[5] - - kC16[9] * in[7]); - out[0] = even0 + odd0; - out[15] = even0 - odd0; - out[1] = even1 + odd1; - out[14] = even1 - odd1; - out[2] = even2 + odd2; - out[13] = even2 - odd2; - out[3] = even3 + odd3; - out[12] = even3 - odd3; - out[4] = even4 + odd4; - out[11] = even4 - odd4; - out[5] = even5 + odd5; - out[10] = even5 - odd5; - out[6] = even6 + odd6; - out[9] = even6 - odd6; - out[7] = even7 + odd7; - out[8] = even7 - odd7; - break; - } - default: - JXL_DEBUG_ABORT("Unreachable"); - break; - } -} - -void InverseTransformBlockGeneric(const int16_t* JXL_RESTRICT qblock, - const float* JXL_RESTRICT dequant, - const float* JXL_RESTRICT biases, - float* JXL_RESTRICT scratch_space, - float* JXL_RESTRICT output, - size_t output_stride, size_t dctsize) { - float* JXL_RESTRICT block0 = scratch_space; - float* JXL_RESTRICT block1 = scratch_space + DCTSIZE2; - DequantBlock(qblock, dequant, biases, block0); - if (dctsize == 1) { - *output = *block0; - } else if (dctsize == 2 || dctsize == 4) { - float* JXL_RESTRICT block2 = scratch_space + 2 * DCTSIZE2; - ComputeScaledIDCT(block0, block1, block2, 8); - if (dctsize == 4) { - for (size_t iy = 0; iy < 4; ++iy) { - for (size_t ix = 0; ix < 4; ++ix) { - float* block = &block2[16 * iy + 2 * ix]; - output[iy * output_stride + ix] = - 0.25f * (block[0] + block[1] + block[8] + block[9]); - } - } - } else { - for (size_t iy = 0; iy < 2; ++iy) { - for (size_t ix = 0; ix < 2; ++ix) { - float* block = &block2[32 * iy + 4 * ix]; - output[iy * output_stride + ix] = - 0.0625f * - (block[0] + block[1] + block[2] + block[3] + block[8] + block[9] + - block[10] + block[11] + block[16] + block[17] + block[18] + - block[19] + block[24] + block[25] + block[26] + block[27]); - } - } - } - } else { - float dctin[DCTSIZE]; - float dctout[DCTSIZE * 2]; - size_t insize = std::min(dctsize, DCTSIZE); - for (size_t ix = 0; ix < insize; ++ix) { - for (size_t iy = 0; iy < insize; ++iy) { - dctin[iy] = block0[iy * DCTSIZE + ix]; - } - Compute1dIDCT(dctin, dctout, dctsize); - for (size_t iy = 0; iy < dctsize; ++iy) { - block1[iy * dctsize + ix] = dctout[iy]; - } - } - for (size_t iy = 0; iy < dctsize; ++iy) { - Compute1dIDCT(block1 + iy * dctsize, output + iy * output_stride, - dctsize); - } - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -HWY_EXPORT(InverseTransformBlock8x8); -HWY_EXPORT(InverseTransformBlockGeneric); - -jxl::Status ChooseInverseTransform(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - for (int c = 0; c < cinfo->num_components; ++c) { - int dct_size = m->scaled_dct_size[c]; - if (dct_size < 1 || dct_size > 16) { - return JXL_FAILURE("Compute1dIDCT does not support N=%d", dct_size); - } - if (dct_size == DCTSIZE) { - m->inverse_transform[c] = HWY_DYNAMIC_DISPATCH(InverseTransformBlock8x8); - } else { - m->inverse_transform[c] = - HWY_DYNAMIC_DISPATCH(InverseTransformBlockGeneric); - } - } - return true; -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/idct.h b/third_party/jpeg-xl/lib/jpegli/idct.h deleted file mode 100644 index 746b1562c1524..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/idct.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_IDCT_H_ -#define LIB_JPEGLI_IDCT_H_ - -#include "lib/jpegli/common.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { - -jxl::Status ChooseInverseTransform(j_decompress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_IDCT_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/input.cc b/third_party/jpeg-xl/lib/jpegli/input.cc deleted file mode 100644 index 16299477f7b32..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/input.cc +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/input.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/input.cc" -#include -#include - -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::Vec; - -using D = HWY_FULL(float); -using DU = HWY_FULL(uint32_t); -using DU8 = Rebind; -using DU16 = Rebind; - -constexpr D d; -constexpr DU du; -constexpr DU8 du8; -constexpr DU16 du16; - -static constexpr double kMul16 = 1.0 / 257.0; -static constexpr double kMulFloat = 255.0; - -template -void ReadUint8Row(const uint8_t* row_in, size_t x0, size_t len, - float* row_out[kMaxComponents]) { - for (size_t x = x0; x < len; ++x) { - for (size_t c = 0; c < C; ++c) { - row_out[c][x] = row_in[C * x + c]; - } - } -} - -template -void ReadUint16Row(const uint8_t* row_in, size_t x0, size_t len, - float* row_out[kMaxComponents]) { - const uint16_t* row16 = reinterpret_cast(row_in); - for (size_t x = x0; x < len; ++x) { - for (size_t c = 0; c < C; ++c) { - uint16_t val = row16[C * x + c]; - if (swap_endianness) val = JXL_BSWAP16(val); - row_out[c][x] = val * kMul16; - } - } -} - -template -void ReadFloatRow(const uint8_t* row_in, size_t x0, size_t len, - float* row_out[kMaxComponents]) { - const float* rowf = reinterpret_cast(row_in); - for (size_t x = x0; x < len; ++x) { - for (size_t c = 0; c < C; ++c) { - float val = rowf[C * x + c]; - if (swap_endianness) val = BSwapFloat(val); - row_out[c][x] = val * kMulFloat; - } - } -} - -void ReadUint8RowSingle(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - float* JXL_RESTRICT const row0 = row_out[0]; - for (size_t x = 0; x < simd_len; x += N) { - Store(ConvertTo(d, PromoteTo(du, LoadU(du8, row_in + x))), d, row0 + x); - } - ReadUint8Row<1>(row_in, simd_len, len, row_out); -} - -void ReadUint8RowInterleaved2(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - Vec out0, out1; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved2(du8, row_in + 2 * x, out0, out1); - Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); - Store(ConvertTo(d, PromoteTo(du, out1)), d, row1 + x); - } - ReadUint8Row<2>(row_in, simd_len, len, row_out); -} - -void ReadUint8RowInterleaved3(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - Vec out0, out1, out2; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved3(du8, row_in + 3 * x, out0, out1, out2); - Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); - Store(ConvertTo(d, PromoteTo(du, out1)), d, row1 + x); - Store(ConvertTo(d, PromoteTo(du, out2)), d, row2 + x); - } - ReadUint8Row<3>(row_in, simd_len, len, row_out); -} - -void ReadUint8RowInterleaved4(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - float* JXL_RESTRICT const row3 = row_out[3]; - Vec out0, out1, out2, out3; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved4(du8, row_in + 4 * x, out0, out1, out2, out3); - Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); - Store(ConvertTo(d, PromoteTo(du, out1)), d, row1 + x); - Store(ConvertTo(d, PromoteTo(du, out2)), d, row2 + x); - Store(ConvertTo(d, PromoteTo(du, out3)), d, row3 + x); - } - ReadUint8Row<4>(row_in, simd_len, len, row_out); -} - -void ReadUint16RowSingle(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMul16); - const uint16_t* JXL_RESTRICT const row = - reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - for (size_t x = 0; x < simd_len; x += N) { - Store(Mul(mul, ConvertTo(d, PromoteTo(du, LoadU(du16, row + x)))), d, - row0 + x); - } - ReadUint16Row<1>(row_in, simd_len, len, row_out); -} - -void ReadUint16RowInterleaved2(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMul16); - const uint16_t* JXL_RESTRICT const row = - reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - Vec out0, out1; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved2(du16, row + 2 * x, out0, out1); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out1))), d, row1 + x); - } - ReadUint16Row<2>(row_in, simd_len, len, row_out); -} - -void ReadUint16RowInterleaved3(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMul16); - const uint16_t* JXL_RESTRICT const row = - reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - Vec out0, out1, out2; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved3(du16, row + 3 * x, out0, out1, out2); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out1))), d, row1 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out2))), d, row2 + x); - } - ReadUint16Row<3>(row_in, simd_len, len, row_out); -} - -void ReadUint16RowInterleaved4(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMul16); - const uint16_t* JXL_RESTRICT const row = - reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - float* JXL_RESTRICT const row3 = row_out[3]; - Vec out0, out1, out2, out3; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved4(du16, row + 4 * x, out0, out1, out2, out3); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out1))), d, row1 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out2))), d, row2 + x); - Store(Mul(mul, ConvertTo(d, PromoteTo(du, out3))), d, row3 + x); - } - ReadUint16Row<4>(row_in, simd_len, len, row_out); -} - -void ReadUint16RowSingleSwap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadUint16Row<1, true>(row_in, 0, len, row_out); -} - -void ReadUint16RowInterleaved2Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadUint16Row<2, true>(row_in, 0, len, row_out); -} - -void ReadUint16RowInterleaved3Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadUint16Row<3, true>(row_in, 0, len, row_out); -} - -void ReadUint16RowInterleaved4Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadUint16Row<4, true>(row_in, 0, len, row_out); -} - -void ReadFloatRowSingle(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMulFloat); - const float* JXL_RESTRICT const row = reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - for (size_t x = 0; x < simd_len; x += N) { - Store(Mul(mul, LoadU(d, row + x)), d, row0 + x); - } - ReadFloatRow<1>(row_in, simd_len, len, row_out); -} - -void ReadFloatRowInterleaved2(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMulFloat); - const float* JXL_RESTRICT const row = reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - Vec out0, out1; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved2(d, row + 2 * x, out0, out1); - Store(Mul(mul, out0), d, row0 + x); - Store(Mul(mul, out1), d, row1 + x); - } - ReadFloatRow<2>(row_in, simd_len, len, row_out); -} - -void ReadFloatRowInterleaved3(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMulFloat); - const float* JXL_RESTRICT const row = reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - Vec out0, out1, out2; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved3(d, row + 3 * x, out0, out1, out2); - Store(Mul(mul, out0), d, row0 + x); - Store(Mul(mul, out1), d, row1 + x); - Store(Mul(mul, out2), d, row2 + x); - } - ReadFloatRow<3>(row_in, simd_len, len, row_out); -} - -void ReadFloatRowInterleaved4(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - const size_t N = Lanes(d); - const size_t simd_len = len & (~(N - 1)); - const auto mul = Set(d, kMulFloat); - const float* JXL_RESTRICT const row = reinterpret_cast(row_in); - float* JXL_RESTRICT const row0 = row_out[0]; - float* JXL_RESTRICT const row1 = row_out[1]; - float* JXL_RESTRICT const row2 = row_out[2]; - float* JXL_RESTRICT const row3 = row_out[3]; - Vec out0, out1, out2, out3; // NOLINT - for (size_t x = 0; x < simd_len; x += N) { - LoadInterleaved4(d, row + 4 * x, out0, out1, out2, out3); - Store(Mul(mul, out0), d, row0 + x); - Store(Mul(mul, out1), d, row1 + x); - Store(Mul(mul, out2), d, row2 + x); - Store(Mul(mul, out3), d, row3 + x); - } - ReadFloatRow<4>(row_in, simd_len, len, row_out); -} - -void ReadFloatRowSingleSwap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadFloatRow<1, true>(row_in, 0, len, row_out); -} - -void ReadFloatRowInterleaved2Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadFloatRow<2, true>(row_in, 0, len, row_out); -} - -void ReadFloatRowInterleaved3Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadFloatRow<3, true>(row_in, 0, len, row_out); -} - -void ReadFloatRowInterleaved4Swap(const uint8_t* row_in, size_t len, - float* row_out[kMaxComponents]) { - ReadFloatRow<4, true>(row_in, 0, len, row_out); -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -HWY_EXPORT(ReadUint8RowSingle); -HWY_EXPORT(ReadUint8RowInterleaved2); -HWY_EXPORT(ReadUint8RowInterleaved3); -HWY_EXPORT(ReadUint8RowInterleaved4); -HWY_EXPORT(ReadUint16RowSingle); -HWY_EXPORT(ReadUint16RowInterleaved2); -HWY_EXPORT(ReadUint16RowInterleaved3); -HWY_EXPORT(ReadUint16RowInterleaved4); -HWY_EXPORT(ReadUint16RowSingleSwap); -HWY_EXPORT(ReadUint16RowInterleaved2Swap); -HWY_EXPORT(ReadUint16RowInterleaved3Swap); -HWY_EXPORT(ReadUint16RowInterleaved4Swap); -HWY_EXPORT(ReadFloatRowSingle); -HWY_EXPORT(ReadFloatRowInterleaved2); -HWY_EXPORT(ReadFloatRowInterleaved3); -HWY_EXPORT(ReadFloatRowInterleaved4); -HWY_EXPORT(ReadFloatRowSingleSwap); -HWY_EXPORT(ReadFloatRowInterleaved2Swap); -HWY_EXPORT(ReadFloatRowInterleaved3Swap); -HWY_EXPORT(ReadFloatRowInterleaved4Swap); - -void ChooseInputMethod(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - bool swap_endianness = - (m->endianness == JPEGLI_LITTLE_ENDIAN && !IsLittleEndian()) || - (m->endianness == JPEGLI_BIG_ENDIAN && IsLittleEndian()); - m->input_method = nullptr; - if (m->data_type == JPEGLI_TYPE_UINT8) { - if (cinfo->raw_data_in || cinfo->input_components == 1) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint8RowSingle); - } else if (cinfo->input_components == 2) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint8RowInterleaved2); - } else if (cinfo->input_components == 3) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint8RowInterleaved3); - } else if (cinfo->input_components == 4) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint8RowInterleaved4); - } - } else if (m->data_type == JPEGLI_TYPE_UINT16 && !swap_endianness) { - if (cinfo->raw_data_in || cinfo->input_components == 1) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowSingle); - } else if (cinfo->input_components == 2) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved2); - } else if (cinfo->input_components == 3) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved3); - } else if (cinfo->input_components == 4) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved4); - } - } else if (m->data_type == JPEGLI_TYPE_UINT16 && swap_endianness) { - if (cinfo->raw_data_in || cinfo->input_components == 1) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowSingleSwap); - } else if (cinfo->input_components == 2) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved2Swap); - } else if (cinfo->input_components == 3) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved3Swap); - } else if (cinfo->input_components == 4) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadUint16RowInterleaved4Swap); - } - } else if (m->data_type == JPEGLI_TYPE_FLOAT && !swap_endianness) { - if (cinfo->raw_data_in || cinfo->input_components == 1) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowSingle); - } else if (cinfo->input_components == 2) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved2); - } else if (cinfo->input_components == 3) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved3); - } else if (cinfo->input_components == 4) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved4); - } - } else if (m->data_type == JPEGLI_TYPE_FLOAT && swap_endianness) { - if (cinfo->raw_data_in || cinfo->input_components == 1) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowSingleSwap); - } else if (cinfo->input_components == 2) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved2Swap); - } else if (cinfo->input_components == 3) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved3Swap); - } else if (cinfo->input_components == 4) { - m->input_method = HWY_DYNAMIC_DISPATCH(ReadFloatRowInterleaved4Swap); - } - } - if (m->input_method == nullptr) { - JPEGLI_ERROR("Could not find input method."); - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/input.h b/third_party/jpeg-xl/lib/jpegli/input.h deleted file mode 100644 index f54d0bee438d8..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/input.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_INPUT_H_ -#define LIB_JPEGLI_INPUT_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void ChooseInputMethod(j_compress_ptr cinfo); - -} // namespace jpegli - -#endif // LIB_JPEGLI_INPUT_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc deleted file mode 100644 index 6fa1416213ce6..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { -namespace { - -constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; - -struct SourceManager { - SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size, - bool is_partial_file) - : data_(data), - len_(len), - pos_(0), - max_chunk_size_(max_chunk_size), - is_partial_file_(is_partial_file) { - pub_.init_source = init_source; - pub_.fill_input_buffer = fill_input_buffer; - pub_.next_input_byte = nullptr; - pub_.bytes_in_buffer = 0; - pub_.skip_input_data = skip_input_data; - pub_.resync_to_restart = jpegli_resync_to_restart; - pub_.term_source = term_source; - if (max_chunk_size_ == 0) max_chunk_size_ = len; - } - - ~SourceManager() { - EXPECT_EQ(0, pub_.bytes_in_buffer); - if (!is_partial_file_) { - EXPECT_EQ(len_, pos_); - } - } - - bool LoadNextChunk() { - if (pos_ >= len_ && !is_partial_file_) { - return false; - } - if (pub_.bytes_in_buffer > 0) { - EXPECT_LE(pub_.bytes_in_buffer, buffer_.size()); - memmove(buffer_.data(), pub_.next_input_byte, pub_.bytes_in_buffer); - } - size_t chunk_size = - pos_ < len_ ? std::min(len_ - pos_, max_chunk_size_) : 2; - buffer_.resize(pub_.bytes_in_buffer + chunk_size); - memcpy(&buffer_[pub_.bytes_in_buffer], - pos_ < len_ ? data_ + pos_ : kFakeEoiMarker, chunk_size); - pub_.next_input_byte = buffer_.data(); - pub_.bytes_in_buffer += chunk_size; - pos_ += chunk_size; - return true; - } - - private: - jpeg_source_mgr pub_; - std::vector buffer_; - const uint8_t* data_; - size_t len_; - size_t pos_; - size_t max_chunk_size_; - bool is_partial_file_; - - static void init_source(j_decompress_ptr cinfo) { - auto* src = reinterpret_cast(cinfo->src); - src->pub_.next_input_byte = nullptr; - src->pub_.bytes_in_buffer = 0; - } - - static boolean fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; } - - static void skip_input_data(j_decompress_ptr cinfo, - long num_bytes /* NOLINT*/) { - auto* src = reinterpret_cast(cinfo->src); - if (num_bytes <= 0) { - return; - } - if (src->pub_.bytes_in_buffer >= static_cast(num_bytes)) { - src->pub_.bytes_in_buffer -= num_bytes; - src->pub_.next_input_byte += num_bytes; - } else { - src->pos_ += num_bytes - src->pub_.bytes_in_buffer; - src->pub_.bytes_in_buffer = 0; - } - } - - static void term_source(j_decompress_ptr cinfo) {} -}; - -uint8_t markers_seen[kMarkerSequenceLen]; -size_t num_markers_seen = 0; - -uint8_t get_next_byte(j_decompress_ptr cinfo) { - cinfo->src->bytes_in_buffer--; - return *cinfo->src->next_input_byte++; -} - -boolean test_marker_processor(j_decompress_ptr cinfo) { - markers_seen[num_markers_seen] = cinfo->unread_marker; - if (cinfo->src->bytes_in_buffer < 2) { - return FALSE; - } - size_t marker_len = (get_next_byte(cinfo) << 8) + get_next_byte(cinfo); - EXPECT_EQ(2 + ((num_markers_seen + 2) % sizeof(kMarkerData)), marker_len); - if (marker_len > 2) { - (*cinfo->src->skip_input_data)(cinfo, marker_len - 2); - } - ++num_markers_seen; - return TRUE; -} - -jxl::Status ReadOutputImage(const DecompressParams& dparams, - j_decompress_ptr cinfo, SourceManager* src, - TestImage* output) { - output->ysize = cinfo->output_height; - output->xsize = cinfo->output_width; - output->components = cinfo->num_components; - if (cinfo->raw_data_out) { - output->color_space = cinfo->jpeg_color_space; - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - std::vector plane(ysize * xsize); - output->raw_data.emplace_back(std::move(plane)); - } - } else { - output->color_space = cinfo->out_color_space; - output->AllocatePixels(); - } - size_t total_output_lines = 0; - while (cinfo->output_scanline < cinfo->output_height) { - size_t max_lines; - size_t num_output_lines; - if (cinfo->raw_data_out) { - size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE; - EXPECT_EQ(cinfo->output_scanline, cinfo->output_iMCU_row * iMCU_height); - max_lines = iMCU_height; - std::vector> rowdata(cinfo->num_components); - std::vector data(cinfo->num_components); - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - size_t num_lines = cinfo->comp_info[c].v_samp_factor * DCTSIZE; - rowdata[c].resize(num_lines); - size_t y0 = cinfo->output_iMCU_row * num_lines; - for (size_t i = 0; i < num_lines; ++i) { - rowdata[c][i] = - y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; - } - data[c] = rowdata[c].data(); - } - while ((num_output_lines = - jpegli_read_raw_data(cinfo, data.data(), max_lines)) == 0) { - JXL_ENSURE(src && src->LoadNextChunk()); - } - } else { - size_t max_output_lines = dparams.max_output_lines; - if (max_output_lines == 0) max_output_lines = cinfo->output_height; - size_t lines_left = cinfo->output_height - cinfo->output_scanline; - max_lines = std::min(max_output_lines, lines_left); - size_t stride = cinfo->output_width * cinfo->num_components; - std::vector scanlines(max_lines); - for (size_t i = 0; i < max_lines; ++i) { - size_t yidx = cinfo->output_scanline + i; - scanlines[i] = &output->pixels[yidx * stride]; - } - while ((num_output_lines = jpegli_read_scanlines(cinfo, scanlines.data(), - max_lines)) == 0) { - JXL_ENSURE(src && src->LoadNextChunk()); - } - } - total_output_lines += num_output_lines; - EXPECT_EQ(total_output_lines, cinfo->output_scanline); - if (num_output_lines < max_lines) { - JXL_ENSURE(src && src->LoadNextChunk()); - } - } - return true; -} - -struct TestConfig { - std::string fn; - std::string fn_desc; - TestImage input; - CompressParams jparams; - DecompressParams dparams; - float max_rms_dist = 1.0f; -}; - -jxl::StatusOr> GetTestJpegData(TestConfig& config) { - std::vector compressed; - if (!config.fn.empty()) { - JXL_ASSIGN_OR_RETURN(compressed, ReadTestData(config.fn)); - } else { - GeneratePixels(&config.input); - JXL_RETURN_IF_ERROR( - EncodeWithJpegli(config.input, config.jparams, &compressed)); - } - return compressed; -} - -bool IsSequential(const TestConfig& config) { - if (!config.fn.empty()) { - return config.fn_desc.find("PROGR") == std::string::npos; - } - return config.jparams.progressive_mode <= 0; -} - -class InputSuspensionTestParam : public ::testing::TestWithParam {}; - -TEST_P(InputSuspensionTestParam, InputOutputLockStepNonBuffered) { - TestConfig config = GetParam(); - const DecompressParams& dparams = config.dparams; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data."); - bool is_partial = config.dparams.size_factor < 1.0f; - if (is_partial) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size, - is_partial); - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - cinfo.src = reinterpret_cast(&src); - - if (config.jparams.add_marker) { - jpegli_save_markers(&cinfo, kSpecialMarker0, 0xffff); - jpegli_save_markers(&cinfo, kSpecialMarker1, 0xffff); - num_markers_seen = 0; - jpegli_set_marker_processor(&cinfo, 0xe6, test_marker_processor); - jpegli_set_marker_processor(&cinfo, 0xe7, test_marker_processor); - jpegli_set_marker_processor(&cinfo, 0xe8, test_marker_processor); - } - while (jpegli_read_header(&cinfo, TRUE) == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - SetDecompressParams(dparams, &cinfo); - jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness); - if (config.jparams.add_marker) { - EXPECT_EQ(num_markers_seen, kMarkerSequenceLen); - EXPECT_EQ(0, memcmp(markers_seen, kMarkerSequence, num_markers_seen)); - } - VerifyHeader(config.jparams, &cinfo); - cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); - - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays; - while ((coef_arrays = jpegli_read_coefficients(&cinfo)) == nullptr) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - CopyCoefficients(&cinfo, coef_arrays, &output0); - } else { - while (!jpegli_start_decompress(&cinfo)) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - JPEGLI_TEST_ENSURE_TRUE(ReadOutputImage(dparams, &cinfo, &src, &output0)); - } - - while (!jpegli_finish_decompress(&cinfo)) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - TestImage output1; - DecodeWithLibjpeg(config.jparams, dparams, compressed, &output1); - VerifyOutputImage(output1, output0, config.max_rms_dist); -} - -TEST_P(InputSuspensionTestParam, InputOutputLockStepBuffered) { - TestConfig config = GetParam(); - if (config.jparams.add_marker) return; - const DecompressParams& dparams = config.dparams; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data."); - bool is_partial = config.dparams.size_factor < 1.0f; - if (is_partial) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size, - is_partial); - std::vector output_progression0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - - cinfo.src = reinterpret_cast(&src); - - while (jpegli_read_header(&cinfo, TRUE) == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - SetDecompressParams(dparams, &cinfo); - jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness); - - cinfo.buffered_image = TRUE; - cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); - - EXPECT_TRUE(jpegli_start_decompress(&cinfo)); - EXPECT_FALSE(jpegli_input_complete(&cinfo)); - EXPECT_EQ(0, cinfo.output_scan_number); - - int sos_marker_cnt = 1; // read_header reads the first SOS marker - while (!jpegli_input_complete(&cinfo)) { - EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt); - EXPECT_TRUE(jpegli_start_output(&cinfo, cinfo.input_scan_number)); - // start output sets output_scan_number, but does not change - // input_scan_number - EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number); - EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt); - TestImage output; - JPEGLI_TEST_ENSURE_TRUE(ReadOutputImage(dparams, &cinfo, &src, &output)); - output_progression0.emplace_back(std::move(output)); - // read scanlines/read raw data does not change input/output scan number - EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt); - EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number); - while (!jpegli_finish_output(&cinfo)) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - ++sos_marker_cnt; // finish output reads the next SOS marker or EOI - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo); - JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr); - CopyCoefficients(&cinfo, coef_arrays, &output_progression0.back()); - } - } - - EXPECT_TRUE(jpegli_finish_decompress(&cinfo)); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - std::vector output_progression1; - DecodeAllScansWithLibjpeg(config.jparams, dparams, compressed, - &output_progression1); - ASSERT_EQ(output_progression0.size(), output_progression1.size()); - for (size_t i = 0; i < output_progression0.size(); ++i) { - const TestImage& output = output_progression0[i]; - const TestImage& expected = output_progression1[i]; - VerifyOutputImage(expected, output, config.max_rms_dist); - } -} - -TEST_P(InputSuspensionTestParam, PreConsumeInputBuffered) { - TestConfig config = GetParam(); - if (config.jparams.add_marker) return; - const DecompressParams& dparams = config.dparams; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data."); - bool is_partial = config.dparams.size_factor < 1.0f; - if (is_partial) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - std::vector output_progression1; - DecodeAllScansWithLibjpeg(config.jparams, dparams, compressed, - &output_progression1); - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size, - is_partial); - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - cinfo.src = reinterpret_cast(&src); - - int status; - while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_SOS) { - if (status == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - } - EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo)); - cinfo.buffered_image = TRUE; - cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); - cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing); - - EXPECT_TRUE(jpegli_start_decompress(&cinfo)); - EXPECT_FALSE(jpegli_input_complete(&cinfo)); - EXPECT_EQ(1, cinfo.input_scan_number); - EXPECT_EQ(0, cinfo.output_scan_number); - - while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_EOI) { - if (status == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - } - - EXPECT_TRUE(jpegli_input_complete(&cinfo)); - EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number); - EXPECT_EQ(0, cinfo.output_scan_number); - - EXPECT_TRUE(jpegli_start_output(&cinfo, cinfo.input_scan_number)); - EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number); - EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number); - - JPEGLI_TEST_ENSURE_TRUE( - ReadOutputImage(dparams, &cinfo, nullptr, &output0)); - EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number); - EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number); - - EXPECT_TRUE(jpegli_finish_output(&cinfo)); - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo); - JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr); - CopyCoefficients(&cinfo, coef_arrays, &output0); - } - EXPECT_TRUE(jpegli_finish_decompress(&cinfo)); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - VerifyOutputImage(output_progression1.back(), output0, config.max_rms_dist); -} - -TEST_P(InputSuspensionTestParam, PreConsumeInputNonBuffered) { - TestConfig config = GetParam(); - if (config.jparams.add_marker || IsSequential(config)) return; - const DecompressParams& dparams = config.dparams; - JXL_ASSIGN_OR_QUIT(std::vector compressed, GetTestJpegData(config), - "Failed to create test data."); - bool is_partial = config.dparams.size_factor < 1.0f; - if (is_partial) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size, - is_partial); - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - cinfo.src = reinterpret_cast(&src); - - int status; - while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_SOS) { - if (status == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - } - EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo)); - cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); - cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing); - - if (dparams.output_mode == COEFFICIENTS) { - jpegli_read_coefficients(&cinfo); - } else { - while (!jpegli_start_decompress(&cinfo)) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - } - - while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_EOI) { - if (status == JPEG_SUSPENDED) { - JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk()); - } - } - - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo); - JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr); - CopyCoefficients(&cinfo, coef_arrays, &output0); - } else { - JPEGLI_TEST_ENSURE_TRUE( - ReadOutputImage(dparams, &cinfo, nullptr, &output0)); - } - - EXPECT_TRUE(jpegli_finish_decompress(&cinfo)); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - TestImage output1; - DecodeWithLibjpeg(config.jparams, dparams, compressed, &output1); - VerifyOutputImage(output1, output0, config.max_rms_dist); -} - -std::vector GenerateTests() { - std::vector all_tests; - std::vector> testfiles({ - {"jxl/flower/flower.png.im_q85_444.jpg", "Q85YUV444"}, - {"jxl/flower/flower.png.im_q85_420_R13B.jpg", "Q85YUV420R13B"}, - {"jxl/flower/flower.png.im_q85_420_progr.jpg", "Q85YUV420PROGR"}, - }); - for (const auto& it : testfiles) { - for (size_t chunk_size : {1, 64, 65536}) { - for (size_t max_output_lines : {0, 1, 8, 16}) { - TestConfig config; - config.fn = it.first; - config.fn_desc = it.second; - config.dparams.chunk_size = chunk_size; - config.dparams.max_output_lines = max_output_lines; - all_tests.push_back(config); - if (max_output_lines == 16) { - config.dparams.output_mode = RAW_DATA; - all_tests.push_back(config); - config.dparams.output_mode = COEFFICIENTS; - all_tests.push_back(config); - } - } - } - } - for (size_t r : {1, 17, 1024}) { - for (size_t chunk_size : {1, 65536}) { - TestConfig config; - config.dparams.chunk_size = chunk_size; - config.jparams.progressive_mode = 2; - config.jparams.restart_interval = r; - all_tests.push_back(config); - } - } - for (size_t chunk_size : {1, 4, 1024}) { - TestConfig config; - config.input.xsize = 256; - config.input.ysize = 256; - config.dparams.chunk_size = chunk_size; - config.jparams.add_marker = true; - all_tests.push_back(config); - } - // Tests for partial input. - for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f}) { - for (int progr : {0, 1, 3}) { - for (int samp : {1, 2}) { - for (JpegIOMode output_mode : {PIXELS, RAW_DATA}) { - TestConfig config; - config.input.xsize = 517; - config.input.ysize = 523; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = progr; - config.dparams.size_factor = size_factor; - config.dparams.output_mode = output_mode; - // The last partially available block can behave differently. - // TODO(szabadka) Figure out if we can make the behaviour more - // similar. - config.max_rms_dist = samp == 1 ? 1.75f : 3.0f; - all_tests.push_back(config); - } - } - } - } - // Tests for block smoothing. - for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f, 1.0f}) { - for (int samp : {1, 2}) { - TestConfig config; - config.input.xsize = 517; - config.input.ysize = 523; - config.jparams.h_sampling = {samp, 1, 1}; - config.jparams.v_sampling = {samp, 1, 1}; - config.jparams.progressive_mode = 2; - config.dparams.size_factor = size_factor; - config.dparams.do_block_smoothing = true; - // libjpeg does smoothing for incomplete scans differently at - // the border between current and previous scans. - config.max_rms_dist = 8.0f; - all_tests.push_back(config); - } - } - return all_tests; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - if (!c.fn.empty()) { - os << c.fn_desc; - } else { - os << c.input; - } - os << c.jparams; - if (c.dparams.chunk_size == 0) { - os << "CompleteInput"; - } else { - os << "InputChunks" << c.dparams.chunk_size; - } - if (c.dparams.size_factor < 1.0f) { - os << "Partial" << static_cast(c.dparams.size_factor * 100) << "p"; - } - if (c.dparams.max_output_lines == 0) { - os << "CompleteOutput"; - } else { - os << "OutputLines" << c.dparams.max_output_lines; - } - if (c.dparams.output_mode == RAW_DATA) { - os << "RawDataOut"; - } else if (c.dparams.output_mode == COEFFICIENTS) { - os << "CoeffsOut"; - } - if (c.dparams.do_block_smoothing) { - os << "BlockSmoothing"; - } - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(InputSuspensionTest, InputSuspensionTestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/jpeg.version.62 b/third_party/jpeg-xl/lib/jpegli/jpeg.version.62 deleted file mode 100644 index 3a8d1f5ec5f00..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/jpeg.version.62 +++ /dev/null @@ -1,11 +0,0 @@ -LIBJPEG_6.2 { - global: - jpeg*; -}; - -LIBJPEGTURBO_6.2 { - global: - jpeg_mem_src*; - jpeg_mem_dest*; - tj*; -}; \ No newline at end of file diff --git a/third_party/jpeg-xl/lib/jpegli/jpeg.version.8 b/third_party/jpeg-xl/lib/jpegli/jpeg.version.8 deleted file mode 100644 index aa891f85716e5..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/jpeg.version.8 +++ /dev/null @@ -1,9 +0,0 @@ -LIBJPEG_8.0 { - global: - jpeg*; -}; - -LIBJPEGTURBO_8.0 { - global: - tj*; -}; diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc deleted file mode 100644 index 76746a8fddb34..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/libjpeg_test_util.h" - -#include -#include - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/include_jpeglib.h" // NOLINT -#include "lib/jxl/base/sanitizers.h" - -namespace jpegli { - -namespace { - -void Check(bool ok) { - if (!ok) { - JXL_CRASH(); - } -} - -#define JPEG_API_FN(name) jpeg_##name -#include "lib/jpegli/test_utils-inl.h" -#undef JPEG_API_FN - -void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams, - TestImage* output) { - JDIMENSION xoffset = 0; - JDIMENSION yoffset = 0; - JDIMENSION xsize_cropped = cinfo->output_width; - JDIMENSION ysize_cropped = cinfo->output_height; - if (dparams.crop_output) { - xoffset = xsize_cropped = cinfo->output_width / 3; - yoffset = ysize_cropped = cinfo->output_height / 3; - jpeg_crop_scanline(cinfo, &xoffset, &xsize_cropped); - Check(xsize_cropped == cinfo->output_width); - } - output->xsize = xsize_cropped; - output->ysize = ysize_cropped; - output->components = cinfo->out_color_components; - if (cinfo->quantize_colors) { - JSAMPLE** colormap = cinfo->colormap; - jxl::msan::UnpoisonMemory(reinterpret_cast(colormap), - cinfo->out_color_components * sizeof(JSAMPLE*)); - for (int c = 0; c < cinfo->out_color_components; ++c) { - jxl::msan::UnpoisonMemory( - reinterpret_cast(colormap[c]), - cinfo->actual_number_of_colors * sizeof(JSAMPLE)); - } - } - if (!cinfo->raw_data_out) { - size_t stride = output->xsize * output->components; - output->pixels.resize(output->ysize * stride); - output->color_space = cinfo->out_color_space; - if (yoffset > 0) { - jpeg_skip_scanlines(cinfo, yoffset); - } - for (size_t y = 0; y < output->ysize; ++y) { - JSAMPROW rows[] = { - reinterpret_cast(&output->pixels[y * stride])}; - Check(1 == jpeg_read_scanlines(cinfo, rows, 1)); - jxl::msan::UnpoisonMemory( - rows[0], sizeof(JSAMPLE) * cinfo->output_components * output->xsize); - if (cinfo->quantize_colors) { - UnmapColors(rows[0], cinfo->output_width, cinfo->out_color_components, - cinfo->colormap, cinfo->actual_number_of_colors); - } - } - if (cinfo->output_scanline < cinfo->output_height) { - jpeg_skip_scanlines(cinfo, cinfo->output_height - cinfo->output_scanline); - } - } else { - output->color_space = cinfo->jpeg_color_space; - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - std::vector plane(ysize * xsize); - output->raw_data.emplace_back(std::move(plane)); - } - while (cinfo->output_scanline < cinfo->output_height) { - size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE; - Check(cinfo->output_scanline == cinfo->output_iMCU_row * iMCU_height); - std::vector> rowdata(cinfo->num_components); - std::vector data(cinfo->num_components); - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - size_t num_lines = cinfo->comp_info[c].v_samp_factor * DCTSIZE; - rowdata[c].resize(num_lines); - size_t y0 = cinfo->output_iMCU_row * num_lines; - for (size_t i = 0; i < num_lines; ++i) { - rowdata[c][i] = - y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; - } - data[c] = rowdata[c].data(); - } - Check(iMCU_height == jpeg_read_raw_data(cinfo, data.data(), iMCU_height)); - } - } - Check(cinfo->total_iMCU_rows == - DivCeil(cinfo->image_height, cinfo->max_v_samp_factor * DCTSIZE)); -} - -void DecodeWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, j_decompress_ptr cinfo, - TestImage* output) { - if (jparams.add_marker) { - jpeg_save_markers(cinfo, kSpecialMarker0, 0xffff); - jpeg_save_markers(cinfo, kSpecialMarker1, 0xffff); - } - if (!jparams.icc.empty()) { - jpeg_save_markers(cinfo, JPEG_APP0 + 2, 0xffff); - } - Check(JPEG_REACHED_SOS == jpeg_read_header(cinfo, /*require_image=*/TRUE)); - if (!jparams.icc.empty()) { - uint8_t* icc_data = nullptr; - unsigned int icc_len = 0; // "unpoison" via initialization - Check(jpeg_read_icc_profile(cinfo, &icc_data, &icc_len)); - Check(icc_data); - jxl::msan::UnpoisonMemory(icc_data, icc_len); - Check(0 == memcmp(jparams.icc.data(), icc_data, icc_len)); - free(icc_data); - } - SetDecompressParams(dparams, cinfo); - VerifyHeader(jparams, cinfo); - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpeg_read_coefficients(cinfo); - Check(coef_arrays != nullptr); - jxl::msan::UnpoisonMemory(coef_arrays, - cinfo->num_components * sizeof(jvirt_barray_ptr)); - CopyCoefficients(cinfo, coef_arrays, output); - } else { - Check(jpeg_start_decompress(cinfo)); - VerifyScanHeader(jparams, cinfo); - ReadOutputPass(cinfo, dparams, output); - } - Check(jpeg_finish_decompress(cinfo)); -} - -} // namespace - -// Verifies that an image encoded with libjpegli can be decoded with libjpeg, -// and checks that the jpeg coding metadata matches jparams. -void DecodeAllScansWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const std::vector& compressed, - std::vector* output_progression) { - jpeg_decompress_struct cinfo = {}; - const auto try_catch_block = [&]() { - jpeg_error_mgr jerr; - jmp_buf env; - cinfo.err = jpeg_std_error(&jerr); - if (setjmp(env)) { - return false; - } - cinfo.client_data = reinterpret_cast(&env); - cinfo.err->error_exit = [](j_common_ptr cinfo) { - (*cinfo->err->output_message)(cinfo); - jmp_buf* env = reinterpret_cast(cinfo->client_data); - jpeg_destroy(cinfo); - longjmp(*env, 1); - }; - jpeg_create_decompress(&cinfo); - jpeg_mem_src(&cinfo, compressed.data(), compressed.size()); - if (jparams.add_marker) { - jpeg_save_markers(&cinfo, kSpecialMarker0, 0xffff); - jpeg_save_markers(&cinfo, kSpecialMarker1, 0xffff); - } - Check(JPEG_REACHED_SOS == jpeg_read_header(&cinfo, /*require_image=*/TRUE)); - cinfo.buffered_image = TRUE; - SetDecompressParams(dparams, &cinfo); - VerifyHeader(jparams, &cinfo); - Check(jpeg_start_decompress(&cinfo)); - // start decompress should not read the whole input in buffered image mode - Check(!jpeg_input_complete(&cinfo)); - Check(cinfo.output_scan_number == 0); - int sos_marker_cnt = 1; // read header reads the first SOS marker - while (!jpeg_input_complete(&cinfo)) { - Check(cinfo.input_scan_number == sos_marker_cnt); - if (dparams.skip_scans && (cinfo.input_scan_number % 2) != 1) { - int result = JPEG_SUSPENDED; - while (result != JPEG_REACHED_SOS && result != JPEG_REACHED_EOI) { - result = jpeg_consume_input(&cinfo); - } - if (result == JPEG_REACHED_SOS) ++sos_marker_cnt; - continue; - } - SetScanDecompressParams(dparams, &cinfo, cinfo.input_scan_number); - Check(jpeg_start_output(&cinfo, cinfo.input_scan_number)); - // start output sets output_scan_number, but does not change - // input_scan_number - Check(cinfo.output_scan_number == cinfo.input_scan_number); - Check(cinfo.input_scan_number == sos_marker_cnt); - VerifyScanHeader(jparams, &cinfo); - TestImage output; - ReadOutputPass(&cinfo, dparams, &output); - output_progression->emplace_back(std::move(output)); - // read scanlines/read raw data does not change input/output scan number - if (!cinfo.progressive_mode) { - Check(cinfo.input_scan_number == sos_marker_cnt); - Check(cinfo.output_scan_number == cinfo.input_scan_number); - } - Check(jpeg_finish_output(&cinfo)); - ++sos_marker_cnt; // finish output reads the next SOS marker or EOI - if (dparams.output_mode == COEFFICIENTS) { - jvirt_barray_ptr* coef_arrays = jpeg_read_coefficients(&cinfo); - Check(coef_arrays != nullptr); - jxl::msan::UnpoisonMemory( - coef_arrays, cinfo.num_components * sizeof(jvirt_barray_ptr)); - CopyCoefficients(&cinfo, coef_arrays, &output_progression->back()); - } - } - Check(jpeg_finish_decompress(&cinfo)); - return true; - }; - Check(try_catch_block()); - jpeg_destroy_decompress(&cinfo); -} - -// Returns the number of bytes read from compressed. -size_t DecodeWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const uint8_t* table_stream, size_t table_stream_size, - const uint8_t* compressed, size_t len, - TestImage* output) { - jpeg_decompress_struct cinfo = {}; - size_t bytes_read; - const auto try_catch_block = [&]() { - jpeg_error_mgr jerr; - jmp_buf env; - cinfo.err = jpeg_std_error(&jerr); - if (setjmp(env)) { - return false; - } - cinfo.client_data = reinterpret_cast(&env); - cinfo.err->error_exit = [](j_common_ptr cinfo) { - (*cinfo->err->output_message)(cinfo); - jmp_buf* env = reinterpret_cast(cinfo->client_data); - jpeg_destroy(cinfo); - longjmp(*env, 1); - }; - jpeg_create_decompress(&cinfo); - if (table_stream != nullptr) { - jpeg_mem_src(&cinfo, table_stream, table_stream_size); - jpeg_read_header(&cinfo, FALSE); - } - jpeg_mem_src(&cinfo, compressed, len); - DecodeWithLibjpeg(jparams, dparams, &cinfo, output); - jxl::msan::UnpoisonMemory(cinfo.src, sizeof(jpeg_source_mgr)); - bytes_read = len - cinfo.src->bytes_in_buffer; - return true; - }; - Check(try_catch_block()); - jpeg_destroy_decompress(&cinfo); - return bytes_read; -} - -void DecodeWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const std::vector& compressed, - TestImage* output) { - DecodeWithLibjpeg(jparams, dparams, nullptr, 0, compressed.data(), - compressed.size(), output); -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.h b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.h deleted file mode 100644 index 18cc1e57b54a1..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_LIBJPEG_TEST_UTIL_H_ -#define LIB_JPEGLI_LIBJPEG_TEST_UTIL_H_ - -#include -#include - -#include - -#include "lib/jpegli/test_params.h" - -namespace jpegli { - -// Verifies that an image encoded with libjpegli can be decoded with libjpeg, -// and checks that the jpeg coding metadata matches jparams. -void DecodeAllScansWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const std::vector& compressed, - std::vector* output_progression); -// Returns the number of bytes read from compressed. -size_t DecodeWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const uint8_t* table_stream, size_t table_stream_size, - const uint8_t* compressed, size_t len, - TestImage* output); -void DecodeWithLibjpeg(const CompressParams& jparams, - const DecompressParams& dparams, - const std::vector& compressed, - TestImage* output); - -} // namespace jpegli - -#endif // LIB_JPEGLI_LIBJPEG_TEST_UTIL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc deleted file mode 100644 index a2b333afef2c7..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file contains wrapper-functions that are used to build the libjpeg.so -// shared library that is API- and ABI-compatible with libjpeg-turbo's version -// of libjpeg.so. - -#include "lib/jpegli/common.h" -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/error.h" - -struct jpeg_error_mgr *jpeg_std_error(struct jpeg_error_mgr *err) { - return jpegli_std_error(err); -} - -void jpeg_abort(j_common_ptr cinfo) { jpegli_abort(cinfo); } - -void jpeg_destroy(j_common_ptr cinfo) { jpegli_destroy(cinfo); } - -JQUANT_TBL *jpeg_alloc_quant_table(j_common_ptr cinfo) { - return jpegli_alloc_quant_table(cinfo); -} - -JHUFF_TBL *jpeg_alloc_huff_table(j_common_ptr cinfo) { - return jpegli_alloc_huff_table(cinfo); -} - -void jpeg_CreateDecompress(j_decompress_ptr cinfo, int version, - size_t structsize) { - jpegli_CreateDecompress(cinfo, version, structsize); -} - -void jpeg_stdio_src(j_decompress_ptr cinfo, FILE *infile) { - jpegli_stdio_src(cinfo, infile); -} - -void jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *inbuffer, - unsigned long insize /* NOLINT */) { - jpegli_mem_src(cinfo, inbuffer, insize); -} - -int jpeg_read_header(j_decompress_ptr cinfo, boolean require_image) { - return jpegli_read_header(cinfo, require_image); -} - -boolean jpeg_start_decompress(j_decompress_ptr cinfo) { - return jpegli_start_decompress(cinfo); -} - -JDIMENSION jpeg_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION max_lines) { - return jpegli_read_scanlines(cinfo, scanlines, max_lines); -} - -JDIMENSION jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { - return jpegli_skip_scanlines(cinfo, num_lines); -} - -void jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, - JDIMENSION *width) { - jpegli_crop_scanline(cinfo, xoffset, width); -} - -boolean jpeg_finish_decompress(j_decompress_ptr cinfo) { - return jpegli_finish_decompress(cinfo); -} - -JDIMENSION jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION max_lines) { - return jpegli_read_raw_data(cinfo, data, max_lines); -} - -jvirt_barray_ptr *jpeg_read_coefficients(j_decompress_ptr cinfo) { - return jpegli_read_coefficients(cinfo); -} - -boolean jpeg_has_multiple_scans(j_decompress_ptr cinfo) { - return jpegli_has_multiple_scans(cinfo); -} - -boolean jpeg_start_output(j_decompress_ptr cinfo, int scan_number) { - return jpegli_start_output(cinfo, scan_number); -} - -boolean jpeg_finish_output(j_decompress_ptr cinfo) { - return jpegli_finish_output(cinfo); -} - -boolean jpeg_input_complete(j_decompress_ptr cinfo) { - return jpegli_input_complete(cinfo); -} - -int jpeg_consume_input(j_decompress_ptr cinfo) { - return jpegli_consume_input(cinfo); -} - -#if JPEG_LIB_VERSION >= 80 -void jpeg_core_output_dimensions(j_decompress_ptr cinfo) { - jpegli_core_output_dimensions(cinfo); -} -#endif -void jpeg_calc_output_dimensions(j_decompress_ptr cinfo) { - jpegli_calc_output_dimensions(cinfo); -} - -void jpeg_save_markers(j_decompress_ptr cinfo, int marker_code, - unsigned int length_limit) { - jpegli_save_markers(cinfo, marker_code, length_limit); -} - -void jpeg_set_marker_processor(j_decompress_ptr cinfo, int marker_code, - jpeg_marker_parser_method routine) { - jpegli_set_marker_processor(cinfo, marker_code, routine); -} - -boolean jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, - unsigned int *icc_data_len) { - return jpegli_read_icc_profile(cinfo, icc_data_ptr, icc_data_len); -} - -void jpeg_abort_decompress(j_decompress_ptr cinfo) { - jpegli_abort_decompress(cinfo); -} - -void jpeg_destroy_decompress(j_decompress_ptr cinfo) { - jpegli_destroy_decompress(cinfo); -} - -void jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) { - jpegli_CreateCompress(cinfo, version, structsize); -} - -void jpeg_stdio_dest(j_compress_ptr cinfo, FILE *outfile) { - jpegli_stdio_dest(cinfo, outfile); -} - -void jpeg_mem_dest(j_compress_ptr cinfo, unsigned char **outbuffer, - unsigned long *outsize /* NOLINT */) { - jpegli_mem_dest(cinfo, outbuffer, outsize); -} - -void jpeg_set_defaults(j_compress_ptr cinfo) { jpegli_set_defaults(cinfo); } - -void jpeg_default_colorspace(j_compress_ptr cinfo) { - jpegli_default_colorspace(cinfo); -} - -void jpeg_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { - jpegli_set_colorspace(cinfo, colorspace); -} - -void jpeg_set_quality(j_compress_ptr cinfo, int quality, - boolean force_baseline) { - jpegli_set_quality(cinfo, quality, force_baseline); -} - -void jpeg_set_linear_quality(j_compress_ptr cinfo, int scale_factor, - boolean force_baseline) { - jpegli_set_linear_quality(cinfo, scale_factor, force_baseline); -} - -#if JPEG_LIB_VERSION >= 70 -void jpeg_default_qtables(j_compress_ptr cinfo, boolean force_baseline) { - jpegli_default_qtables(cinfo, force_baseline); -} -#endif - -int jpeg_quality_scaling(int quality) { - return jpegli_quality_scaling(quality); -} - -void jpeg_add_quant_table(j_compress_ptr cinfo, int which_tbl, - const unsigned int *basic_table, int scale_factor, - boolean force_baseline) { - jpegli_add_quant_table(cinfo, which_tbl, basic_table, scale_factor, - force_baseline); -} - -void jpeg_simple_progression(j_compress_ptr cinfo) { - jpegli_simple_progression(cinfo); -} - -void jpeg_suppress_tables(j_compress_ptr cinfo, boolean suppress) { - jpegli_suppress_tables(cinfo, suppress); -} - -#if JPEG_LIB_VERSION >= 70 -void jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo) { - jpegli_calc_jpeg_dimensions(cinfo); -} -#endif - -void jpeg_copy_critical_parameters(j_decompress_ptr srcinfo, - j_compress_ptr dstinfo) { - jpegli_copy_critical_parameters(srcinfo, dstinfo); -} - -void jpeg_write_m_header(j_compress_ptr cinfo, int marker, - unsigned int datalen) { - jpegli_write_m_header(cinfo, marker, datalen); -} - -void jpeg_write_m_byte(j_compress_ptr cinfo, int val) { - jpegli_write_m_byte(cinfo, val); -} - -void jpeg_write_marker(j_compress_ptr cinfo, int marker, const JOCTET *dataptr, - unsigned int datalen) { - jpegli_write_marker(cinfo, marker, dataptr, datalen); -} - -void jpeg_write_icc_profile(j_compress_ptr cinfo, const JOCTET *icc_data_ptr, - unsigned int icc_data_len) { - jpegli_write_icc_profile(cinfo, icc_data_ptr, icc_data_len); -} - -void jpeg_start_compress(j_compress_ptr cinfo, boolean write_all_tables) { - jpegli_start_compress(cinfo, write_all_tables); -} - -void jpeg_write_tables(j_compress_ptr cinfo) { jpegli_write_tables(cinfo); } - -JDIMENSION jpeg_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines, - JDIMENSION num_lines) { - return jpegli_write_scanlines(cinfo, scanlines, num_lines); -} - -JDIMENSION jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, - JDIMENSION num_lines) { - return jpegli_write_raw_data(cinfo, data, num_lines); -} - -void jpeg_write_coefficients(j_compress_ptr cinfo, - jvirt_barray_ptr *coef_arrays) { - jpegli_write_coefficients(cinfo, coef_arrays); -} - -void jpeg_finish_compress(j_compress_ptr cinfo) { - jpegli_finish_compress(cinfo); -} - -void jpeg_abort_compress(j_compress_ptr cinfo) { jpegli_abort_compress(cinfo); } - -void jpeg_destroy_compress(j_compress_ptr cinfo) { - jpegli_destroy_compress(cinfo); -} - -boolean jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired) { - return jpegli_resync_to_restart(cinfo, desired); -} - -void jpeg_new_colormap(j_decompress_ptr cinfo) { jpegli_new_colormap(cinfo); } diff --git a/third_party/jpeg-xl/lib/jpegli/memory_manager.cc b/third_party/jpeg-xl/lib/jpegli/memory_manager.cc deleted file mode 100644 index 3a8f230e63800..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/memory_manager.cc +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/memory_manager.h" - -#include - -#include -#include - -#include "lib/jpegli/common_internal.h" -#include "lib/jpegli/error.h" - -struct jvirt_sarray_control { - JSAMPARRAY full_buffer; - size_t numrows; - JDIMENSION maxaccess; -}; - -struct jvirt_barray_control { - JBLOCKARRAY full_buffer; - size_t numrows; - JDIMENSION maxaccess; -}; - -namespace jpegli { - -namespace { - -struct MemoryManager { - struct jpeg_memory_mgr pub; - std::vector owned_ptrs[2 * JPOOL_NUMPOOLS]; - uint64_t pool_memory_usage[2 * JPOOL_NUMPOOLS]; - uint64_t total_memory_usage; - uint64_t peak_memory_usage; -}; - -void* Alloc(j_common_ptr cinfo, int pool_id, size_t sizeofobject) { - MemoryManager* mem = reinterpret_cast(cinfo->mem); - if (pool_id < 0 || pool_id >= 2 * JPOOL_NUMPOOLS) { - JPEGLI_ERROR("Invalid pool id %d", pool_id); - } - if (mem->pub.max_memory_to_use > 0 && - mem->total_memory_usage + static_cast(sizeofobject) > - static_cast(mem->pub.max_memory_to_use)) { - JPEGLI_ERROR("Total memory usage exceeding %ld", - mem->pub.max_memory_to_use); - } - void* p; - if (pool_id < JPOOL_NUMPOOLS) { - p = malloc(sizeofobject); - } else { - p = hwy::AllocateAlignedBytes(sizeofobject, nullptr, nullptr); - } - if (p == nullptr) { - JPEGLI_ERROR("Out of memory"); - } - mem->owned_ptrs[pool_id].push_back(p); - mem->pool_memory_usage[pool_id] += sizeofobject; - mem->total_memory_usage += sizeofobject; - mem->peak_memory_usage = - std::max(mem->peak_memory_usage, mem->total_memory_usage); - return p; -} - -constexpr size_t gcd(size_t a, size_t b) { return b == 0 ? a : gcd(b, a % b); } -constexpr size_t lcm(size_t a, size_t b) { return (a * b) / gcd(a, b); } - -template -T** Alloc2dArray(j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, - JDIMENSION numrows) { - T** array = Allocate(cinfo, numrows, pool_id); - // Always use aligned allocator for large 2d arrays. - if (pool_id < JPOOL_NUMPOOLS) { - pool_id += JPOOL_NUMPOOLS; - } - size_t alignment = lcm(sizeof(T), HWY_ALIGNMENT); - size_t memstride = RoundUpTo(samplesperrow * sizeof(T), alignment); - size_t stride = memstride / sizeof(T); - T* buffer = Allocate(cinfo, numrows * stride, pool_id); - for (size_t i = 0; i < numrows; ++i) { - array[i] = &buffer[i * stride]; - } - return array; -} - -template -Control* RequestVirtualArray(j_common_ptr cinfo, int pool_id, boolean pre_zero, - JDIMENSION samplesperrow, JDIMENSION numrows, - JDIMENSION maxaccess) { - if (pool_id != JPOOL_IMAGE) { - JPEGLI_ERROR("Only image lifetime virtual arrays are supported."); - } - Control* p = Allocate(cinfo, 1, pool_id); - p->full_buffer = Alloc2dArray(cinfo, pool_id, samplesperrow, numrows); - p->numrows = numrows; - p->maxaccess = maxaccess; - if (pre_zero) { - for (size_t i = 0; i < numrows; ++i) { - memset(p->full_buffer[i], 0, samplesperrow * sizeof(T)); - } - } - return p; -} - -void RealizeVirtualArrays(j_common_ptr cinfo) { - // Nothing to do, the full arrays were realized at request time already. -} - -template -T** AccessVirtualArray(j_common_ptr cinfo, Control* ptr, JDIMENSION start_row, - JDIMENSION num_rows, boolean writable) { - if (num_rows > ptr->maxaccess) { - JPEGLI_ERROR("Invalid virtual array access, num rows %u vs max rows %u", - num_rows, ptr->maxaccess); - } - if (start_row + num_rows > ptr->numrows) { - JPEGLI_ERROR("Invalid virtual array access, %u vs %u total rows", - start_row + num_rows, ptr->numrows); - } - if (ptr->full_buffer == nullptr) { - JPEGLI_ERROR("Invalid virtual array access, array not realized."); - } - return ptr->full_buffer + start_row; -} - -void ClearPool(j_common_ptr cinfo, int pool_id) { - MemoryManager* mem = reinterpret_cast(cinfo->mem); - mem->owned_ptrs[pool_id].clear(); - mem->total_memory_usage -= mem->pool_memory_usage[pool_id]; - mem->pool_memory_usage[pool_id] = 0; -} - -void FreePool(j_common_ptr cinfo, int pool_id) { - MemoryManager* mem = reinterpret_cast(cinfo->mem); - if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) { - JPEGLI_ERROR("Invalid pool id %d", pool_id); - } - for (void* ptr : mem->owned_ptrs[pool_id]) { - free(ptr); - } - ClearPool(cinfo, pool_id); - for (void* ptr : mem->owned_ptrs[JPOOL_NUMPOOLS + pool_id]) { - hwy::FreeAlignedBytes(ptr, nullptr, nullptr); - } - ClearPool(cinfo, JPOOL_NUMPOOLS + pool_id); -} - -void SelfDestruct(j_common_ptr cinfo) { - MemoryManager* mem = reinterpret_cast(cinfo->mem); - for (int pool_id = 0; pool_id < JPOOL_NUMPOOLS; ++pool_id) { - FreePool(cinfo, pool_id); - } - delete mem; - cinfo->mem = nullptr; -} - -} // namespace - -void InitMemoryManager(j_common_ptr cinfo) { - MemoryManager* mem = new MemoryManager; - mem->pub.alloc_small = jpegli::Alloc; - mem->pub.alloc_large = jpegli::Alloc; - mem->pub.alloc_sarray = jpegli::Alloc2dArray; - mem->pub.alloc_barray = jpegli::Alloc2dArray; - mem->pub.request_virt_sarray = - jpegli::RequestVirtualArray; - mem->pub.request_virt_barray = - jpegli::RequestVirtualArray; - mem->pub.realize_virt_arrays = jpegli::RealizeVirtualArrays; - mem->pub.access_virt_sarray = - jpegli::AccessVirtualArray; - mem->pub.access_virt_barray = - jpegli::AccessVirtualArray; - mem->pub.free_pool = jpegli::FreePool; - mem->pub.self_destruct = jpegli::SelfDestruct; - mem->pub.max_memory_to_use = 0; - mem->total_memory_usage = 0; - mem->peak_memory_usage = 0; - memset(mem->pool_memory_usage, 0, sizeof(mem->pool_memory_usage)); - cinfo->mem = reinterpret_cast(mem); -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/memory_manager.h b/third_party/jpeg-xl/lib/jpegli/memory_manager.h deleted file mode 100644 index c650caad49ed8..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/memory_manager.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_MEMORY_MANAGER_H_ -#define LIB_JPEGLI_MEMORY_MANAGER_H_ - -#include - -#include "lib/jpegli/common.h" - -#define JPOOL_PERMANENT_ALIGNED (JPOOL_NUMPOOLS + JPOOL_PERMANENT) -#define JPOOL_IMAGE_ALIGNED (JPOOL_NUMPOOLS + JPOOL_IMAGE) - -namespace jpegli { - -void InitMemoryManager(j_common_ptr cinfo); - -template -T* Allocate(j_common_ptr cinfo, size_t len, int pool_id = JPOOL_PERMANENT) { - const size_t size = len * sizeof(T); // NOLINT - void* p = (*cinfo->mem->alloc_small)(cinfo, pool_id, size); - return reinterpret_cast(p); -} - -template -T* Allocate(j_decompress_ptr cinfo, size_t len, int pool_id = JPOOL_PERMANENT) { - return Allocate(reinterpret_cast(cinfo), len, pool_id); -} - -template -T* Allocate(j_compress_ptr cinfo, size_t len, int pool_id = JPOOL_PERMANENT) { - return Allocate(reinterpret_cast(cinfo), len, pool_id); -} - -template -JBLOCKARRAY GetBlockRow(T cinfo, int c, JDIMENSION by) { - return (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), cinfo->master->coeff_buffers[c], - by, 1, true); -} - -} // namespace jpegli - -#endif // LIB_JPEGLI_MEMORY_MANAGER_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc deleted file mode 100644 index 44d63fdcbb4d0..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/encode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" - -namespace jpegli { -namespace { - -constexpr size_t kInitialBufferSize = 1024; -constexpr size_t kFinalBufferSize = 18; - -struct DestinationManager { - jpeg_destination_mgr pub; - std::vector buffer; - - DestinationManager() { - pub.init_destination = init_destination; - pub.empty_output_buffer = empty_output_buffer; - pub.term_destination = term_destination; - } - - void Rewind() { - pub.next_output_byte = buffer.data(); - pub.free_in_buffer = buffer.size(); - } - - void EmptyTo(std::vector* output, size_t new_size = 0) { - output->insert(output->end(), buffer.data(), pub.next_output_byte); - if (new_size > 0) { - buffer.resize(new_size); - } - Rewind(); - } - - static void init_destination(j_compress_ptr cinfo) { - auto* us = reinterpret_cast(cinfo->dest); - us->buffer.resize(kInitialBufferSize); - us->Rewind(); - } - - static boolean empty_output_buffer(j_compress_ptr cinfo) { return FALSE; } - - static void term_destination(j_compress_ptr cinfo) {} -}; - -struct TestConfig { - TestImage input; - CompressParams jparams; - size_t buffer_size; - size_t lines_batch_size; -}; - -class OutputSuspensionTestParam : public ::testing::TestWithParam { -}; - -TEST_P(OutputSuspensionTestParam, PixelData) { - jpeg_compress_struct cinfo = {}; - TestConfig config = GetParam(); - TestImage& input = config.input; - GeneratePixels(&input); - DestinationManager dest; - std::vector compressed; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.dest = reinterpret_cast(&dest); - - cinfo.image_width = input.xsize; - cinfo.image_height = input.ysize; - cinfo.input_components = input.components; - cinfo.in_color_space = JCS_RGB; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].v_samp_factor = config.jparams.v_sampling[0]; - jpegli_set_progressive_level(&cinfo, 0); - cinfo.optimize_coding = FALSE; - jpegli_start_compress(&cinfo, TRUE); - - size_t stride = cinfo.image_width * cinfo.input_components; - std::vector row_bytes(config.lines_batch_size * stride); - while (cinfo.next_scanline < cinfo.image_height) { - size_t lines_left = cinfo.image_height - cinfo.next_scanline; - size_t num_lines = std::min(config.lines_batch_size, lines_left); - memcpy(row_bytes.data(), &input.pixels[cinfo.next_scanline * stride], - num_lines * stride); - std::vector rows(num_lines); - for (size_t i = 0; i < num_lines; ++i) { - rows[i] = &row_bytes[i * stride]; - } - size_t lines_done = 0; - while (lines_done < num_lines) { - lines_done += jpegli_write_scanlines(&cinfo, &rows[lines_done], - num_lines - lines_done); - if (lines_done < num_lines) { - dest.EmptyTo(&compressed, config.buffer_size); - } - } - } - dest.EmptyTo(&compressed, kFinalBufferSize); - jpegli_finish_compress(&cinfo); - dest.EmptyTo(&compressed); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_compress(&cinfo); - TestImage output; - DecodeWithLibjpeg(CompressParams(), DecompressParams(), compressed, &output); - VerifyOutputImage(input, output, 2.5); -} - -TEST_P(OutputSuspensionTestParam, RawData) { - jpeg_compress_struct cinfo = {}; - TestConfig config = GetParam(); - if (config.lines_batch_size != 1) return; - TestImage& input = config.input; - input.color_space = JCS_YCbCr; - GeneratePixels(&input); - GenerateRawData(config.jparams, &input); - DestinationManager dest; - std::vector compressed; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - cinfo.dest = reinterpret_cast(&dest); - cinfo.image_width = input.xsize; - cinfo.image_height = input.ysize; - cinfo.input_components = input.components; - cinfo.in_color_space = JCS_YCbCr; - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].h_samp_factor = config.jparams.h_sampling[0]; - cinfo.comp_info[0].v_samp_factor = config.jparams.v_sampling[0]; - jpegli_set_progressive_level(&cinfo, 0); - cinfo.optimize_coding = FALSE; - cinfo.raw_data_in = TRUE; - jpegli_start_compress(&cinfo, TRUE); - - std::vector> raw_data = input.raw_data; - size_t max_lines = config.jparams.max_v_sample() * DCTSIZE; - std::vector> rowdata(cinfo.num_components); - std::vector data(cinfo.num_components); - for (int c = 0; c < cinfo.num_components; ++c) { - rowdata[c].resize(config.jparams.v_samp(c) * DCTSIZE); - data[c] = rowdata[c].data(); - } - while (cinfo.next_scanline < cinfo.image_height) { - for (int c = 0; c < cinfo.num_components; ++c) { - size_t cwidth = cinfo.comp_info[c].width_in_blocks * DCTSIZE; - size_t cheight = cinfo.comp_info[c].height_in_blocks * DCTSIZE; - size_t num_lines = config.jparams.v_samp(c) * DCTSIZE; - size_t y0 = (cinfo.next_scanline / max_lines) * num_lines; - for (size_t i = 0; i < num_lines; ++i) { - rowdata[c][i] = - (y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr); - } - } - while (jpegli_write_raw_data(&cinfo, data.data(), max_lines) == 0) { - dest.EmptyTo(&compressed, config.buffer_size); - } - } - dest.EmptyTo(&compressed, kFinalBufferSize); - jpegli_finish_compress(&cinfo); - dest.EmptyTo(&compressed); - return true; - }; - try_catch_block(); - jpegli_destroy_compress(&cinfo); - DecompressParams dparams; - dparams.output_mode = RAW_DATA; - TestImage output; - DecodeWithLibjpeg(CompressParams(), dparams, compressed, &output); - VerifyOutputImage(input, output, 3.5); -} - -std::vector GenerateTests() { - std::vector all_tests; - const size_t xsize0 = 1920; - const size_t ysize0 = 1080; - for (int dysize : {0, 1, 8, 9}) { - for (int v_sampling : {1, 2}) { - for (int nlines : {1, 8, 117}) { - for (int bufsize : {1, 16, 16 << 10}) { - TestConfig config; - config.lines_batch_size = nlines; - config.buffer_size = bufsize; - config.input.xsize = xsize0; - config.input.ysize = ysize0 + dysize; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {v_sampling, 1, 1}; - all_tests.push_back(config); - } - } - } - } - return all_tests; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - os << c.input; - os << c.jparams; - os << "Lines" << c.lines_batch_size; - os << "BufSize" << c.buffer_size; - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(OutputSuspensionTest, OutputSuspensionTestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/quant.cc b/third_party/jpeg-xl/lib/jpegli/quant.cc deleted file mode 100644 index 14db6701b2e64..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/quant.cc +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/quant.h" - -#include -#include -#include - -#include "lib/jpegli/adaptive_quantization.h" -#include "lib/jpegli/common.h" -#include "lib/jpegli/encode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { - -namespace { - -// Global scale is chosen in a way that butteraugli 3-norm matches libjpeg -// with the same quality setting. Fitted for quality 90 on jyrki31 corpus. -constexpr float kGlobalScaleXYB = 1.43951668f; -constexpr float kGlobalScaleYCbCr = 1.73966010f; - -constexpr float kBaseQuantMatrixXYB[] = { - // c = 0 - 7.5629935265f, - 19.8247814178f, - 22.5724945068f, - 20.6706695557f, - 22.6864585876f, - 23.5696277618f, - 25.8129081726f, - 36.3307571411f, - 19.8247814178f, - 21.5503177643f, - 19.9372234344f, - 20.5424213409f, - 21.8645496368f, - 23.9041385651f, - 28.2844066620f, - 32.6609764099f, - 22.5724945068f, - 19.9372234344f, - 21.9017257690f, - 19.1223449707f, - 21.7515811920f, - 24.6724700928f, - 25.4249649048f, - 32.6653823853f, - 20.6706695557f, - 20.5424213409f, - 19.1223449707f, - 20.1610221863f, - 25.3719692230f, - 25.9668903351f, - 30.9804954529f, - 31.3406009674f, - 22.6864585876f, - 21.8645496368f, - 21.7515811920f, - 25.3719692230f, - 26.2431850433f, - 40.5992202759f, - 43.2624626160f, - 63.3010940552f, - 23.5696277618f, - 23.9041385651f, - 24.6724700928f, - 25.9668903351f, - 40.5992202759f, - 48.3026771545f, - 34.0964355469f, - 61.9852142334f, - 25.8129081726f, - 28.2844066620f, - 25.4249649048f, - 30.9804954529f, - 43.2624626160f, - 34.0964355469f, - 34.4937438965f, - 66.9702758789f, - 36.3307571411f, - 32.6609764099f, - 32.6653823853f, - 31.3406009674f, - 63.3010940552f, - 61.9852142334f, - 66.9702758789f, - 39.9652709961f, - // c = 1 - 1.6262000799f, - 3.2199242115f, - 3.4903779030f, - 3.9148359299f, - 4.8337211609f, - 4.9108843803f, - 5.3137121201f, - 6.1676793098f, - 3.2199242115f, - 3.4547898769f, - 3.6036829948f, - 4.2652835846f, - 4.8368387222f, - 4.8226222992f, - 5.6120514870f, - 6.3431472778f, - 3.4903779030f, - 3.6036829948f, - 3.9044559002f, - 4.3374395370f, - 4.8435096741f, - 5.4057979584f, - 5.6066360474f, - 6.1075134277f, - 3.9148359299f, - 4.2652835846f, - 4.3374395370f, - 4.6064834595f, - 5.1751475334f, - 5.4013924599f, - 6.0399808884f, - 6.7825231552f, - 4.8337211609f, - 4.8368387222f, - 4.8435096741f, - 5.1751475334f, - 5.3748049736f, - 6.1410837173f, - 7.6529307365f, - 7.5235214233f, - 4.9108843803f, - 4.8226222992f, - 5.4057979584f, - 5.4013924599f, - 6.1410837173f, - 6.3431472778f, - 7.1083049774f, - 7.6008300781f, - 5.3137121201f, - 5.6120514870f, - 5.6066360474f, - 6.0399808884f, - 7.6529307365f, - 7.1083049774f, - 7.0943155289f, - 7.0478363037f, - 6.1676793098f, - 6.3431472778f, - 6.1075134277f, - 6.7825231552f, - 7.5235214233f, - 7.6008300781f, - 7.0478363037f, - 6.9186143875f, - // c = 2 - 3.3038473129f, - 10.0689258575f, - 12.2785224915f, - 14.6041173935f, - 16.2107315063f, - 19.2314529419f, - 28.0129547119f, - 55.6682891846f, - 10.0689258575f, - 11.4085016251f, - 11.3871345520f, - 15.4934167862f, - 16.5364933014f, - 14.9153423309f, - 26.3748722076f, - 40.8614425659f, - 12.2785224915f, - 11.3871345520f, - 17.0886878967f, - 13.9500350952f, - 16.0003223419f, - 28.5660629272f, - 26.2124195099f, - 30.1260128021f, - 14.6041173935f, - 15.4934167862f, - 13.9500350952f, - 21.1235027313f, - 26.1579780579f, - 25.5579223633f, - 40.6859359741f, - 33.8056335449f, - 16.2107315063f, - 16.5364933014f, - 16.0003223419f, - 26.1579780579f, - 26.8042831421f, - 26.1587715149f, - 35.7343978882f, - 43.6857032776f, - 19.2314529419f, - 14.9153423309f, - 28.5660629272f, - 25.5579223633f, - 26.1587715149f, - 34.5418128967f, - 41.3197937012f, - 48.7867660522f, - 28.0129547119f, - 26.3748722076f, - 26.2124195099f, - 40.6859359741f, - 35.7343978882f, - 41.3197937012f, - 47.6329460144f, - 55.3498458862f, - 55.6682891846f, - 40.8614425659f, - 30.1260128021f, - 33.8056335449f, - 43.6857032776f, - 48.7867660522f, - 55.3498458862f, - 63.6065597534f, -}; - -const float kBaseQuantMatrixYCbCr[] = { - // c = 0 - 1.2397409345866273f, // - 1.7227115097630963f, // - 2.9212167156636855f, // - 2.812737435286529f, // - 3.339819711906184f, // - 3.463603762596166f, // - 3.840915217993518f, // - 3.86956f, // - 1.7227115097630963f, // - 2.0928894413636874f, // - 2.8456760904429297f, // - 2.704506820909662f, // - 3.4407673520905337f, // - 3.166232352090534f, // - 4.025208741558432f, // - 4.035324490952577f, // - 2.9212167156636855f, // - 2.8456760904429297f, // - 2.9587403520905338f, // - 3.3862948970669273f, // - 3.619523781336757f, // - 3.9046279999999998f, // - 3.757835838431854f, // - 4.237447515714274f, // - 2.812737435286529f, // - 2.704506820909662f, // - 3.3862948970669273f, // - 3.380058821812233f, // - 4.1679867415584315f, // - 4.805510627261856f, // - 4.784259f, // - 4.605934f, // - 3.339819711906184f, // - 3.4407673520905337f, // - 3.619523781336757f, // - 4.1679867415584315f, // - 4.579851258441568f, // - 4.923237f, // - 5.574107f, // - 5.48533336146308f, // - 3.463603762596166f, // - 3.166232352090534f, // - 3.9046279999999998f, // - 4.805510627261856f, // - 4.923237f, // - 5.43936f, // - 5.093895741558431f, // - 6.0872254423617225f, // - 3.840915217993518f, // - 4.025208741558432f, // - 3.757835838431854f, // - 4.784259f, // - 5.574107f, // - 5.093895741558431f, // - 5.438461f, // - 5.4037359493250845f, // - 3.86956f, // - 4.035324490952577f, // - 4.237447515714274f, // - 4.605934f, // - 5.48533336146308f, // - 6.0872254423617225f, // - 5.4037359493250845f, // - 4.37787101190424f, - // c = 1 - 2.8236197786377537f, // - 6.495639358561486f, // - 9.310489207538302f, // - 10.64747864717083f, // - 11.07419143098738f, // - 17.146390223910462f, // - 18.463982229408998f, // - 29.087001644203088f, // - 6.495639358561486f, // - 8.890103846667353f, // - 8.976895794294748f, // - 13.666270550318826f, // - 16.547071905624193f, // - 16.63871382827686f, // - 26.778396930893695f, // - 21.33034294694781f, // - 9.310489207538302f, // - 8.976895794294748f, // - 11.08737706005991f, // - 18.20548239870446f, // - 19.752481654011646f, // - 23.985660533114896f, // - 102.6457378402362f, // - 24.450989f, // - 10.64747864717083f, // - 13.666270550318826f, // - 18.20548239870446f, // - 18.628012327860365f, // - 16.042509519487183f, // - 25.04918273242625f, // - 25.017140189353015f, // - 35.79788782635831f, // - 11.07419143098738f, // - 16.547071905624193f, // - 19.752481654011646f, // - 16.042509519487183f, // - 19.373482748612577f, // - 14.677529999999999f, // - 19.94695960400931f, // - 51.094112f, // - 17.146390223910462f, // - 16.63871382827686f, // - 23.985660533114896f, // - 25.04918273242625f, // - 14.677529999999999f, // - 31.320412426835304f, // - 46.357234000000005f, // - 67.48111451705412f, // - 18.463982229408998f, // - 26.778396930893695f, // - 102.6457378402362f, // - 25.017140189353015f, // - 19.94695960400931f, // - 46.357234000000005f, // - 61.315764694388044f, // - 88.34665293823721f, // - 29.087001644203088f, // - 21.33034294694781f, // - 24.450989f, // - 35.79788782635831f, // - 51.094112f, // - 67.48111451705412f, // - 88.34665293823721f, // - 112.16099098350989f, - // c = 2 - 2.9217254961255255f, // - 4.497681013199305f, // - 7.356344520940414f, // - 6.583891506504051f, // - 8.535608740100237f, // - 8.799434353234647f, // - 9.188341534163023f, // - 9.482700481227672f, // - 4.497681013199305f, // - 6.309548851989123f, // - 7.024608962670982f, // - 7.156445324163424f, // - 8.049059218663244f, // - 7.0124290657218555f, // - 6.711923184393611f, // - 8.380307846134853f, // - 7.356344520940414f, // - 7.024608962670982f, // - 6.892101177327445f, // - 6.882819916277163f, // - 8.782226090078568f, // - 6.8774750000000004f, // - 7.8858175969577955f, // - 8.67909f, // - 6.583891506504051f, // - 7.156445324163424f, // - 6.882819916277163f, // - 7.003072944847055f, // - 7.7223464701024875f, // - 7.955425720217421f, // - 7.4734110000000005f, // - 8.362933242943903f, // - 8.535608740100237f, // - 8.049059218663244f, // - 8.782226090078568f, // - 7.7223464701024875f, // - 6.778005927001542f, // - 9.484922741558432f, // - 9.043702663686046f, // - 8.053178199770173f, // - 8.799434353234647f, // - 7.0124290657218555f, // - 6.8774750000000004f, // - 7.955425720217421f, // - 9.484922741558432f, // - 8.607606527385098f, // - 9.922697394370815f, // - 64.25135180237939f, // - 9.188341534163023f, // - 6.711923184393611f, // - 7.8858175969577955f, // - 7.4734110000000005f, // - 9.043702663686046f, // - 9.922697394370815f, // - 63.184936549738225f, // - 83.35294340273799f, // - 9.482700481227672f, // - 8.380307846134853f, // - 8.67909f, // - 8.362933242943903f, // - 8.053178199770173f, // - 64.25135180237939f, // - 83.35294340273799f, // - 114.89202448569779f, // -}; - -const float k420GlobalScale = 1.22; -const float k420Rescale[64] = { - 0.4093, 0.3209, 0.3477, 0.3333, 0.3144, 0.2823, 0.3214, 0.3354, // - 0.3209, 0.3111, 0.3489, 0.2801, 0.3059, 0.3119, 0.4135, 0.3445, // - 0.3477, 0.3489, 0.3586, 0.3257, 0.2727, 0.3754, 0.3369, 0.3484, // - 0.3333, 0.2801, 0.3257, 0.3020, 0.3515, 0.3410, 0.3971, 0.3839, // - 0.3144, 0.3059, 0.2727, 0.3515, 0.3105, 0.3397, 0.2716, 0.3836, // - 0.2823, 0.3119, 0.3754, 0.3410, 0.3397, 0.3212, 0.3203, 0.0726, // - 0.3214, 0.4135, 0.3369, 0.3971, 0.2716, 0.3203, 0.0798, 0.0553, // - 0.3354, 0.3445, 0.3484, 0.3839, 0.3836, 0.0726, 0.0553, 0.3368, // -}; - -const float kBaseQuantMatrixStd[] = { - // c = 0 - 16.0f, 11.0f, 10.0f, 16.0f, 24.0f, 40.0f, 51.0f, 61.0f, // - 12.0f, 12.0f, 14.0f, 19.0f, 26.0f, 58.0f, 60.0f, 55.0f, // - 14.0f, 13.0f, 16.0f, 24.0f, 40.0f, 57.0f, 69.0f, 56.0f, // - 14.0f, 17.0f, 22.0f, 29.0f, 51.0f, 87.0f, 80.0f, 62.0f, // - 18.0f, 22.0f, 37.0f, 56.0f, 68.0f, 109.0f, 103.0f, 77.0f, // - 24.0f, 35.0f, 55.0f, 64.0f, 81.0f, 104.0f, 113.0f, 92.0f, // - 49.0f, 64.0f, 78.0f, 87.0f, 103.0f, 121.0f, 120.0f, 101.0f, // - 72.0f, 92.0f, 95.0f, 98.0f, 112.0f, 100.0f, 103.0f, 99.0f, // - // c = 1 - 17.0f, 18.0f, 24.0f, 47.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 18.0f, 21.0f, 26.0f, 66.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 24.0f, 26.0f, 56.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 47.0f, 66.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // - 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // -}; - -const float kZeroBiasMulYCbCrLQ[] = { - // c = 0 - 0.0000f, 0.0568f, 0.3880f, 0.6190f, 0.6190f, 0.4490f, 0.4490f, 0.6187f, // - 0.0568f, 0.5829f, 0.6189f, 0.6190f, 0.6190f, 0.7190f, 0.6190f, 0.6189f, // - 0.3880f, 0.6189f, 0.6190f, 0.6190f, 0.6190f, 0.6190f, 0.6187f, 0.6100f, // - 0.6190f, 0.6190f, 0.6190f, 0.6190f, 0.5890f, 0.3839f, 0.7160f, 0.6190f, // - 0.6190f, 0.6190f, 0.6190f, 0.5890f, 0.6190f, 0.3880f, 0.5860f, 0.4790f, // - 0.4490f, 0.7190f, 0.6190f, 0.3839f, 0.3880f, 0.6190f, 0.6190f, 0.6190f, // - 0.4490f, 0.6190f, 0.6187f, 0.7160f, 0.5860f, 0.6190f, 0.6204f, 0.6190f, // - 0.6187f, 0.6189f, 0.6100f, 0.6190f, 0.4790f, 0.6190f, 0.6190f, 0.3480f, // - // c = 1 - 0.0000f, 1.1640f, 0.9373f, 1.1319f, 0.8016f, 0.9136f, 1.1530f, 0.9430f, // - 1.1640f, 0.9188f, 0.9160f, 1.1980f, 1.1830f, 0.9758f, 0.9430f, 0.9430f, // - 0.9373f, 0.9160f, 0.8430f, 1.1720f, 0.7083f, 0.9430f, 0.9430f, 0.9430f, // - 1.1319f, 1.1980f, 1.1720f, 1.1490f, 0.8547f, 0.9430f, 0.9430f, 0.9430f, // - 0.8016f, 1.1830f, 0.7083f, 0.8547f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, // - 0.9136f, 0.9758f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, // - 1.1530f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9480f, // - 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9430f, 0.9480f, 0.9430f, // - // c = 2 - 0.0000f, 1.3190f, 0.4308f, 0.4460f, 0.0661f, 0.0660f, 0.2660f, 0.2960f, // - 1.3190f, 0.3280f, 0.3093f, 0.0750f, 0.0505f, 0.1594f, 0.3060f, 0.2113f, // - 0.4308f, 0.3093f, 0.3060f, 0.1182f, 0.0500f, 0.3060f, 0.3915f, 0.2426f, // - 0.4460f, 0.0750f, 0.1182f, 0.0512f, 0.0500f, 0.2130f, 0.3930f, 0.1590f, // - 0.0661f, 0.0505f, 0.0500f, 0.0500f, 0.3055f, 0.3360f, 0.5148f, 0.5403f, // - 0.0660f, 0.1594f, 0.3060f, 0.2130f, 0.3360f, 0.5060f, 0.5874f, 0.3060f, // - 0.2660f, 0.3060f, 0.3915f, 0.3930f, 0.5148f, 0.5874f, 0.3060f, 0.3060f, // - 0.2960f, 0.2113f, 0.2426f, 0.1590f, 0.5403f, 0.3060f, 0.3060f, 0.3060f, // -}; - -const float kZeroBiasMulYCbCrHQ[] = { - // c = 0 - 0.0000f, 0.0044f, 0.2521f, 0.6547f, 0.8161f, 0.6130f, 0.8841f, 0.8155f, // - 0.0044f, 0.6831f, 0.6553f, 0.6295f, 0.7848f, 0.7843f, 0.8474f, 0.7836f, // - 0.2521f, 0.6553f, 0.7834f, 0.7829f, 0.8161f, 0.8072f, 0.7743f, 0.9242f, // - 0.6547f, 0.6295f, 0.7829f, 0.8654f, 0.7829f, 0.6986f, 0.7818f, 0.7726f, // - 0.8161f, 0.7848f, 0.8161f, 0.7829f, 0.7471f, 0.7827f, 0.7843f, 0.7653f, // - 0.6130f, 0.7843f, 0.8072f, 0.6986f, 0.7827f, 0.7848f, 0.9508f, 0.7653f, // - 0.8841f, 0.8474f, 0.7743f, 0.7818f, 0.7843f, 0.9508f, 0.7839f, 0.8437f, // - 0.8155f, 0.7836f, 0.9242f, 0.7726f, 0.7653f, 0.7653f, 0.8437f, 0.7819f, // - // c = 1 - 0.0000f, 1.0816f, 1.0556f, 1.2876f, 1.1554f, 1.1567f, 1.8851f, 0.5488f, // - 1.0816f, 1.1537f, 1.1850f, 1.0712f, 1.1671f, 2.0719f, 1.0544f, 1.4764f, // - 1.0556f, 1.1850f, 1.2870f, 1.1981f, 1.8181f, 1.2618f, 1.0564f, 1.1191f, // - 1.2876f, 1.0712f, 1.1981f, 1.4753f, 2.0609f, 1.0564f, 1.2645f, 1.0564f, // - 1.1554f, 1.1671f, 1.8181f, 2.0609f, 0.7324f, 1.1163f, 0.8464f, 1.0564f, // - 1.1567f, 2.0719f, 1.2618f, 1.0564f, 1.1163f, 1.0040f, 1.0564f, 1.0564f, // - 1.8851f, 1.0544f, 1.0564f, 1.2645f, 0.8464f, 1.0564f, 1.0564f, 1.0564f, // - 0.5488f, 1.4764f, 1.1191f, 1.0564f, 1.0564f, 1.0564f, 1.0564f, 1.0564f, // - // c = 2 - 0.0000f, 0.5392f, 0.6659f, 0.8968f, 0.6829f, 0.6328f, 0.5802f, 0.4836f, // - 0.5392f, 0.6746f, 0.6760f, 0.6102f, 0.6015f, 0.6958f, 0.7327f, 0.4897f, // - 0.6659f, 0.6760f, 0.6957f, 0.6543f, 0.4396f, 0.6330f, 0.7081f, 0.2583f, // - 0.8968f, 0.6102f, 0.6543f, 0.5913f, 0.6457f, 0.5828f, 0.5139f, 0.3565f, // - 0.6829f, 0.6015f, 0.4396f, 0.6457f, 0.5633f, 0.4263f, 0.6371f, 0.5949f, // - 0.6328f, 0.6958f, 0.6330f, 0.5828f, 0.4263f, 0.2847f, 0.2909f, 0.6629f, // - 0.5802f, 0.7327f, 0.7081f, 0.5139f, 0.6371f, 0.2909f, 0.6644f, 0.6644f, // - 0.4836f, 0.4897f, 0.2583f, 0.3565f, 0.5949f, 0.6629f, 0.6644f, 0.6644f, // -}; - -const float kZeroBiasOffsetYCbCrDC[] = {0.0f, 0.0f, 0.0f}; - -const float kZeroBiasOffsetYCbCrAC[] = { - 0.59082f, - 0.58146f, - 0.57988f, -}; - -constexpr uint8_t kTransferFunctionPQ = 16; -constexpr uint8_t kTransferFunctionHLG = 18; - -float DistanceToLinearQuality(float distance) { - if (distance <= 0.1f) { - return 1.0f; - } else if (distance <= 4.6f) { - return (200.0f / 9.0f) * (distance - 0.1f); - } else if (distance <= 6.4f) { - return 5000.0f / (100.0f - (distance - 0.1f) / 0.09f); - } else if (distance < 25.0f) { - return 530000.0f / - (3450.0f - - 300.0f * std::sqrt((848.0f * distance - 5330.0f) / 120.0f)); - } else { - return 5000.0f; - } -} - -constexpr float kExponent[DCTSIZE2] = { - 1.00f, 0.51f, 0.67f, 0.74f, 1.00f, 1.00f, 1.00f, 1.00f, // - 0.51f, 0.66f, 0.69f, 0.87f, 1.00f, 1.00f, 1.00f, 1.00f, // - 0.67f, 0.69f, 0.84f, 0.83f, 0.96f, 1.00f, 1.00f, 1.00f, // - 0.74f, 0.87f, 0.83f, 1.00f, 1.00f, 0.91f, 0.91f, 1.00f, // - 1.00f, 1.00f, 0.96f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, // - 1.00f, 1.00f, 1.00f, 0.91f, 1.00f, 1.00f, 1.00f, 1.00f, // - 1.00f, 1.00f, 1.00f, 0.91f, 1.00f, 1.00f, 1.00f, 1.00f, // - 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, // -}; -constexpr float kDist0 = 1.5f; // distance where non-linearity kicks in. - -float DistanceToScale(float distance, int k) { - if (distance < kDist0) { - return distance; - } - const float exp = kExponent[k]; - const float mul = std::pow(kDist0, 1.0 - exp); - return std::max(0.5f * distance, mul * std::pow(distance, exp)); -} - -float ScaleToDistance(float scale, int k) { - if (scale < kDist0) { - return scale; - } - const float exp = 1.0 / kExponent[k]; - const float mul = std::pow(kDist0, 1.0 - exp); - return std::min(2.0f * scale, mul * std::pow(scale, exp)); -} - -float QuantValsToDistance(j_compress_ptr cinfo) { - jpeg_comp_master* m = cinfo->master; - float global_scale = kGlobalScaleYCbCr; - if (m->cicp_transfer_function == kTransferFunctionPQ) { - global_scale *= .4f; - } else if (m->cicp_transfer_function == kTransferFunctionHLG) { - global_scale *= .5f; - } - int quant_max = m->force_baseline ? 255 : 32767U; - static const float kDistMax = 10000.0f; - float dist_min = 0.0f; - float dist_max = kDistMax; - for (int c = 0; c < cinfo->num_components; ++c) { - int quant_idx = cinfo->comp_info[c].quant_tbl_no; - uint16_t* quantval = cinfo->quant_tbl_ptrs[quant_idx]->quantval; - const float* base_qm = &kBaseQuantMatrixYCbCr[quant_idx * DCTSIZE2]; - for (int k = 0; k < DCTSIZE2; ++k) { - float dmin = 0.0; - float dmax = kDistMax; - float invq = 1.0f / base_qm[k] / global_scale; - int qval = quantval[k]; - if (qval > 1) { - float scale_min = (qval - 0.5f) * invq; - dmin = ScaleToDistance(scale_min, k); - } - if (qval < quant_max) { - float scale_max = (qval + 0.5f) * invq; - dmax = ScaleToDistance(scale_max, k); - } - if (dmin <= dist_max) { - dist_min = std::max(dmin, dist_min); - } - if (dmax >= dist_min) { - dist_max = std::min(dist_max, dmax); - } - } - } - float distance; - if (dist_min == 0) { - distance = dist_max; - } else if (dist_max == kDistMax) { - distance = dist_min; - } else { - distance = 0.5f * (dist_min + dist_max); - } - return distance; -} - -bool IsYUV420(j_compress_ptr cinfo) { - return (cinfo->jpeg_color_space == JCS_YCbCr && - cinfo->comp_info[0].h_samp_factor == 2 && - cinfo->comp_info[0].v_samp_factor == 2 && - cinfo->comp_info[1].h_samp_factor == 1 && - cinfo->comp_info[1].v_samp_factor == 1 && - cinfo->comp_info[2].h_samp_factor == 1 && - cinfo->comp_info[2].v_samp_factor == 1); -} - -} // namespace - -void SetQuantMatrices(j_compress_ptr cinfo, float distances[NUM_QUANT_TBLS], - bool add_two_chroma_tables) { - jpeg_comp_master* m = cinfo->master; - const bool xyb = m->xyb_mode && cinfo->jpeg_color_space == JCS_RGB; - const bool is_yuv420 = IsYUV420(cinfo); - - float global_scale; - bool non_linear_scaling = true; - const float* base_quant_matrix[NUM_QUANT_TBLS]; - int num_base_tables; - - if (xyb) { - global_scale = kGlobalScaleXYB; - num_base_tables = 3; - base_quant_matrix[0] = kBaseQuantMatrixXYB; - base_quant_matrix[1] = kBaseQuantMatrixXYB + DCTSIZE2; - base_quant_matrix[2] = kBaseQuantMatrixXYB + 2 * DCTSIZE2; - } else if (cinfo->jpeg_color_space == JCS_YCbCr && !m->use_std_tables) { - global_scale = kGlobalScaleYCbCr; - if (m->cicp_transfer_function == kTransferFunctionPQ) { - global_scale *= .4f; - } else if (m->cicp_transfer_function == kTransferFunctionHLG) { - global_scale *= .5f; - } - if (is_yuv420) { - global_scale *= k420GlobalScale; - } - if (add_two_chroma_tables) { - cinfo->comp_info[2].quant_tbl_no = 2; - num_base_tables = 3; - base_quant_matrix[0] = kBaseQuantMatrixYCbCr; - base_quant_matrix[1] = kBaseQuantMatrixYCbCr + DCTSIZE2; - base_quant_matrix[2] = kBaseQuantMatrixYCbCr + 2 * DCTSIZE2; - } else { - num_base_tables = 2; - base_quant_matrix[0] = kBaseQuantMatrixYCbCr; - // Use the Cr table for both Cb and Cr. - base_quant_matrix[1] = kBaseQuantMatrixYCbCr + 2 * DCTSIZE2; - } - } else { - global_scale = 0.01f; - non_linear_scaling = false; - num_base_tables = 2; - base_quant_matrix[0] = kBaseQuantMatrixStd; - base_quant_matrix[1] = kBaseQuantMatrixStd + DCTSIZE2; - } - - int quant_max = m->force_baseline ? 255 : 32767U; - for (int quant_idx = 0; quant_idx < num_base_tables; ++quant_idx) { - const float* base_qm = base_quant_matrix[quant_idx]; - JQUANT_TBL** qtable = &cinfo->quant_tbl_ptrs[quant_idx]; - if (*qtable == nullptr) { - *qtable = jpegli_alloc_quant_table(reinterpret_cast(cinfo)); - } - for (int k = 0; k < DCTSIZE2; ++k) { - float scale = global_scale; - if (non_linear_scaling) { - scale *= DistanceToScale(distances[quant_idx], k); - if (is_yuv420 && quant_idx > 0) { - scale *= k420Rescale[k]; - } - } else { - scale *= DistanceToLinearQuality(distances[quant_idx]); - } - int qval = std::round(scale * base_qm[k]); - (*qtable)->quantval[k] = std::max(1, std::min(qval, quant_max)); - } - (*qtable)->sent_table = FALSE; - } -} - -void InitQuantizer(j_compress_ptr cinfo, QuantPass pass) { - jpeg_comp_master* m = cinfo->master; - // Compute quantization multupliers from the quant table values. - for (int c = 0; c < cinfo->num_components; ++c) { - int quant_idx = cinfo->comp_info[c].quant_tbl_no; - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_idx]; - if (!quant_table) { - JPEGLI_ERROR("Missing quantization table %d for component %d", quant_idx, - c); - } - for (size_t k = 0; k < DCTSIZE2; k++) { - int val = quant_table->quantval[k]; - if (val == 0) { - JPEGLI_ERROR("Invalid quantval 0."); - } - switch (pass) { - case QuantPass::NO_SEARCH: - m->quant_mul[c][k] = 8.0f / val; - break; - case QuantPass::SEARCH_FIRST_PASS: - m->quant_mul[c][k] = 128.0f; - break; - case QuantPass::SEARCH_SECOND_PASS: - m->quant_mul[c][kJPEGZigZagOrder[k]] = 1.0f / (16 * val); - break; - } - } - } - if (m->use_adaptive_quantization) { - for (int c = 0; c < cinfo->num_components; ++c) { - for (int k = 0; k < DCTSIZE2; ++k) { - m->zero_bias_mul[c][k] = k == 0 ? 0.0f : 0.5f; - m->zero_bias_offset[c][k] = k == 0 ? 0.0f : 0.5f; - } - } - if (cinfo->jpeg_color_space == JCS_YCbCr) { - float distance = QuantValsToDistance(cinfo); - static const float kDistHQ = 1.0f; - static const float kDistLQ = 3.0f; - float mix0 = (distance - kDistHQ) / (kDistLQ - kDistHQ); - mix0 = std::max(0.0f, std::min(1.0f, mix0)); - float mix1 = 1.0f - mix0; - for (int c = 0; c < cinfo->num_components; ++c) { - for (int k = 0; k < DCTSIZE2; ++k) { - float mul0 = kZeroBiasMulYCbCrLQ[c * DCTSIZE2 + k]; - float mul1 = kZeroBiasMulYCbCrHQ[c * DCTSIZE2 + k]; - m->zero_bias_mul[c][k] = mix0 * mul0 + mix1 * mul1; - m->zero_bias_offset[c][k] = - k == 0 ? kZeroBiasOffsetYCbCrDC[c] : kZeroBiasOffsetYCbCrAC[c]; - } - } - } - } else if (cinfo->jpeg_color_space == JCS_YCbCr) { - for (int c = 0; c < cinfo->num_components; ++c) { - for (int k = 0; k < DCTSIZE2; ++k) { - m->zero_bias_offset[c][k] = - k == 0 ? kZeroBiasOffsetYCbCrDC[c] : kZeroBiasOffsetYCbCrAC[c]; - } - } - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/quant.h b/third_party/jpeg-xl/lib/jpegli/quant.h deleted file mode 100644 index cb37757ae204b..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/quant.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_QUANT_H_ -#define LIB_JPEGLI_QUANT_H_ - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void SetQuantMatrices(j_compress_ptr cinfo, float distances[NUM_QUANT_TBLS], - bool add_two_chroma_tables); - -enum QuantPass { - NO_SEARCH, - SEARCH_FIRST_PASS, - SEARCH_SECOND_PASS, -}; - -void InitQuantizer(j_compress_ptr cinfo, QuantPass pass); - -} // namespace jpegli - -#endif // LIB_JPEGLI_QUANT_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/render.cc b/third_party/jpeg-xl/lib/jpegli/render.cc deleted file mode 100644 index 1510401fa70e6..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/render.cc +++ /dev/null @@ -1,761 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/render.h" - -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/color_quantize.h" -#include "lib/jpegli/color_transform.h" -#include "lib/jpegli/decode_internal.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/idct.h" -#include "lib/jpegli/upsample.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" - -#ifdef MEMORY_SANITIZER -#define JXL_MEMORY_SANITIZER 1 -#elif defined(__has_feature) -#if __has_feature(memory_sanitizer) -#define JXL_MEMORY_SANITIZER 1 -#else -#define JXL_MEMORY_SANITIZER 0 -#endif -#else -#define JXL_MEMORY_SANITIZER 0 -#endif - -#if JXL_MEMORY_SANITIZER -#include "sanitizer/msan_interface.h" -#endif - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/render.cc" -#include -#include - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Abs; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::Clamp; -using hwy::HWY_NAMESPACE::Gt; -using hwy::HWY_NAMESPACE::IfThenElseZero; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::NearestInt; -using hwy::HWY_NAMESPACE::Or; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::ShiftLeftSame; -using hwy::HWY_NAMESPACE::ShiftRightSame; -using hwy::HWY_NAMESPACE::Vec; -using D = HWY_FULL(float); -using DI = HWY_FULL(int32_t); -constexpr D d; -constexpr DI di; - -void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs, - const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros, - int32_t* JXL_RESTRICT sumabs) { - for (size_t i = 0; i < coeffs_size; i += Lanes(d)) { - size_t k = i % DCTSIZE2; - const Rebind di16; - const Vec coeff = PromoteTo(di, Load(di16, coeffs + i)); - const auto abs_coeff = Abs(coeff); - const auto not_0 = Gt(abs_coeff, Zero(di)); - const auto nzero = IfThenElseZero(not_0, Set(di, 1)); - Store(Add(nzero, Load(di, nonzeros + k)), di, nonzeros + k); - Store(Add(abs_coeff, Load(di, sumabs + k)), di, sumabs + k); - } -} - -void DecenterRow(float* row, size_t xsize) { - const HWY_CAPPED(float, 8) df; - const auto c128 = Set(df, 128.0f / 255); - for (size_t x = 0; x < xsize; x += Lanes(df)) { - Store(Add(Load(df, row + x), c128), df, row + x); - } -} - -void DitherRow(j_decompress_ptr cinfo, float* row, int c, size_t y, - size_t xsize) { - jpeg_decomp_master* m = cinfo->master; - if (!m->dither_[c]) return; - const float* dither_row = - &m->dither_[c][(y & m->dither_mask_) * m->dither_size_]; - for (size_t x = 0; x < xsize; ++x) { - row[x] += dither_row[x & m->dither_mask_]; - } -} - -template -void StoreUnsignedRow(float* JXL_RESTRICT input[], size_t x0, size_t len, - size_t num_channels, float multiplier, T* output) { - const HWY_CAPPED(float, 8) d; - auto zero = Zero(d); - auto mul = Set(d, multiplier); - const Rebind du; -#if JXL_MEMORY_SANITIZER - const size_t padding = hwy::RoundUpTo(len, Lanes(d)) - len; - for (size_t c = 0; c < num_channels; ++c) { - __msan_unpoison(input[c] + x0 + len, sizeof(input[c][0]) * padding); - } -#endif - if (num_channels == 1) { - for (size_t i = 0; i < len; i += Lanes(d)) { - auto v0 = Clamp(zero, Mul(LoadU(d, &input[0][x0 + i]), mul), mul); - StoreU(DemoteTo(du, NearestInt(v0)), du, &output[i]); - } - } else if (num_channels == 2) { - for (size_t i = 0; i < len; i += Lanes(d)) { - auto v0 = Clamp(zero, Mul(LoadU(d, &input[0][x0 + i]), mul), mul); - auto v1 = Clamp(zero, Mul(LoadU(d, &input[1][x0 + i]), mul), mul); - StoreInterleaved2(DemoteTo(du, NearestInt(v0)), - DemoteTo(du, NearestInt(v1)), du, &output[2 * i]); - } - } else if (num_channels == 3) { - for (size_t i = 0; i < len; i += Lanes(d)) { - auto v0 = Clamp(zero, Mul(LoadU(d, &input[0][x0 + i]), mul), mul); - auto v1 = Clamp(zero, Mul(LoadU(d, &input[1][x0 + i]), mul), mul); - auto v2 = Clamp(zero, Mul(LoadU(d, &input[2][x0 + i]), mul), mul); - StoreInterleaved3(DemoteTo(du, NearestInt(v0)), - DemoteTo(du, NearestInt(v1)), - DemoteTo(du, NearestInt(v2)), du, &output[3 * i]); - } - } else if (num_channels == 4) { - for (size_t i = 0; i < len; i += Lanes(d)) { - auto v0 = Clamp(zero, Mul(LoadU(d, &input[0][x0 + i]), mul), mul); - auto v1 = Clamp(zero, Mul(LoadU(d, &input[1][x0 + i]), mul), mul); - auto v2 = Clamp(zero, Mul(LoadU(d, &input[2][x0 + i]), mul), mul); - auto v3 = Clamp(zero, Mul(LoadU(d, &input[3][x0 + i]), mul), mul); - StoreInterleaved4(DemoteTo(du, NearestInt(v0)), - DemoteTo(du, NearestInt(v1)), - DemoteTo(du, NearestInt(v2)), - DemoteTo(du, NearestInt(v3)), du, &output[4 * i]); - } - } -#if JXL_MEMORY_SANITIZER - __msan_poison(output + num_channels * len, - sizeof(output[0]) * num_channels * padding); -#endif -} - -void StoreFloatRow(float* JXL_RESTRICT input[3], size_t x0, size_t len, - size_t num_channels, float* output) { - const HWY_CAPPED(float, 8) d; - if (num_channels == 1) { - memcpy(output, input[0] + x0, len * sizeof(output[0])); - } else if (num_channels == 2) { - for (size_t i = 0; i < len; i += Lanes(d)) { - StoreInterleaved2(LoadU(d, &input[0][x0 + i]), - LoadU(d, &input[1][x0 + i]), d, &output[2 * i]); - } - } else if (num_channels == 3) { - for (size_t i = 0; i < len; i += Lanes(d)) { - StoreInterleaved3(LoadU(d, &input[0][x0 + i]), - LoadU(d, &input[1][x0 + i]), - LoadU(d, &input[2][x0 + i]), d, &output[3 * i]); - } - } else if (num_channels == 4) { - for (size_t i = 0; i < len; i += Lanes(d)) { - StoreInterleaved4(LoadU(d, &input[0][x0 + i]), - LoadU(d, &input[1][x0 + i]), - LoadU(d, &input[2][x0 + i]), - LoadU(d, &input[3][x0 + i]), d, &output[4 * i]); - } - } -} - -static constexpr float kFSWeightMR = 7.0f / 16.0f; -static constexpr float kFSWeightBL = 3.0f / 16.0f; -static constexpr float kFSWeightBM = 5.0f / 16.0f; -static constexpr float kFSWeightBR = 1.0f / 16.0f; - -float LimitError(float error) { - float abserror = std::abs(error); - if (abserror > 48.0f) { - abserror = 32.0f; - } else if (abserror > 16.0f) { - abserror = 0.5f * abserror + 8.0f; - } - return error > 0.0f ? abserror : -abserror; -} - -void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], - size_t xoffset, size_t len, size_t num_channels, - uint8_t* JXL_RESTRICT output) { - jpeg_decomp_master* m = cinfo->master; - uint8_t* JXL_RESTRICT scratch_space = m->output_scratch_; - if (cinfo->quantize_colors && m->quant_pass_ == 1) { - float* error_row[kMaxComponents]; - float* next_error_row[kMaxComponents]; - J_DITHER_MODE dither_mode = cinfo->dither_mode; - if (dither_mode == JDITHER_ORDERED) { - for (size_t c = 0; c < num_channels; ++c) { - DitherRow(cinfo, &rows[c][xoffset], c, cinfo->output_scanline, - cinfo->output_width); - } - } else if (dither_mode == JDITHER_FS) { - for (size_t c = 0; c < num_channels; ++c) { - if (cinfo->output_scanline % 2 == 0) { - error_row[c] = m->error_row_[c]; - next_error_row[c] = m->error_row_[c + kMaxComponents]; - } else { - error_row[c] = m->error_row_[c + kMaxComponents]; - next_error_row[c] = m->error_row_[c]; - } - memset(next_error_row[c], 0.0, cinfo->output_width * sizeof(float)); - } - } - const float mul = 255.0f; - if (dither_mode != JDITHER_FS) { - StoreUnsignedRow(rows, xoffset, len, num_channels, mul, scratch_space); - } - for (size_t i = 0; i < len; ++i) { - uint8_t* pixel = &scratch_space[num_channels * i]; - if (dither_mode == JDITHER_FS) { - for (size_t c = 0; c < num_channels; ++c) { - float val = rows[c][i] * mul + LimitError(error_row[c][i]); - pixel[c] = std::round(std::min(255.0f, std::max(0.0f, val))); - } - } - int index = LookupColorIndex(cinfo, pixel); - output[i] = index; - if (dither_mode == JDITHER_FS) { - size_t prev_i = i > 0 ? i - 1 : 0; - size_t next_i = i + 1 < len ? i + 1 : len - 1; - for (size_t c = 0; c < num_channels; ++c) { - float error = pixel[c] - cinfo->colormap[c][index]; - error_row[c][next_i] += kFSWeightMR * error; - next_error_row[c][prev_i] += kFSWeightBL * error; - next_error_row[c][i] += kFSWeightBM * error; - next_error_row[c][next_i] += kFSWeightBR * error; - } - } - } - } else if (m->output_data_type_ == JPEGLI_TYPE_UINT8) { - const float mul = 255.0; - StoreUnsignedRow(rows, xoffset, len, num_channels, mul, scratch_space); - memcpy(output, scratch_space, len * num_channels); - } else if (m->output_data_type_ == JPEGLI_TYPE_UINT16) { - const float mul = 65535.0; - uint16_t* tmp = reinterpret_cast(scratch_space); - StoreUnsignedRow(rows, xoffset, len, num_channels, mul, tmp); - if (m->swap_endianness_) { - const HWY_CAPPED(uint16_t, 8) du; - size_t output_len = len * num_channels; - for (size_t j = 0; j < output_len; j += Lanes(du)) { - auto v = LoadU(du, tmp + j); - auto vswap = Or(ShiftRightSame(v, 8), ShiftLeftSame(v, 8)); - StoreU(vswap, du, tmp + j); - } - } - memcpy(output, tmp, len * num_channels * 2); - } else if (m->output_data_type_ == JPEGLI_TYPE_FLOAT) { - float* tmp = reinterpret_cast(scratch_space); - StoreFloatRow(rows, xoffset, len, num_channels, tmp); - if (m->swap_endianness_) { - size_t output_len = len * num_channels; - for (size_t j = 0; j < output_len; ++j) { - tmp[j] = BSwapFloat(tmp[j]); - } - } - memcpy(output, tmp, len * num_channels * 4); - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE - -namespace jpegli { - -HWY_EXPORT(GatherBlockStats); -HWY_EXPORT(WriteToOutput); -HWY_EXPORT(DecenterRow); - -void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs, - const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros, - int32_t* JXL_RESTRICT sumabs) { - HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros, sumabs); -} - -void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], - size_t xoffset, size_t len, size_t num_channels, - uint8_t* JXL_RESTRICT output) { - HWY_DYNAMIC_DISPATCH(WriteToOutput) - (cinfo, rows, xoffset, len, num_channels, output); -} - -void DecenterRow(float* row, size_t xsize) { - HWY_DYNAMIC_DISPATCH(DecenterRow)(row, xsize); -} - -bool ShouldApplyDequantBiases(j_decompress_ptr cinfo, int ci) { - const auto& compinfo = cinfo->comp_info[ci]; - return (compinfo.h_samp_factor == cinfo->max_h_samp_factor && - compinfo.v_samp_factor == cinfo->max_v_samp_factor); -} - -// See the following article for the details: -// J. R. Price and M. Rabbani, "Dequantization bias for JPEG decompression" -// Proceedings International Conference on Information Technology: Coding and -// Computing (Cat. No.PR00540), 2000, pp. 30-35, doi: 10.1109/ITCC.2000.844179. -void ComputeOptimalLaplacianBiases(const int num_blocks, const int* nonzeros, - const int* sumabs, float* biases) { - for (size_t k = 1; k < DCTSIZE2; ++k) { - if (nonzeros[k] == 0) { - biases[k] = 0.5f; - continue; - } - // Notation adapted from the article - float N = num_blocks; - float N1 = nonzeros[k]; - float N0 = num_blocks - N1; - float S = sumabs[k]; - // Compute gamma from N0, N1, N, S (eq. 11), with A and B being just - // temporary grouping of terms. - float A = 4.0 * S + 2.0 * N; - float B = 4.0 * S - 2.0 * N1; - float gamma = (-1.0 * N0 + std::sqrt(N0 * N0 * 1.0 + A * B)) / A; - float gamma2 = gamma * gamma; - // The bias is computed from gamma with (eq. 5), where the quantization - // multiplier Q can be factored out and thus the bias can be applied - // directly on the quantized coefficient. - biases[k] = - 0.5 * (((1.0 + gamma2) / (1.0 - gamma2)) + 1.0 / std::log(gamma)); - } -} - -constexpr std::array Q_POS = {0, 1, 8, 16, 9, - 2, 3, 10, 17, 24}; - -bool is_nonzero_quantizers(const JQUANT_TBL* qtable) { - return std::all_of(Q_POS.begin(), Q_POS.end(), - [&](int pos) { return qtable->quantval[pos] != 0; }); -} - -// Determine whether smoothing should be applied during decompression -bool do_smoothing(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - bool smoothing_useful = false; - - if (!cinfo->progressive_mode || cinfo->coef_bits == nullptr) { - return false; - } - auto* coef_bits_latch = m->coef_bits_latch; - auto* prev_coef_bits_latch = m->prev_coef_bits_latch; - - for (int ci = 0; ci < cinfo->num_components; ci++) { - jpeg_component_info* compptr = &cinfo->comp_info[ci]; - JQUANT_TBL* qtable = compptr->quant_table; - int* coef_bits = cinfo->coef_bits[ci]; - int* prev_coef_bits = cinfo->coef_bits[ci + cinfo->num_components]; - - // Return early if conditions for smoothing are not met - if (qtable == nullptr || !is_nonzero_quantizers(qtable) || - coef_bits[0] < 0) { - return false; - } - - coef_bits_latch[ci][0] = coef_bits[0]; - - for (int coefi = 1; coefi < SAVED_COEFS; coefi++) { - prev_coef_bits_latch[ci][coefi] = - cinfo->input_scan_number > 1 ? prev_coef_bits[coefi] : -1; - if (coef_bits[coefi] != 0) { - smoothing_useful = true; - } - coef_bits_latch[ci][coefi] = coef_bits[coefi]; - } - } - - return smoothing_useful; -} - -void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component, - size_t bx, int iy) { - const size_t imcu_row = cinfo->output_iMCU_row; - int16_t* scratch = cinfo->master->smoothing_scratch_; - std::vector Q_VAL(SAVED_COEFS); - int* coef_bits; - - std::array, 5> dc_values; - auto& compinfo = cinfo->comp_info[component]; - const size_t by0 = imcu_row * compinfo.v_samp_factor; - const size_t by = by0 + iy; - - int prev_iy = by > 0 ? iy - 1 : 0; - int prev_prev_iy = by > 1 ? iy - 2 : prev_iy; - int next_iy = by + 1 < compinfo.height_in_blocks ? iy + 1 : iy; - int next_next_iy = by + 2 < compinfo.height_in_blocks ? iy + 2 : next_iy; - - const int16_t* cur_row = blocks[iy][bx]; - const int16_t* prev_row = blocks[prev_iy][bx]; - const int16_t* prev_prev_row = blocks[prev_prev_iy][bx]; - const int16_t* next_row = blocks[next_iy][bx]; - const int16_t* next_next_row = blocks[next_next_iy][bx]; - - int prev_block_ind = bx ? -DCTSIZE2 : 0; - int prev_prev_block_ind = bx > 1 ? -2 * DCTSIZE2 : prev_block_ind; - int next_block_ind = bx + 1 < compinfo.width_in_blocks ? DCTSIZE2 : 0; - int next_next_block_ind = - bx + 2 < compinfo.width_in_blocks ? DCTSIZE2 * 2 : next_block_ind; - - std::array row_ptrs = {prev_prev_row, prev_row, cur_row, - next_row, next_next_row}; - std::array block_inds = {prev_prev_block_ind, prev_block_ind, 0, - next_block_ind, next_next_block_ind}; - - memcpy(scratch, cur_row, DCTSIZE2 * sizeof(cur_row[0])); - - for (int r = 0; r < 5; ++r) { - for (int c = 0; c < 5; ++c) { - dc_values[r][c] = row_ptrs[r][block_inds[c]]; - } - } - // Get the correct coef_bits: In case of an incomplete scan, we use the - // prev coefficients. - if (cinfo->output_iMCU_row + 1 > cinfo->input_iMCU_row) { - coef_bits = cinfo->master->prev_coef_bits_latch[component]; - } else { - coef_bits = cinfo->master->coef_bits_latch[component]; - } - - bool change_dc = true; - for (int i = 1; i < SAVED_COEFS; i++) { - if (coef_bits[i] != -1) { - change_dc = false; - break; - } - } - - JQUANT_TBL* quanttbl = cinfo->quant_tbl_ptrs[compinfo.quant_tbl_no]; - for (size_t i = 0; i < 6; ++i) { - Q_VAL[i] = quanttbl->quantval[Q_POS[i]]; - } - if (change_dc) { - for (size_t i = 6; i < SAVED_COEFS; ++i) { - Q_VAL[i] = quanttbl->quantval[Q_POS[i]]; - } - } - auto calculate_dct_value = [&](int coef_index) { - int64_t num = 0; - int pred; - int Al; - // we use the symmetry of the smoothing matrices by transposing the 5x5 dc - // matrix in that case. - bool swap_indices = coef_index == 2 || coef_index == 5 || coef_index == 8 || - coef_index == 9; - auto dc = [&](int i, int j) { - return swap_indices ? dc_values[j][i] : dc_values[i][j]; - }; - JPEGLI_CHECK(coef_index >= 0 && coef_index < 10); - Al = coef_bits[coef_index]; - switch (coef_index) { - case 0: - // set the DC - num = (-2 * dc(0, 0) - 6 * dc(0, 1) - 8 * dc(0, 2) - 6 * dc(0, 3) - - 2 * dc(0, 4) - 6 * dc(1, 0) + 6 * dc(1, 1) + 42 * dc(1, 2) + - 6 * dc(1, 3) - 6 * dc(1, 4) - 8 * dc(2, 0) + 42 * dc(2, 1) + - 152 * dc(2, 2) + 42 * dc(2, 3) - 8 * dc(2, 4) - 6 * dc(3, 0) + - 6 * dc(3, 1) + 42 * dc(3, 2) + 6 * dc(3, 3) - 6 * dc(3, 4) - - 2 * dc(4, 0) - 6 * dc(4, 1) - 8 * dc(4, 2) - 6 * dc(4, 3) - - 2 * dc(4, 4)); - // special case: for the DC the dequantization is different - Al = 0; - break; - case 1: - case 2: - // set Q01 or Q10 - num = (change_dc ? (-dc(0, 0) - dc(0, 1) + dc(0, 3) + dc(0, 4) - - 3 * dc(1, 0) + 13 * dc(1, 1) - 13 * dc(1, 3) + - 3 * dc(1, 4) - 3 * dc(2, 0) + 38 * dc(2, 1) - - 38 * dc(2, 3) + 3 * dc(2, 4) - 3 * dc(3, 0) + - 13 * dc(3, 1) - 13 * dc(3, 3) + 3 * dc(3, 4) - - dc(4, 0) - dc(4, 1) + dc(4, 3) + dc(4, 4)) - : (-7 * dc(2, 0) + 50 * dc(2, 1) - 50 * dc(2, 3) + - 7 * dc(2, 4))); - break; - case 3: - case 5: - // set Q02 or Q20 - num = (change_dc - ? dc(0, 2) + 2 * dc(1, 1) + 7 * dc(1, 2) + 2 * dc(1, 3) - - 5 * dc(2, 1) - 14 * dc(2, 2) - 5 * dc(2, 3) + - 2 * dc(3, 1) + 7 * dc(3, 2) + 2 * dc(3, 3) + dc(4, 2) - : (-dc(0, 2) + 13 * dc(1, 2) - 24 * dc(2, 2) + - 13 * dc(3, 2) - dc(4, 2))); - break; - case 4: - // set Q11 - num = - (change_dc ? -dc(0, 0) + dc(0, 4) + 9 * dc(1, 1) - 9 * dc(1, 3) - - 9 * dc(3, 1) + 9 * dc(3, 3) + dc(4, 0) - dc(4, 4) - : (dc(1, 4) + dc(3, 0) - 10 * dc(3, 1) + 10 * dc(3, 3) - - dc(0, 1) - dc(3, 4) + dc(4, 1) - dc(4, 3) + dc(0, 3) - - dc(1, 0) + 10 * dc(1, 1) - 10 * dc(1, 3))); - break; - case 6: - case 9: - // set Q03 or Q30 - num = (dc(1, 1) - dc(1, 3) + 2 * dc(2, 1) - 2 * dc(2, 3) + dc(3, 1) - - dc(3, 3)); - break; - case 7: - case 8: - default: - // set Q12 and Q21 - num = (dc(1, 1) - 3 * dc(1, 2) + dc(1, 3) - dc(3, 1) + 3 * dc(3, 2) - - dc(3, 3)); - break; - } - num = Q_VAL[0] * num; - if (num >= 0) { - pred = ((Q_VAL[coef_index] << 7) + num) / (Q_VAL[coef_index] << 8); - if (Al > 0 && pred >= (1 << Al)) pred = (1 << Al) - 1; - } else { - pred = ((Q_VAL[coef_index] << 7) - num) / (Q_VAL[coef_index] << 8); - if (Al > 0 && pred >= (1 << Al)) pred = (1 << Al) - 1; - pred = -pred; - } - return static_cast(pred); - }; - - int loop_end = change_dc ? SAVED_COEFS : 6; - for (int i = 1; i < loop_end; ++i) { - if (coef_bits[i] != 0 && scratch[Q_POS[i]] == 0) { - scratch[Q_POS[i]] = calculate_dct_value(i); - } - } - if (change_dc) { - scratch[0] = calculate_dct_value(0); - } -} - -void PrepareForOutput(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - bool smoothing = do_smoothing(cinfo); - m->apply_smoothing = smoothing && FROM_JXL_BOOL(cinfo->do_block_smoothing); - size_t coeffs_per_block = cinfo->num_components * DCTSIZE2; - memset(m->nonzeros_, 0, coeffs_per_block * sizeof(m->nonzeros_[0])); - memset(m->sumabs_, 0, coeffs_per_block * sizeof(m->sumabs_[0])); - memset(m->num_processed_blocks_, 0, sizeof(m->num_processed_blocks_)); - memset(m->biases_, 0, coeffs_per_block * sizeof(m->biases_[0])); - cinfo->output_iMCU_row = 0; - cinfo->output_scanline = 0; - const float kDequantScale = 1.0f / (8 * 255); - for (int c = 0; c < cinfo->num_components; c++) { - const auto& comp = cinfo->comp_info[c]; - JQUANT_TBL* table = comp.quant_table; - if (table == nullptr) continue; - for (size_t k = 0; k < DCTSIZE2; ++k) { - m->dequant_[c * DCTSIZE2 + k] = table->quantval[k] * kDequantScale; - } - } - JPEGLI_CHECK(ChooseInverseTransform(cinfo)); - ChooseColorTransform(cinfo); -} - -void DecodeCurrentiMCURow(j_decompress_ptr cinfo) { - jpeg_decomp_master* m = cinfo->master; - const size_t imcu_row = cinfo->output_iMCU_row; - JBLOCKARRAY blocks[kMaxComponents]; - for (int c = 0; c < cinfo->num_components; ++c) { - const jpeg_component_info* comp = &cinfo->comp_info[c]; - int by0 = imcu_row * comp->v_samp_factor; - int block_rows_left = comp->height_in_blocks - by0; - int max_block_rows = std::min(comp->v_samp_factor, block_rows_left); - int offset = m->streaming_mode_ ? 0 : by0; - blocks[c] = (*cinfo->mem->access_virt_barray)( - reinterpret_cast(cinfo), m->coef_arrays[c], offset, - max_block_rows, FALSE); - } - for (int c = 0; c < cinfo->num_components; ++c) { - size_t k0 = c * DCTSIZE2; - auto& compinfo = cinfo->comp_info[c]; - size_t block_row = imcu_row * compinfo.v_samp_factor; - if (ShouldApplyDequantBiases(cinfo, c)) { - // Update statistics for this iMCU row. - for (int iy = 0; iy < compinfo.v_samp_factor; ++iy) { - size_t by = block_row + iy; - if (by >= compinfo.height_in_blocks) { - continue; - } - int16_t* JXL_RESTRICT coeffs = &blocks[c][iy][0][0]; - size_t num = compinfo.width_in_blocks * DCTSIZE2; - GatherBlockStats(coeffs, num, &m->nonzeros_[k0], &m->sumabs_[k0]); - m->num_processed_blocks_[c] += compinfo.width_in_blocks; - } - if (imcu_row % 4 == 3) { - // Re-compute optimal biases every few iMCU-rows. - ComputeOptimalLaplacianBiases(m->num_processed_blocks_[c], - &m->nonzeros_[k0], &m->sumabs_[k0], - &m->biases_[k0]); - } - } - RowBuffer* raw_out = &m->raw_output_[c]; - for (int iy = 0; iy < compinfo.v_samp_factor; ++iy) { - size_t by = block_row + iy; - if (by >= compinfo.height_in_blocks) { - continue; - } - size_t dctsize = m->scaled_dct_size[c]; - int16_t* JXL_RESTRICT row_in = &blocks[c][iy][0][0]; - float* JXL_RESTRICT row_out = raw_out->Row(by * dctsize); - for (size_t bx = 0; bx < compinfo.width_in_blocks; ++bx) { - if (m->apply_smoothing) { - PredictSmooth(cinfo, blocks[c], c, bx, iy); - (*m->inverse_transform[c])(m->smoothing_scratch_, &m->dequant_[k0], - &m->biases_[k0], m->idct_scratch_, - &row_out[bx * dctsize], raw_out->stride(), - dctsize); - } else { - (*m->inverse_transform[c])(&row_in[bx * DCTSIZE2], &m->dequant_[k0], - &m->biases_[k0], m->idct_scratch_, - &row_out[bx * dctsize], raw_out->stride(), - dctsize); - } - } - if (m->streaming_mode_) { - memset(row_in, 0, compinfo.width_in_blocks * sizeof(JBLOCK)); - } - } - } -} - -void ProcessRawOutput(j_decompress_ptr cinfo, JSAMPIMAGE data) { - jpegli::DecodeCurrentiMCURow(cinfo); - jpeg_decomp_master* m = cinfo->master; - for (int c = 0; c < cinfo->num_components; ++c) { - const auto& compinfo = cinfo->comp_info[c]; - size_t comp_width = compinfo.width_in_blocks * DCTSIZE; - size_t comp_height = compinfo.height_in_blocks * DCTSIZE; - size_t comp_nrows = compinfo.v_samp_factor * DCTSIZE; - size_t y0 = cinfo->output_iMCU_row * compinfo.v_samp_factor * DCTSIZE; - size_t y1 = std::min(y0 + comp_nrows, comp_height); - for (size_t y = y0; y < y1; ++y) { - float* rows[1] = {m->raw_output_[c].Row(y)}; - uint8_t* output = data[c][y - y0]; - DecenterRow(rows[0], comp_width); - WriteToOutput(cinfo, rows, 0, comp_width, 1, output); - } - } - ++cinfo->output_iMCU_row; - cinfo->output_scanline += cinfo->max_v_samp_factor * DCTSIZE; - if (cinfo->output_scanline >= cinfo->output_height) { - ++m->output_passes_done_; - } -} - -void ProcessOutput(j_decompress_ptr cinfo, size_t* num_output_rows, - JSAMPARRAY scanlines, size_t max_output_rows) { - jpeg_decomp_master* m = cinfo->master; - const int vfactor = cinfo->max_v_samp_factor; - const int hfactor = cinfo->max_h_samp_factor; - const size_t context = m->need_context_rows_ ? 1 : 0; - const size_t imcu_row = cinfo->output_iMCU_row; - const size_t imcu_height = vfactor * m->min_scaled_dct_size; - const size_t imcu_width = hfactor * m->min_scaled_dct_size; - const size_t output_width = m->iMCU_cols_ * imcu_width; - if (imcu_row == cinfo->total_iMCU_rows || - (imcu_row > context && - cinfo->output_scanline < (imcu_row - context) * imcu_height)) { - // We are ready to output some scanlines. - size_t ybegin = cinfo->output_scanline; - size_t yend = (imcu_row == cinfo->total_iMCU_rows - ? cinfo->output_height - : (imcu_row - context) * imcu_height); - yend = std::min(yend, ybegin + max_output_rows - *num_output_rows); - size_t yb = (ybegin / vfactor) * vfactor; - size_t ye = DivCeil(yend, vfactor) * vfactor; - for (size_t y = yb; y < ye; y += vfactor) { - for (int c = 0; c < cinfo->num_components; ++c) { - RowBuffer* raw_out = &m->raw_output_[c]; - RowBuffer* render_out = &m->render_output_[c]; - int line_groups = vfactor / m->v_factor[c]; - int downsampled_width = output_width / m->h_factor[c]; - size_t yc = y / m->v_factor[c]; - for (int dy = 0; dy < line_groups; ++dy) { - size_t ymid = yc + dy; - const float* JXL_RESTRICT row_mid = raw_out->Row(ymid); - if (cinfo->do_fancy_upsampling && m->v_factor[c] == 2) { - const float* JXL_RESTRICT row_top = - ymid == 0 ? row_mid : raw_out->Row(ymid - 1); - const float* JXL_RESTRICT row_bot = ymid + 1 == m->raw_height_[c] - ? row_mid - : raw_out->Row(ymid + 1); - Upsample2Vertical(row_top, row_mid, row_bot, - render_out->Row(2 * dy), - render_out->Row(2 * dy + 1), downsampled_width); - } else { - for (int yix = 0; yix < m->v_factor[c]; ++yix) { - memcpy(render_out->Row(m->v_factor[c] * dy + yix), row_mid, - downsampled_width * sizeof(float)); - } - } - if (m->h_factor[c] > 1) { - for (int yix = 0; yix < m->v_factor[c]; ++yix) { - int row_ix = m->v_factor[c] * dy + yix; - float* JXL_RESTRICT row = render_out->Row(row_ix); - float* JXL_RESTRICT tmp = m->upsample_scratch_; - if (cinfo->do_fancy_upsampling && m->h_factor[c] == 2) { - Upsample2Horizontal(row, tmp, output_width); - } else { - // TODO(szabadka) SIMDify this. - for (size_t x = 0; x < output_width; ++x) { - tmp[x] = row[x / m->h_factor[c]]; - } - memcpy(row, tmp, output_width * sizeof(tmp[0])); - } - } - } - } - } - for (int yix = 0; yix < vfactor; ++yix) { - if (y + yix < ybegin || y + yix >= yend) continue; - float* rows[kMaxComponents]; - int num_all_components = - std::max(cinfo->out_color_components, cinfo->num_components); - for (int c = 0; c < num_all_components; ++c) { - rows[c] = m->render_output_[c].Row(yix); - } - (*m->color_transform)(rows, output_width); - for (int c = 0; c < cinfo->out_color_components; ++c) { - // Undo the centering of the sample values around zero. - DecenterRow(rows[c], output_width); - } - if (scanlines) { - uint8_t* output = scanlines[*num_output_rows]; - WriteToOutput(cinfo, rows, m->xoffset_, cinfo->output_width, - cinfo->out_color_components, output); - } - JPEGLI_CHECK(cinfo->output_scanline == y + yix); - ++cinfo->output_scanline; - ++(*num_output_rows); - if (cinfo->output_scanline == cinfo->output_height) { - ++m->output_passes_done_; - } - } - } - } else { - DecodeCurrentiMCURow(cinfo); - ++cinfo->output_iMCU_row; - } -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/render.h b/third_party/jpeg-xl/lib/jpegli/render.h deleted file mode 100644 index ad69335d70bb3..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/render.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_RENDER_H_ -#define LIB_JPEGLI_RENDER_H_ - -#include - -#include "lib/jpegli/common.h" - -namespace jpegli { - -void PrepareForOutput(j_decompress_ptr cinfo); - -void ProcessOutput(j_decompress_ptr cinfo, size_t* num_output_rows, - JSAMPARRAY scanlines, size_t max_output_rows); - -void ProcessRawOutput(j_decompress_ptr cinfo, JSAMPIMAGE data); - -} // namespace jpegli - -#endif // LIB_JPEGLI_RENDER_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/simd.cc b/third_party/jpeg-xl/lib/jpegli/simd.cc deleted file mode 100644 index 5e849393426ac..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/simd.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/simd.h" - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/simd.cc" -#include -#include - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -size_t GetVectorSize() { return HWY_LANES(uint8_t); } - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { -namespace { - -HWY_EXPORT(GetVectorSize); // Local function. - -} // namespace - -size_t VectorSize() { - static size_t bytes = HWY_DYNAMIC_DISPATCH(GetVectorSize)(); - return bytes; -} - -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/simd.h b/third_party/jpeg-xl/lib/jpegli/simd.h deleted file mode 100644 index aec772e2d4a47..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/simd.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_SIMD_H_ -#define LIB_JPEGLI_SIMD_H_ - -#include - -namespace jpegli { - -// Returns SIMD vector size in bytes. -size_t VectorSize(); - -} // namespace jpegli - -#endif // LIB_JPEGLI_SIMD_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager.cc b/third_party/jpeg-xl/lib/jpegli/source_manager.cc deleted file mode 100644 index e0f0b4c275a12..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/source_manager.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/error.h" -#include "lib/jpegli/memory_manager.h" - -namespace jpegli { - -void init_mem_source(j_decompress_ptr cinfo) {} -void init_stdio_source(j_decompress_ptr cinfo) {} - -void skip_input_data(j_decompress_ptr cinfo, long num_bytes /* NOLINT */) { - if (num_bytes <= 0) return; - while (num_bytes > - static_cast(cinfo->src->bytes_in_buffer)) { // NOLINT - num_bytes -= cinfo->src->bytes_in_buffer; - (*cinfo->src->fill_input_buffer)(cinfo); - } - cinfo->src->next_input_byte += num_bytes; - cinfo->src->bytes_in_buffer -= num_bytes; -} - -void term_source(j_decompress_ptr cinfo) {} - -boolean EmitFakeEoiMarker(j_decompress_ptr cinfo) { - static constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; - cinfo->src->next_input_byte = kFakeEoiMarker; - cinfo->src->bytes_in_buffer = 2; - return TRUE; -} - -constexpr size_t kStdioBufferSize = 64 << 10; - -struct StdioSourceManager { - jpeg_source_mgr pub; - FILE* f; - uint8_t* buffer; - - static boolean fill_input_buffer(j_decompress_ptr cinfo) { - auto* src = reinterpret_cast(cinfo->src); - size_t num_bytes_read = fread(src->buffer, 1, kStdioBufferSize, src->f); - if (num_bytes_read == 0) { - return EmitFakeEoiMarker(cinfo); - } - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = num_bytes_read; - return TRUE; - } -}; - -} // namespace jpegli - -void jpegli_mem_src(j_decompress_ptr cinfo, const unsigned char* inbuffer, - unsigned long insize /* NOLINT */) { - if (cinfo->src && cinfo->src->init_source != jpegli::init_mem_source) { - JPEGLI_ERROR("jpegli_mem_src: a different source manager was already set"); - } - if (!cinfo->src) { - cinfo->src = jpegli::Allocate(cinfo, 1); - } - cinfo->src->next_input_byte = inbuffer; - cinfo->src->bytes_in_buffer = insize; - cinfo->src->init_source = jpegli::init_mem_source; - cinfo->src->fill_input_buffer = jpegli::EmitFakeEoiMarker; - cinfo->src->skip_input_data = jpegli::skip_input_data; - cinfo->src->resync_to_restart = jpegli_resync_to_restart; - cinfo->src->term_source = jpegli::term_source; -} - -void jpegli_stdio_src(j_decompress_ptr cinfo, FILE* infile) { - if (cinfo->src && cinfo->src->init_source != jpegli::init_stdio_source) { - JPEGLI_ERROR("jpeg_stdio_src: a different source manager was already set"); - } - if (!cinfo->src) { - cinfo->src = reinterpret_cast( - jpegli::Allocate(cinfo, 1)); - } - auto* src = reinterpret_cast(cinfo->src); - src->f = infile; - src->buffer = jpegli::Allocate(cinfo, jpegli::kStdioBufferSize); - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = 0; - src->pub.init_source = jpegli::init_stdio_source; - src->pub.fill_input_buffer = jpegli::StdioSourceManager::fill_input_buffer; - src->pub.skip_input_data = jpegli::skip_input_data; - src->pub.resync_to_restart = jpegli_resync_to_restart; - src->pub.term_source = jpegli::term_source; -} diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc b/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc deleted file mode 100644 index b70b43d8dbef3..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" -#include "lib/jxl/base/status.h" - -namespace jpegli { -namespace { - -void ReadOutputImage(j_decompress_ptr cinfo, TestImage* output) { - jpegli_read_header(cinfo, /*require_image=*/TRUE); - jpegli_start_decompress(cinfo); - output->ysize = cinfo->output_height; - output->xsize = cinfo->output_width; - output->components = cinfo->num_components; - output->AllocatePixels(); - size_t stride = cinfo->output_width * cinfo->num_components; - while (cinfo->output_scanline < cinfo->output_height) { - JSAMPROW scanline = &output->pixels[cinfo->output_scanline * stride]; - jpegli_read_scanlines(cinfo, &scanline, 1); - } - jpegli_finish_decompress(cinfo); -} - -struct TestConfig { - std::string fn; - std::string fn_desc; - DecompressParams dparams; -}; - -class SourceManagerTestParam : public ::testing::TestWithParam {}; - -namespace { -FILE* MemOpen(const std::vector& data) { - FILE* src = tmpfile(); - if (!src) return nullptr; - fwrite(data.data(), 1, data.size(), src); - fseek(src, 0, SEEK_SET); - return src; -} -} // namespace - -TEST_P(SourceManagerTestParam, TestStdioSourceManager) { - TestConfig config = GetParam(); - JXL_ASSIGN_OR_QUIT(std::vector compressed, ReadTestData(config.fn), - "Failed to read test data."); - if (config.dparams.size_factor < 1.0) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - FILE* src = MemOpen(compressed); - ASSERT_TRUE(src); - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_stdio_src(&cinfo, src); - ReadOutputImage(&cinfo, &output0); - return true; - }; - bool ok = try_catch_block(); - fclose(src); - ASSERT_TRUE(ok); - jpegli_destroy_decompress(&cinfo); - - TestImage output1; - DecodeWithLibjpeg(CompressParams(), DecompressParams(), compressed, &output1); - VerifyOutputImage(output1, output0, 1.0f); -} - -TEST_P(SourceManagerTestParam, TestMemSourceManager) { - TestConfig config = GetParam(); - JXL_ASSIGN_OR_QUIT(std::vector compressed, ReadTestData(config.fn), - "Failed to read test data."); - if (config.dparams.size_factor < 1.0f) { - compressed.resize(compressed.size() * config.dparams.size_factor); - } - TestImage output0; - jpeg_decompress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_decompress(&cinfo); - jpegli_mem_src(&cinfo, compressed.data(), compressed.size()); - ReadOutputImage(&cinfo, &output0); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&cinfo); - - TestImage output1; - DecodeWithLibjpeg(CompressParams(), DecompressParams(), compressed, &output1); - VerifyOutputImage(output1, output0, 1.0f); -} - -std::vector GenerateTests() { - std::vector all_tests; - { - std::vector> testfiles({ - {"jxl/flower/flower.png.im_q85_444.jpg", "Q85YUV444"}, - {"jxl/flower/flower.png.im_q85_420.jpg", "Q85YUV420"}, - {"jxl/flower/flower.png.im_q85_420_R13B.jpg", "Q85YUV420R13B"}, - }); - for (const auto& it : testfiles) { - for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f}) { - TestConfig config; - config.fn = it.first; - config.fn_desc = it.second; - config.dparams.size_factor = size_factor; - all_tests.push_back(config); - } - } - return all_tests; - } -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - os << c.fn_desc; - if (c.dparams.size_factor < 1.0f) { - os << "Partial" << static_cast(c.dparams.size_factor * 100) << "p"; - } - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(SourceManagerTest, SourceManagerTestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc b/third_party/jpeg-xl/lib/jpegli/streaming_test.cc deleted file mode 100644 index ae85fd2451a18..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" - -namespace jpegli { -namespace { - -// A simple suspending source manager with an input buffer. -struct SourceManager { - jpeg_source_mgr pub; - std::vector buffer; - - SourceManager() { - pub.next_input_byte = nullptr; - pub.bytes_in_buffer = 0; - pub.init_source = init_source; - pub.fill_input_buffer = fill_input_buffer; - pub.skip_input_data = skip_input_data; - pub.resync_to_restart = jpegli_resync_to_restart; - pub.term_source = term_source; - } - - static void init_source(j_decompress_ptr cinfo) {} - static boolean fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; } - static void skip_input_data(j_decompress_ptr cinfo, - long num_bytes /* NOLINT */) {} - static void term_source(j_decompress_ptr cinfo) {} -}; - -// A destination manager that empties its output buffer into a SourceManager's -// input buffer. The buffer size is kept short because empty_output_buffer() is -// called only when the output buffer is full, and we want to update the decoder -// input frequently to demonstrate that streaming works. -constexpr size_t kOutputBufferSize = 1024; -struct DestinationManager { - jpeg_destination_mgr pub; - std::vector buffer; - SourceManager* dest; - - explicit DestinationManager(SourceManager* src) - : buffer(kOutputBufferSize), dest(src) { - pub.next_output_byte = buffer.data(); - pub.free_in_buffer = buffer.size(); - pub.init_destination = init_destination; - pub.empty_output_buffer = empty_output_buffer; - pub.term_destination = term_destination; - } - - static void init_destination(j_compress_ptr cinfo) {} - - static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto* us = reinterpret_cast(cinfo->dest); - jpeg_destination_mgr* src = &us->pub; - jpeg_source_mgr* dst = &us->dest->pub; - std::vector& src_buf = us->buffer; - std::vector& dst_buf = us->dest->buffer; - if (dst->bytes_in_buffer > 0 && dst->bytes_in_buffer < dst_buf.size()) { - memmove(dst_buf.data(), dst->next_input_byte, dst->bytes_in_buffer); - } - size_t src_len = src_buf.size() - src->free_in_buffer; - dst_buf.resize(dst->bytes_in_buffer + src_len); - memcpy(&dst_buf[dst->bytes_in_buffer], src_buf.data(), src_len); - dst->next_input_byte = dst_buf.data(); - dst->bytes_in_buffer = dst_buf.size(); - src->next_output_byte = src_buf.data(); - src->free_in_buffer = src_buf.size(); - return TRUE; - } - - static void term_destination(j_compress_ptr cinfo) { - empty_output_buffer(cinfo); - } -}; - -struct TestConfig { - TestImage input; - CompressParams jparams; -}; - -class StreamingTestParam : public ::testing::TestWithParam {}; - -TEST_P(StreamingTestParam, TestStreaming) { - jpeg_decompress_struct dinfo = {}; - jpeg_compress_struct cinfo = {}; - SourceManager src; - TestConfig config = GetParam(); - TestImage& input = config.input; - TestImage output; - GeneratePixels(&input); - const auto try_catch_block = [&]() { - ERROR_HANDLER_SETUP(jpegli); - dinfo.err = cinfo.err; - dinfo.client_data = cinfo.client_data; - // Create a pair of compressor and decompressor objects, where the - // compressor's output is connected to the decompressor's input. - jpegli_create_decompress(&dinfo); - jpegli_create_compress(&cinfo); - dinfo.src = reinterpret_cast(&src); - DestinationManager dest(&src); - cinfo.dest = reinterpret_cast(&dest); - - cinfo.image_width = input.xsize; - cinfo.image_height = input.ysize; - cinfo.input_components = input.components; - cinfo.in_color_space = static_cast(input.color_space); - jpegli_set_defaults(&cinfo); - cinfo.comp_info[0].v_samp_factor = config.jparams.v_sampling[0]; - jpegli_set_progressive_level(&cinfo, 0); - cinfo.optimize_coding = FALSE; - jpegli_start_compress(&cinfo, TRUE); - - size_t stride = cinfo.image_width * cinfo.input_components; - size_t iMCU_height = 8 * cinfo.max_v_samp_factor; - std::vector row_bytes(iMCU_height * stride); - size_t y_in = 0; - size_t y_out = 0; - while (y_in < cinfo.image_height) { - // Feed one iMCU row at a time to the compressor. - size_t lines_in = std::min(iMCU_height, cinfo.image_height - y_in); - memcpy(row_bytes.data(), &input.pixels[y_in * stride], lines_in * stride); - std::vector rows_in(lines_in); - for (size_t i = 0; i < lines_in; ++i) { - rows_in[i] = &row_bytes[i * stride]; - } - EXPECT_EQ(lines_in, - jpegli_write_scanlines(&cinfo, rows_in.data(), lines_in)); - y_in += lines_in; - if (y_in == cinfo.image_height) { - jpegli_finish_compress(&cinfo); - } - - // Atfer the first iMCU row, we don't yet expect any output because the - // compressor delays processing to have context rows after the iMCU row. - if (y_in < std::min(2 * iMCU_height, cinfo.image_height)) { - continue; - } - - // After two iMCU rows, the compressor has started emitting compressed - // data. We check here that at least the scan header was output, because - // we expect that the compressor's output buffer was filled at least once - // while emitting the first compressed iMCU row. - if (y_in == std::min(2 * iMCU_height, cinfo.image_height)) { - EXPECT_EQ(JPEG_REACHED_SOS, - jpegli_read_header(&dinfo, /*require_image=*/TRUE)); - output.xsize = dinfo.image_width; - output.ysize = dinfo.image_height; - output.components = dinfo.num_components; - EXPECT_EQ(output.xsize, input.xsize); - EXPECT_EQ(output.ysize, input.ysize); - EXPECT_EQ(output.components, input.components); - EXPECT_TRUE(jpegli_start_decompress(&dinfo)); - output.pixels.resize(output.ysize * stride); - if (y_in < cinfo.image_height) { - continue; - } - } - - // After six iMCU rows, the compressor has emitted five iMCU rows of - // compressed data, of which we expect four full iMCU row of compressed - // data to be in the decoder's input buffer, but since the decoder also - // needs context rows for upsampling and smoothing, we don't expect any - // output to be ready yet. - if (y_in < 7 * iMCU_height && y_in < cinfo.image_height) { - continue; - } - - // After five iMCU rows, we expect the decoder to have rendered the output - // with four iMCU rows of delay. - // TODO(szabadka) Reduce the processing delay in the decoder if possible. - size_t lines_out = - (y_in == cinfo.image_height ? cinfo.image_height - y_out - : iMCU_height); - std::vector rows_out(lines_out); - for (size_t i = 0; i < lines_out; ++i) { - rows_out[i] = - reinterpret_cast(&output.pixels[(y_out + i) * stride]); - } - EXPECT_EQ(lines_out, - jpegli_read_scanlines(&dinfo, rows_out.data(), lines_out)); - VerifyOutputImage(input, output, y_out, lines_out, 3.8f); - y_out += lines_out; - - if (y_out == cinfo.image_height) { - EXPECT_TRUE(jpegli_finish_decompress(&dinfo)); - } - } - return true; - }; - EXPECT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&dinfo); - jpegli_destroy_compress(&cinfo); -} - -std::vector GenerateTests() { - std::vector all_tests; - const size_t xsize0 = 1920; - const size_t ysize0 = 1080; - for (int dysize : {0, 1, 8, 9}) { - for (int v_sampling : {1, 2}) { - TestConfig config; - config.input.xsize = xsize0; - config.input.ysize = ysize0 + dysize; - config.jparams.h_sampling = {1, 1, 1}; - config.jparams.v_sampling = {v_sampling, 1, 1}; - all_tests.push_back(config); - } - } - return all_tests; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - os << c.input; - os << c.jparams; - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(StreamingTest, StreamingTestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/test_params.h b/third_party/jpeg-xl/lib/jpegli/test_params.h deleted file mode 100644 index 6ab9fa573ae60..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/test_params.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_TEST_PARAMS_H_ -#define LIB_JPEGLI_TEST_PARAMS_H_ - -#include -#include - -#include -#include - -#include "lib/jpegli/types.h" - -namespace jpegli { - -// We define this here as well to make sure that the *_api_test.cc tests only -// use the public API and therefore we don't include any *_internal.h headers. -template -constexpr inline T1 DivCeil(T1 a, T2 b) { - return (a + b - 1) / b; -} - -#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) - -static constexpr int kLastScan = 0xffff; - -static uint32_t kTestColorMap[] = { - 0x000000, 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0x00ffff, - 0xff00ff, 0xffffff, 0x6251fc, 0x45d9c7, 0xa7f059, 0xd9a945, - 0xfa4e44, 0xceaffc, 0xbad7db, 0xc1f0b1, 0xdbca9a, 0xfacac5, - 0xf201ff, 0x0063db, 0x00f01c, 0xdbb204, 0xf12f0c, 0x7ba1dc}; -static constexpr int kTestColorMapNumColors = ARRAY_SIZE(kTestColorMap); - -static constexpr int kSpecialMarker0 = 0xe5; -static constexpr int kSpecialMarker1 = 0xe9; -static constexpr uint8_t kMarkerData[] = {0, 1, 255, 0, 17}; -static constexpr uint8_t kMarkerSequence[] = {0xe6, 0xe8, 0xe7, - 0xe6, 0xe7, 0xe8}; -static constexpr size_t kMarkerSequenceLen = ARRAY_SIZE(kMarkerSequence); - -enum JpegIOMode { - PIXELS, - RAW_DATA, - COEFFICIENTS, -}; - -struct CustomQuantTable { - int slot_idx = 0; - uint16_t table_type = 0; - int scale_factor = 100; - bool add_raw = false; - bool force_baseline = true; - std::vector basic_table; - std::vector quantval; - void Generate(); -}; - -struct TestImage { - size_t xsize = 2268; - size_t ysize = 1512; - int color_space = 2; // JCS_RGB - size_t components = 3; - JpegliDataType data_type = JPEGLI_TYPE_UINT8; - JpegliEndianness endianness = JPEGLI_NATIVE_ENDIAN; - std::vector pixels; - std::vector> raw_data; - std::vector> coeffs; - void AllocatePixels() { - pixels.resize(ysize * xsize * components * - jpegli_bytes_per_sample(data_type)); - } - void Clear() { - pixels.clear(); - raw_data.clear(); - coeffs.clear(); - } -}; - -struct CompressParams { - int quality = 90; - bool set_jpeg_colorspace = false; - int jpeg_color_space = 0; // JCS_UNKNOWN - std::vector quant_indexes; - std::vector quant_tables; - std::vector h_sampling; - std::vector v_sampling; - std::vector comp_ids; - int override_JFIF = -1; - int override_Adobe = -1; - bool add_marker = false; - bool simple_progression = false; - // -1 is library default - // 0, 1, 2 is set through jpegli_set_progressive_level() - // 2 + N is kScriptN - int progressive_mode = -1; - unsigned int restart_interval = 0; - int restart_in_rows = 0; - int smoothing_factor = 0; - int optimize_coding = -1; - bool use_flat_dc_luma_code = false; - bool omit_standard_tables = false; - bool xyb_mode = false; - bool libjpeg_mode = false; - bool use_adaptive_quantization = true; - std::vector icc; - - int h_samp(int c) const { return h_sampling.empty() ? 1 : h_sampling[c]; } - int v_samp(int c) const { return v_sampling.empty() ? 1 : v_sampling[c]; } - int max_h_sample() const { - auto it = std::max_element(h_sampling.begin(), h_sampling.end()); - return it == h_sampling.end() ? 1 : *it; - } - int max_v_sample() const { - auto it = std::max_element(v_sampling.begin(), v_sampling.end()); - return it == v_sampling.end() ? 1 : *it; - } - int comp_width(const TestImage& input, int c) const { - return DivCeil(input.xsize * h_samp(c), max_h_sample() * 8) * 8; - } - int comp_height(const TestImage& input, int c) const { - return DivCeil(input.ysize * v_samp(c), max_v_sample() * 8) * 8; - } -}; - -enum ColorQuantMode { - CQUANT_1PASS, - CQUANT_2PASS, - CQUANT_EXTERNAL, - CQUANT_REUSE, -}; - -struct ScanDecompressParams { - int max_scan_number; - int dither_mode; - ColorQuantMode color_quant_mode; -}; - -struct DecompressParams { - float size_factor = 1.0f; - size_t chunk_size = 65536; - size_t max_output_lines = 16; - JpegIOMode output_mode = PIXELS; - JpegliDataType data_type = JPEGLI_TYPE_UINT8; - JpegliEndianness endianness = JPEGLI_NATIVE_ENDIAN; - bool set_out_color_space = false; - int out_color_space = 0; // JCS_UNKNOWN - bool crop_output = false; - bool do_block_smoothing = false; - bool do_fancy_upsampling = true; - bool skip_scans = false; - int scale_num = 1; - int scale_denom = 1; - bool quantize_colors = false; - int desired_number_of_colors = 256; - std::vector scan_params; -}; - -} // namespace jpegli - -#endif // LIB_JPEGLI_TEST_PARAMS_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h b/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h deleted file mode 100644 index da1a924e21f85..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This template file is included in both the libjpeg_test_util.cc and the -// test_utils.cc files with different JPEG_API_FN macros and possibly different -// include paths for the jpeg headers. - -// Sequential non-interleaved. -constexpr jpeg_scan_info kScript1[] = { - {1, {0}, 0, 63, 0, 0}, - {1, {1}, 0, 63, 0, 0}, - {1, {2}, 0, 63, 0, 0}, -}; -// Sequential partially interleaved, chroma first. -constexpr jpeg_scan_info kScript2[] = { - {2, {1, 2}, 0, 63, 0, 0}, - {1, {0}, 0, 63, 0, 0}, -}; - -// Rest of the scan scripts are progressive. - -constexpr jpeg_scan_info kScript3[] = { - // Interleaved full DC. - {3, {0, 1, 2}, 0, 0, 0, 0}, - // Full AC scans. - {1, {0}, 1, 63, 0, 0}, - {1, {1}, 1, 63, 0, 0}, - {1, {2}, 1, 63, 0, 0}, -}; -constexpr jpeg_scan_info kScript4[] = { - // Non-interleaved full DC. - {1, {0}, 0, 0, 0, 0}, - {1, {1}, 0, 0, 0, 0}, - {1, {2}, 0, 0, 0, 0}, - // Full AC scans. - {1, {0}, 1, 63, 0, 0}, - {1, {1}, 1, 63, 0, 0}, - {1, {2}, 1, 63, 0, 0}, -}; -constexpr jpeg_scan_info kScript5[] = { - // Partially interleaved full DC, chroma first. - {2, {1, 2}, 0, 0, 0, 0}, - {1, {0}, 0, 0, 0, 0}, - // AC shifted by 1 bit. - {1, {0}, 1, 63, 0, 1}, - {1, {1}, 1, 63, 0, 1}, - {1, {2}, 1, 63, 0, 1}, - // AC refinement scan. - {1, {0}, 1, 63, 1, 0}, - {1, {1}, 1, 63, 1, 0}, - {1, {2}, 1, 63, 1, 0}, -}; -constexpr jpeg_scan_info kScript6[] = { - // Interleaved DC shifted by 2 bits. - {3, {0, 1, 2}, 0, 0, 0, 2}, - // Interleaved DC refinement scans. - {3, {0, 1, 2}, 0, 0, 2, 1}, - {3, {0, 1, 2}, 0, 0, 1, 0}, - // Full AC scans. - {1, {0}, 1, 63, 0, 0}, - {1, {1}, 1, 63, 0, 0}, - {1, {2}, 1, 63, 0, 0}, -}; - -constexpr jpeg_scan_info kScript7[] = { - // Non-interleaved DC shifted by 2 bits. - {1, {0}, 0, 0, 0, 2}, - {1, {1}, 0, 0, 0, 2}, - {1, {2}, 0, 0, 0, 2}, - // Non-interleaved DC first refinement scans. - {1, {0}, 0, 0, 2, 1}, - {1, {1}, 0, 0, 2, 1}, - {1, {2}, 0, 0, 2, 1}, - // Non-interleaved DC second refinement scans. - {1, {0}, 0, 0, 1, 0}, - {1, {1}, 0, 0, 1, 0}, - {1, {2}, 0, 0, 1, 0}, - // Full AC scans. - {1, {0}, 1, 63, 0, 0}, - {1, {1}, 1, 63, 0, 0}, - {1, {2}, 1, 63, 0, 0}, -}; - -constexpr jpeg_scan_info kScript8[] = { - // Partially interleaved DC shifted by 2 bits, chroma first - {2, {1, 2}, 0, 0, 0, 2}, - {1, {0}, 0, 0, 0, 2}, - // Partially interleaved DC first refinement scans. - {2, {0, 2}, 0, 0, 2, 1}, - {1, {1}, 0, 0, 2, 1}, - // Partially interleaved DC first refinement scans, chroma first. - {2, {1, 2}, 0, 0, 1, 0}, - {1, {0}, 0, 0, 1, 0}, - // Full AC scans. - {1, {0}, 1, 63, 0, 0}, - {1, {1}, 1, 63, 0, 0}, - {1, {2}, 1, 63, 0, 0}, -}; - -constexpr jpeg_scan_info kScript9[] = { - // Interleaved full DC. - {3, {0, 1, 2}, 0, 0, 0, 0}, - // AC scans for component 0 - // shifted by 1 bit, two spectral ranges - {1, {0}, 1, 6, 0, 1}, - {1, {0}, 7, 63, 0, 1}, - // refinement scan, full - {1, {0}, 1, 63, 1, 0}, - // AC scans for component 1 - // shifted by 1 bit, full - {1, {1}, 1, 63, 0, 1}, - // refinement scan, two spectral ranges - {1, {1}, 1, 6, 1, 0}, - {1, {1}, 7, 63, 1, 0}, - // AC scans for component 2 - // shifted by 1 bit, two spectral ranges - {1, {2}, 1, 6, 0, 1}, - {1, {2}, 7, 63, 0, 1}, - // refinement scan, two spectral ranges (but different from above) - {1, {2}, 1, 16, 1, 0}, - {1, {2}, 17, 63, 1, 0}, -}; - -constexpr jpeg_scan_info kScript10[] = { - // Interleaved full DC. - {3, {0, 1, 2}, 0, 0, 0, 0}, - // AC scans for spectral range 1..16 - // shifted by 1 - {1, {0}, 1, 16, 0, 1}, - {1, {1}, 1, 16, 0, 1}, - {1, {2}, 1, 16, 0, 1}, - // refinement scans, two sub-ranges - {1, {0}, 1, 8, 1, 0}, - {1, {0}, 9, 16, 1, 0}, - {1, {1}, 1, 8, 1, 0}, - {1, {1}, 9, 16, 1, 0}, - {1, {2}, 1, 8, 1, 0}, - {1, {2}, 9, 16, 1, 0}, - // AC scans for spectral range 17..63 - {1, {0}, 17, 63, 0, 1}, - {1, {1}, 17, 63, 0, 1}, - {1, {2}, 17, 63, 0, 1}, - // refinement scans, two sub-ranges - {1, {0}, 17, 28, 1, 0}, - {1, {0}, 29, 63, 1, 0}, - {1, {1}, 17, 28, 1, 0}, - {1, {1}, 29, 63, 1, 0}, - {1, {2}, 17, 28, 1, 0}, - {1, {2}, 29, 63, 1, 0}, -}; - -struct ScanScript { - int num_scans; - const jpeg_scan_info* scans; -}; - -constexpr ScanScript kTestScript[] = { - {ARRAY_SIZE(kScript1), kScript1}, {ARRAY_SIZE(kScript2), kScript2}, - {ARRAY_SIZE(kScript3), kScript3}, {ARRAY_SIZE(kScript4), kScript4}, - {ARRAY_SIZE(kScript5), kScript5}, {ARRAY_SIZE(kScript6), kScript6}, - {ARRAY_SIZE(kScript7), kScript7}, {ARRAY_SIZE(kScript8), kScript8}, - {ARRAY_SIZE(kScript9), kScript9}, {ARRAY_SIZE(kScript10), kScript10}, -}; -constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript); - -void SetScanDecompressParams(const DecompressParams& dparams, - j_decompress_ptr cinfo, int scan_number) { - const ScanDecompressParams* sparams = nullptr; - for (const auto& sp : dparams.scan_params) { - if (scan_number <= sp.max_scan_number) { - sparams = &sp; - break; - } - } - if (sparams == nullptr) { - return; - } - if (dparams.quantize_colors) { - cinfo->dither_mode = static_cast(sparams->dither_mode); - if (sparams->color_quant_mode == CQUANT_1PASS) { - cinfo->two_pass_quantize = FALSE; - cinfo->colormap = nullptr; - } else if (sparams->color_quant_mode == CQUANT_2PASS) { - Check(cinfo->out_color_space == JCS_RGB); - cinfo->two_pass_quantize = TRUE; - cinfo->colormap = nullptr; - } else if (sparams->color_quant_mode == CQUANT_EXTERNAL) { - Check(cinfo->out_color_space == JCS_RGB); - cinfo->two_pass_quantize = FALSE; - bool have_colormap = cinfo->colormap != nullptr; - cinfo->actual_number_of_colors = kTestColorMapNumColors; - cinfo->colormap = (*cinfo->mem->alloc_sarray)( - reinterpret_cast(cinfo), JPOOL_IMAGE, - cinfo->actual_number_of_colors, 3); - jxl::msan::UnpoisonMemory(reinterpret_cast(cinfo->colormap), - 3 * sizeof(JSAMPLE*)); - for (int i = 0; i < kTestColorMapNumColors; ++i) { - cinfo->colormap[0][i] = (kTestColorMap[i] >> 16) & 0xff; - cinfo->colormap[1][i] = (kTestColorMap[i] >> 8) & 0xff; - cinfo->colormap[2][i] = (kTestColorMap[i] >> 0) & 0xff; - } - if (have_colormap) { - JPEG_API_FN(new_colormap)(cinfo); - } - } else if (sparams->color_quant_mode == CQUANT_REUSE) { - Check(cinfo->out_color_space == JCS_RGB); - Check(cinfo->colormap); - } - } -} - -void SetDecompressParams(const DecompressParams& dparams, - j_decompress_ptr cinfo) { - cinfo->do_block_smoothing = dparams.do_block_smoothing ? 1 : 0; - cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling ? 1 : 0; - if (dparams.output_mode == RAW_DATA) { - cinfo->raw_data_out = TRUE; - } - if (dparams.set_out_color_space) { - cinfo->out_color_space = - static_cast(dparams.out_color_space); - if (dparams.out_color_space == JCS_UNKNOWN) { - cinfo->jpeg_color_space = JCS_UNKNOWN; - } - } - cinfo->scale_num = dparams.scale_num; - cinfo->scale_denom = dparams.scale_denom; - cinfo->quantize_colors = dparams.quantize_colors ? 1 : 0; - cinfo->desired_number_of_colors = dparams.desired_number_of_colors; - if (!dparams.scan_params.empty()) { - if (cinfo->buffered_image) { - for (const auto& sparams : dparams.scan_params) { - if (sparams.color_quant_mode == CQUANT_1PASS) { - cinfo->enable_1pass_quant = TRUE; - } else if (sparams.color_quant_mode == CQUANT_2PASS) { - cinfo->enable_2pass_quant = TRUE; - } else if (sparams.color_quant_mode == CQUANT_EXTERNAL) { - cinfo->enable_external_quant = TRUE; - } - } - SetScanDecompressParams(dparams, cinfo, 1); - } else { - SetScanDecompressParams(dparams, cinfo, kLastScan); - } - } -} - -void CheckMarkerPresent(j_decompress_ptr cinfo, uint8_t marker_type) { - bool marker_found = false; - for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; - marker = marker->next) { - jxl::msan::UnpoisonMemory(marker, sizeof(*marker)); - jxl::msan::UnpoisonMemory(marker->data, marker->data_length); - if (marker->marker == marker_type && - marker->data_length == sizeof(kMarkerData) && - memcmp(marker->data, kMarkerData, sizeof(kMarkerData)) == 0) { - marker_found = true; - } - } - Check(marker_found); -} - -void VerifyHeader(const CompressParams& jparams, j_decompress_ptr cinfo) { - if (jparams.set_jpeg_colorspace) { - Check(cinfo->jpeg_color_space == jparams.jpeg_color_space); - } - if (jparams.override_JFIF >= 0) { - Check(cinfo->saw_JFIF_marker == jparams.override_JFIF); - } - if (jparams.override_Adobe >= 0) { - Check(cinfo->saw_Adobe_marker == jparams.override_Adobe); - } - if (jparams.add_marker) { - CheckMarkerPresent(cinfo, kSpecialMarker0); - CheckMarkerPresent(cinfo, kSpecialMarker1); - } - jxl::msan::UnpoisonMemory( - cinfo->comp_info, cinfo->num_components * sizeof(cinfo->comp_info[0])); - int max_h_samp_factor = 1; - int max_v_samp_factor = 1; - for (int i = 0; i < cinfo->num_components; ++i) { - jpeg_component_info* comp = &cinfo->comp_info[i]; - if (!jparams.comp_ids.empty()) { - Check(comp->component_id == jparams.comp_ids[i]); - } - if (!jparams.h_sampling.empty()) { - Check(comp->h_samp_factor == jparams.h_sampling[i]); - } - if (!jparams.v_sampling.empty()) { - Check(comp->v_samp_factor == jparams.v_sampling[i]); - } - if (!jparams.quant_indexes.empty()) { - Check(comp->quant_tbl_no == jparams.quant_indexes[i]); - } - max_h_samp_factor = std::max(max_h_samp_factor, comp->h_samp_factor); - max_v_samp_factor = std::max(max_v_samp_factor, comp->v_samp_factor); - } - Check(max_h_samp_factor == cinfo->max_h_samp_factor); - Check(max_v_samp_factor == cinfo->max_v_samp_factor); - int referenced_tables[NUM_QUANT_TBLS] = {}; - for (int i = 0; i < cinfo->num_components; ++i) { - jpeg_component_info* comp = &cinfo->comp_info[i]; - Check(comp->width_in_blocks == - DivCeil(cinfo->image_width * comp->h_samp_factor, - max_h_samp_factor * DCTSIZE)); - Check(comp->height_in_blocks == - DivCeil(cinfo->image_height * comp->v_samp_factor, - max_v_samp_factor * DCTSIZE)); - referenced_tables[comp->quant_tbl_no] = 1; - } - for (const auto& table : jparams.quant_tables) { - JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[table.slot_idx]; - if (!referenced_tables[table.slot_idx]) { - Check(quant_table == nullptr); - continue; - } - Check(quant_table != nullptr); - jxl::msan::UnpoisonMemory(quant_table, sizeof(*quant_table)); - for (int k = 0; k < DCTSIZE2; ++k) { - Check(quant_table->quantval[k] == table.quantval[k]); - } - } -} - -void VerifyScanHeader(const CompressParams& jparams, j_decompress_ptr cinfo) { - Check(cinfo->input_scan_number > 0); - if (cinfo->progressive_mode) { - Check(cinfo->Ss != 0 || cinfo->Se != 63); - } else { - Check(cinfo->Ss == 0 && cinfo->Se == 63); - } - if (jparams.progressive_mode > 2) { - Check(jparams.progressive_mode < 3 + kNumTestScripts); - const ScanScript& script = kTestScript[jparams.progressive_mode - 3]; - Check(cinfo->input_scan_number <= script.num_scans); - const jpeg_scan_info& scan = script.scans[cinfo->input_scan_number - 1]; - Check(cinfo->comps_in_scan == scan.comps_in_scan); - for (int i = 0; i < cinfo->comps_in_scan; ++i) { - Check(cinfo->cur_comp_info[i]->component_index == - scan.component_index[i]); - } - Check(cinfo->Ss == scan.Ss); - Check(cinfo->Se == scan.Se); - Check(cinfo->Ah == scan.Ah); - Check(cinfo->Al == scan.Al); - } - if (jparams.restart_interval > 0) { - Check(cinfo->restart_interval == jparams.restart_interval); - } else if (jparams.restart_in_rows > 0) { - Check(cinfo->restart_interval == - jparams.restart_in_rows * cinfo->MCUs_per_row); - } - if (jparams.progressive_mode == 0 && jparams.optimize_coding == 0) { - if (cinfo->jpeg_color_space == JCS_RGB) { - Check(cinfo->comp_info[0].dc_tbl_no == 0); - Check(cinfo->comp_info[1].dc_tbl_no == 0); - Check(cinfo->comp_info[2].dc_tbl_no == 0); - Check(cinfo->comp_info[0].ac_tbl_no == 0); - Check(cinfo->comp_info[1].ac_tbl_no == 0); - Check(cinfo->comp_info[2].ac_tbl_no == 0); - } else if (cinfo->jpeg_color_space == JCS_YCbCr) { - Check(cinfo->comp_info[0].dc_tbl_no == 0); - Check(cinfo->comp_info[1].dc_tbl_no == 1); - Check(cinfo->comp_info[2].dc_tbl_no == 1); - Check(cinfo->comp_info[0].ac_tbl_no == 0); - Check(cinfo->comp_info[1].ac_tbl_no == 1); - Check(cinfo->comp_info[2].ac_tbl_no == 1); - } else if (cinfo->jpeg_color_space == JCS_CMYK) { - Check(cinfo->comp_info[0].dc_tbl_no == 0); - Check(cinfo->comp_info[1].dc_tbl_no == 0); - Check(cinfo->comp_info[2].dc_tbl_no == 0); - Check(cinfo->comp_info[3].dc_tbl_no == 0); - Check(cinfo->comp_info[0].ac_tbl_no == 0); - Check(cinfo->comp_info[1].ac_tbl_no == 0); - Check(cinfo->comp_info[2].ac_tbl_no == 0); - Check(cinfo->comp_info[3].ac_tbl_no == 0); - } else if (cinfo->jpeg_color_space == JCS_YCCK) { - Check(cinfo->comp_info[0].dc_tbl_no == 0); - Check(cinfo->comp_info[1].dc_tbl_no == 1); - Check(cinfo->comp_info[2].dc_tbl_no == 1); - Check(cinfo->comp_info[3].dc_tbl_no == 0); - Check(cinfo->comp_info[0].ac_tbl_no == 0); - Check(cinfo->comp_info[1].ac_tbl_no == 1); - Check(cinfo->comp_info[2].ac_tbl_no == 1); - Check(cinfo->comp_info[3].ac_tbl_no == 0); - } - if (jparams.use_flat_dc_luma_code) { - JHUFF_TBL* tbl = cinfo->dc_huff_tbl_ptrs[0]; - jxl::msan::UnpoisonMemory(tbl, sizeof(*tbl)); - for (int i = 0; i < 15; ++i) { - Check(tbl->huffval[i] == i); - } - } - } -} - -void UnmapColors(uint8_t* row, size_t xsize, int components, - JSAMPARRAY colormap, size_t num_colors) { - Check(colormap != nullptr); - std::vector tmp(xsize * components); - for (size_t x = 0; x < xsize; ++x) { - Check(row[x] < num_colors); - for (int c = 0; c < components; ++c) { - tmp[x * components + c] = colormap[c][row[x]]; - } - } - memcpy(row, tmp.data(), tmp.size()); -} - -void CopyCoefficients(j_decompress_ptr cinfo, jvirt_barray_ptr* coef_arrays, - TestImage* output) { - output->xsize = cinfo->image_width; - output->ysize = cinfo->image_height; - output->components = cinfo->num_components; - output->color_space = cinfo->out_color_space; - j_common_ptr comptr = reinterpret_cast(cinfo); - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - std::vector coeffs(comp->width_in_blocks * comp->height_in_blocks * - DCTSIZE2); - for (size_t by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY blocks = (*cinfo->mem->access_virt_barray)( - comptr, coef_arrays[c], by, 1, TRUE); - size_t stride = comp->width_in_blocks * sizeof(JBLOCK); - size_t offset = by * comp->width_in_blocks * DCTSIZE2; - memcpy(&coeffs[offset], blocks[0], stride); - } - output->coeffs.emplace_back(std::move(coeffs)); - } -} diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils.cc b/third_party/jpeg-xl/lib/jpegli/test_utils.cc deleted file mode 100644 index 2b903fa0c022f..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/test_utils.cc +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/test_utils.h" - -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/sanitizers.h" -#include "lib/jxl/base/status.h" - -#if !defined(TEST_DATA_PATH) -#include "tools/cpp/runfiles/runfiles.h" -#endif - -namespace jpegli { - -namespace { -void Check(bool ok) { - if (!ok) { - JXL_CRASH(); - } -} -#define QUIT(M) Check(false); -} // namespace - -#define JPEG_API_FN(name) jpegli_##name -#include "lib/jpegli/test_utils-inl.h" -#undef JPEG_API_FN - -#if defined(TEST_DATA_PATH) -std::string GetTestDataPath(const std::string& filename) { - return std::string(TEST_DATA_PATH "/") + filename; -} -#else -using ::bazel::tools::cpp::runfiles::Runfiles; -const std::unique_ptr kRunfiles(Runfiles::Create("")); -std::string GetTestDataPath(const std::string& filename) { - std::string root(JPEGXL_ROOT_PACKAGE "/testdata/"); - return kRunfiles->Rlocation(root + filename); -} -#endif - -jxl::StatusOr> ReadTestData(const std::string& filename) { - std::vector data; - std::string full_path = GetTestDataPath(filename); - fprintf(stderr, "ReadTestData %s\n", full_path.c_str()); - std::ifstream file(full_path, std::ios::binary); - std::vector str((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); - JXL_ENSURE(file.good()); - const uint8_t* raw = reinterpret_cast(str.data()); - data = std::vector(raw, raw + str.size()); - printf("Test data %s is %d bytes long.\n", filename.c_str(), - static_cast(data.size())); - return data; -} - -void CustomQuantTable::Generate() { - basic_table.resize(DCTSIZE2); - quantval.resize(DCTSIZE2); - switch (table_type) { - case 0: { - for (int k = 0; k < DCTSIZE2; ++k) { - basic_table[k] = k + 1; - } - break; - } - default: - for (int k = 0; k < DCTSIZE2; ++k) { - basic_table[k] = table_type; - } - } - for (int k = 0; k < DCTSIZE2; ++k) { - quantval[k] = (basic_table[k] * scale_factor + 50U) / 100U; - quantval[k] = std::max(quantval[k], 1U); - quantval[k] = std::min(quantval[k], 65535U); - if (!add_raw) { - quantval[k] = std::min(quantval[k], force_baseline ? 255U : 32767U); - } - } -} - -bool PNMParser::ParseHeader(const uint8_t** pos, size_t* xsize, size_t* ysize, - size_t* num_channels, size_t* bitdepth) { - if (pos_[0] != 'P' || (pos_[1] != '5' && pos_[1] != '6')) { - fprintf(stderr, "Invalid PNM header."); - return false; - } - *num_channels = (pos_[1] == '5' ? 1 : 3); - pos_ += 2; - - size_t maxval; - if (!SkipWhitespace() || !ParseUnsigned(xsize) || !SkipWhitespace() || - !ParseUnsigned(ysize) || !SkipWhitespace() || !ParseUnsigned(&maxval) || - !SkipWhitespace()) { - return false; - } - if (maxval == 0 || maxval >= 65536) { - fprintf(stderr, "Invalid maxval value.\n"); - return false; - } - bool found_bitdepth = false; - for (int bits = 1; bits <= 16; ++bits) { - if (maxval == (1u << bits) - 1) { - *bitdepth = bits; - found_bitdepth = true; - break; - } - } - if (!found_bitdepth) { - fprintf(stderr, "Invalid maxval value.\n"); - return false; - } - - *pos = pos_; - return true; -} - -bool PNMParser::ParseUnsigned(size_t* number) { - if (pos_ == end_ || *pos_ < '0' || *pos_ > '9') { - fprintf(stderr, "Expected unsigned number.\n"); - return false; - } - *number = 0; - while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { - *number *= 10; - *number += *pos_ - '0'; - ++pos_; - } - - return true; -} - -bool PNMParser::SkipWhitespace() { - if (pos_ == end_ || !IsWhitespace(*pos_)) { - fprintf(stderr, "Expected whitespace.\n"); - return false; - } - while (pos_ < end_ && IsWhitespace(*pos_)) { - ++pos_; - } - return true; -} - -bool ReadPNM(const std::vector& data, size_t* xsize, size_t* ysize, - size_t* num_channels, size_t* bitdepth, - std::vector* pixels) { - if (data.size() < 2) { - fprintf(stderr, "PNM file too small.\n"); - return false; - } - PNMParser parser(data.data(), data.size()); - const uint8_t* pos = nullptr; - if (!parser.ParseHeader(&pos, xsize, ysize, num_channels, bitdepth)) { - return false; - } - pixels->resize(data.data() + data.size() - pos); - memcpy(pixels->data(), pos, pixels->size()); - return true; -} - -std::string ColorSpaceName(J_COLOR_SPACE colorspace) { - switch (colorspace) { - case JCS_UNKNOWN: - return "UNKNOWN"; - case JCS_GRAYSCALE: - return "GRAYSCALE"; - case JCS_RGB: - return "RGB"; - case JCS_YCbCr: - return "YCbCr"; - case JCS_CMYK: - return "CMYK"; - case JCS_YCCK: - return "YCCK"; - case JCS_EXT_RGB: - return "EXT_RGB"; - case JCS_EXT_BGR: - return "EXT_BGR"; - case JCS_EXT_RGBA: - return "EXT_RGBA"; - case JCS_EXT_BGRA: - return "EXT_BGRA"; - case JCS_EXT_ARGB: - return "EXT_ARGB"; - case JCS_EXT_ABGR: - return "EXT_ABGR"; - default: - return ""; - } -} - -std::string IOMethodName(JpegliDataType data_type, - JpegliEndianness endianness) { - std::string retval; - if (data_type == JPEGLI_TYPE_UINT8) { - return ""; - } else if (data_type == JPEGLI_TYPE_UINT16) { - retval = "UINT16"; - } else if (data_type == JPEGLI_TYPE_FLOAT) { - retval = "FLOAT"; - } - if (endianness == JPEGLI_LITTLE_ENDIAN) { - retval += "LE"; - } else if (endianness == JPEGLI_BIG_ENDIAN) { - retval += "BE"; - } - return retval; -} - -std::string SamplingId(const CompressParams& jparams) { - std::stringstream os; - Check(jparams.h_sampling.size() == jparams.v_sampling.size()); - if (!jparams.h_sampling.empty()) { - size_t len = jparams.h_sampling.size(); - while (len > 1 && jparams.h_sampling[len - 1] == 1 && - jparams.v_sampling[len - 1] == 1) { - --len; - } - os << "SAMP"; - for (size_t i = 0; i < len; ++i) { - if (i > 0) os << "_"; - os << jparams.h_sampling[i] << "x" << jparams.v_sampling[i]; - } - } - return os.str(); -} - -std::ostream& operator<<(std::ostream& os, const TestImage& input) { - os << input.xsize << "x" << input.ysize; - os << IOMethodName(input.data_type, input.endianness); - if (input.color_space != JCS_RGB) { - os << "InputColor" - << ColorSpaceName(static_cast(input.color_space)); - } - if (input.color_space == JCS_UNKNOWN) { - os << input.components; - } - return os; -} - -std::ostream& operator<<(std::ostream& os, const CompressParams& jparams) { - os << "Q" << jparams.quality; - os << SamplingId(jparams); - if (jparams.set_jpeg_colorspace) { - os << "JpegColor" - << ColorSpaceName(static_cast(jparams.jpeg_color_space)); - } - if (!jparams.comp_ids.empty()) { - os << "CID"; - for (int cid : jparams.comp_ids) { - os << cid; - } - } - if (!jparams.quant_indexes.empty()) { - os << "QIDX"; - for (int qi : jparams.quant_indexes) { - os << qi; - } - for (const auto& table : jparams.quant_tables) { - os << "TABLE" << table.slot_idx << "T" << table.table_type << "F" - << table.scale_factor - << (table.add_raw ? "R" - : table.force_baseline ? "B" - : ""); - } - } - if (jparams.progressive_mode >= 0) { - os << "P" << jparams.progressive_mode; - } else if (jparams.simple_progression) { - os << "Psimple"; - } - if (jparams.optimize_coding == 1) { - os << "OptimizedCode"; - } else if (jparams.optimize_coding == 0) { - os << "FixedCode"; - if (jparams.use_flat_dc_luma_code) { - os << "FlatDCLuma"; - } else if (jparams.omit_standard_tables) { - os << "OmitDHT"; - } - } - if (!jparams.use_adaptive_quantization) { - os << "NoAQ"; - } - if (jparams.restart_interval > 0) { - os << "R" << jparams.restart_interval; - } - if (jparams.restart_in_rows > 0) { - os << "RR" << jparams.restart_in_rows; - } - if (jparams.xyb_mode) { - os << "XYB"; - } else if (jparams.libjpeg_mode) { - os << "Libjpeg"; - } - if (jparams.override_JFIF >= 0) { - os << (jparams.override_JFIF ? "AddJFIF" : "NoJFIF"); - } - if (jparams.override_Adobe >= 0) { - os << (jparams.override_Adobe ? "AddAdobe" : "NoAdobe"); - } - if (jparams.add_marker) { - os << "AddMarker"; - } - if (!jparams.icc.empty()) { - os << "ICCSize" << jparams.icc.size(); - } - if (jparams.smoothing_factor != 0) { - os << "SF" << jparams.smoothing_factor; - } - return os; -} - -jxl::Status SetNumChannels(J_COLOR_SPACE colorspace, size_t* channels) { - if (colorspace == JCS_GRAYSCALE) { - *channels = 1; - } else if (colorspace == JCS_RGB || colorspace == JCS_YCbCr || - colorspace == JCS_EXT_RGB || colorspace == JCS_EXT_BGR) { - *channels = 3; - } else if (colorspace == JCS_CMYK || colorspace == JCS_YCCK || - colorspace == JCS_EXT_RGBA || colorspace == JCS_EXT_BGRA || - colorspace == JCS_EXT_ARGB || colorspace == JCS_EXT_ABGR) { - *channels = 4; - } else if (colorspace == JCS_UNKNOWN) { - JXL_ENSURE(*channels <= 4); - } else { - return JXL_FAILURE("Unsupported colorspace: %d", - static_cast(colorspace)); - } - return true; -} - -void RGBToYCbCr(float r, float g, float b, float* y, float* cb, float* cr) { - *y = 0.299f * r + 0.587f * g + 0.114f * b; - *cb = -0.168736f * r - 0.331264f * g + 0.5f * b + 0.5f; - *cr = 0.5f * r - 0.418688f * g - 0.081312f * b + 0.5f; -} - -void ConvertPixel(const uint8_t* input_rgb, uint8_t* out, - J_COLOR_SPACE colorspace, size_t num_channels, - JpegliDataType data_type = JPEGLI_TYPE_UINT8, - JXL_BOOL swap_endianness = JPEGLI_NATIVE_ENDIAN) { - const float kMul = 255.0f; - float r = input_rgb[0] / kMul; - float g = input_rgb[1] / kMul; - float b = input_rgb[2] / kMul; - uint8_t out8[MAX_COMPONENTS]; - if (colorspace == JCS_GRAYSCALE) { - const float Y = 0.299f * r + 0.587f * g + 0.114f * b; - out8[0] = static_cast(std::round(Y * kMul)); - } else if (colorspace == JCS_RGB || colorspace == JCS_EXT_RGB || - colorspace == JCS_EXT_RGBA) { - out8[0] = input_rgb[0]; - out8[1] = input_rgb[1]; - out8[2] = input_rgb[2]; - if (colorspace == JCS_EXT_RGBA) out8[3] = 255; - } else if (colorspace == JCS_EXT_BGR || colorspace == JCS_EXT_BGRA) { - out8[2] = input_rgb[0]; - out8[1] = input_rgb[1]; - out8[0] = input_rgb[2]; - if (colorspace == JCS_EXT_BGRA) out8[3] = 255; - } else if (colorspace == JCS_EXT_ABGR) { - out8[0] = 255; - out8[3] = input_rgb[0]; - out8[2] = input_rgb[1]; - out8[1] = input_rgb[2]; - } else if (colorspace == JCS_EXT_ARGB) { - out8[0] = 255; - out8[1] = input_rgb[0]; - out8[2] = input_rgb[1]; - out8[3] = input_rgb[2]; - } else if (colorspace == JCS_UNKNOWN) { - for (size_t c = 0; c < num_channels; ++c) { - out8[c] = input_rgb[std::min(2, c)]; - } - } else if (colorspace == JCS_YCbCr) { - float Y; - float Cb; - float Cr; - RGBToYCbCr(r, g, b, &Y, &Cb, &Cr); - out8[0] = static_cast(std::round(Y * kMul)); - out8[1] = static_cast(std::round(Cb * kMul)); - out8[2] = static_cast(std::round(Cr * kMul)); - } else if (colorspace == JCS_CMYK || colorspace == JCS_YCCK) { - float K = 1.0f - std::max(r, std::max(g, b)); - float scaleK = 1.0f / (1.0f - K); - r *= scaleK; - g *= scaleK; - b *= scaleK; - if (colorspace == JCS_CMYK) { - out8[0] = static_cast(std::round((1.0f - r) * kMul)); - out8[1] = static_cast(std::round((1.0f - g) * kMul)); - out8[2] = static_cast(std::round((1.0f - b) * kMul)); - } else if (colorspace == JCS_YCCK) { - float Y; - float Cb; - float Cr; - RGBToYCbCr(r, g, b, &Y, &Cb, &Cr); - out8[0] = static_cast(std::round(Y * kMul)); - out8[1] = static_cast(std::round(Cb * kMul)); - out8[2] = static_cast(std::round(Cr * kMul)); - } - out8[3] = static_cast(std::round(K * kMul)); - } else { - Check(false); - } - if (data_type == JPEGLI_TYPE_UINT8) { - memcpy(out, out8, num_channels); - } else if (data_type == JPEGLI_TYPE_UINT16) { - for (size_t c = 0; c < num_channels; ++c) { - uint16_t val = (out8[c] << 8) + out8[c]; - val |= 0x40; // Make little-endian and big-endian asymmetric - if (swap_endianness) { - val = JXL_BSWAP16(val); - } - memcpy(&out[sizeof(val) * c], &val, sizeof(val)); - } - } else if (data_type == JPEGLI_TYPE_FLOAT) { - for (size_t c = 0; c < num_channels; ++c) { - float val = out8[c] / 255.0f; - if (swap_endianness) { - val = BSwapFloat(val); - } - memcpy(&out[sizeof(val) * c], &val, sizeof(val)); - } - } -} - -void ConvertToGrayscale(TestImage* img) { - if (img->color_space == JCS_GRAYSCALE) return; - Check(img->data_type == JPEGLI_TYPE_UINT8); - bool rgb_pre_alpha = - img->color_space == JCS_EXT_ARGB || img->color_space == JCS_EXT_ABGR; - bool rgb_post_alpha = - img->color_space == JCS_EXT_RGBA || img->color_space == JCS_EXT_BGRA; - bool rgb_alpha = rgb_pre_alpha || rgb_post_alpha; - bool is_rgb = img->color_space == JCS_RGB || - img->color_space == JCS_EXT_RGB || - img->color_space == JCS_EXT_BGR || rgb_alpha; - bool switch_br = img->color_space == JCS_EXT_BGR || - img->color_space == JCS_EXT_ABGR || - img->color_space == JCS_EXT_BGRA; - size_t stride = rgb_alpha ? 4 : 3; - size_t offset = rgb_pre_alpha ? 1 : 0; - for (size_t i = offset; i < img->pixels.size(); i += stride) { - if (is_rgb) { - if (switch_br) std::swap(img->pixels[i], img->pixels[i + 2]); - ConvertPixel(&img->pixels[i], &img->pixels[i / stride], JCS_GRAYSCALE, 1); - } else if (img->color_space == JCS_YCbCr) { - img->pixels[i / 3] = img->pixels[i]; - } - } - img->pixels.resize(img->pixels.size() / 3); - img->color_space = JCS_GRAYSCALE; - img->components = 1; -} - -void GeneratePixels(TestImage* img) { - JXL_ASSIGN_OR_QUIT(std::vector imgdata, - ReadTestData("jxl/flower/flower.pnm"), - "Failed to read test data"); - size_t xsize; - size_t ysize; - size_t channels; - size_t bitdepth; - std::vector pixels; - Check(ReadPNM(imgdata, &xsize, &ysize, &channels, &bitdepth, &pixels)); - if (img->xsize == 0) img->xsize = xsize; - if (img->ysize == 0) img->ysize = ysize; - Check(img->xsize <= xsize); - Check(img->ysize <= ysize); - Check(3 == channels); - Check(8 == bitdepth); - size_t in_bytes_per_pixel = channels; - size_t in_stride = xsize * in_bytes_per_pixel; - size_t x0 = (xsize - img->xsize) / 2; - size_t y0 = (ysize - img->ysize) / 2; - Check(SetNumChannels(static_cast(img->color_space), - &img->components)); - size_t out_bytes_per_pixel = - jpegli_bytes_per_sample(img->data_type) * img->components; - size_t out_stride = img->xsize * out_bytes_per_pixel; - bool swap_endianness = - (img->endianness == JPEGLI_LITTLE_ENDIAN && !IsLittleEndian()) || - (img->endianness == JPEGLI_BIG_ENDIAN && IsLittleEndian()); - img->pixels.resize(img->ysize * out_stride); - for (size_t iy = 0; iy < img->ysize; ++iy) { - size_t y = y0 + iy; - for (size_t ix = 0; ix < img->xsize; ++ix) { - size_t x = x0 + ix; - size_t idx_in = y * in_stride + x * in_bytes_per_pixel; - size_t idx_out = iy * out_stride + ix * out_bytes_per_pixel; - ConvertPixel(&pixels[idx_in], &img->pixels[idx_out], - static_cast(img->color_space), - img->components, img->data_type, - TO_JXL_BOOL(swap_endianness)); - } - } -} - -void GenerateRawData(const CompressParams& jparams, TestImage* img) { - for (size_t c = 0; c < img->components; ++c) { - size_t xsize = jparams.comp_width(*img, c); - size_t ysize = jparams.comp_height(*img, c); - size_t factor_y = jparams.max_v_sample() / jparams.v_samp(c); - size_t factor_x = jparams.max_h_sample() / jparams.h_samp(c); - size_t factor = factor_x * factor_y; - std::vector plane(ysize * xsize); - size_t bytes_per_pixel = img->components; - for (size_t y = 0; y < ysize; ++y) { - for (size_t x = 0; x < xsize; ++x) { - int result = 0; - for (size_t iy = 0; iy < factor_y; ++iy) { - size_t yy = std::min(y * factor_y + iy, img->ysize - 1); - for (size_t ix = 0; ix < factor_x; ++ix) { - size_t xx = std::min(x * factor_x + ix, img->xsize - 1); - size_t pixel_ix = (yy * img->xsize + xx) * bytes_per_pixel + c; - result += img->pixels[pixel_ix]; - } - } - result = static_cast((result + factor / 2) / factor); - plane[y * xsize + x] = result; - } - } - img->raw_data.emplace_back(std::move(plane)); - } -} - -void GenerateCoeffs(const CompressParams& jparams, TestImage* img) { - for (size_t c = 0; c < img->components; ++c) { - int xsize_blocks = jparams.comp_width(*img, c) / DCTSIZE; - int ysize_blocks = jparams.comp_height(*img, c) / DCTSIZE; - std::vector plane(ysize_blocks * xsize_blocks * DCTSIZE2); - for (int by = 0; by < ysize_blocks; ++by) { - for (int bx = 0; bx < xsize_blocks; ++bx) { - JCOEF* block = &plane[(by * xsize_blocks + bx) * DCTSIZE2]; - for (int k = 0; k < DCTSIZE2; ++k) { - block[k] = (bx - by) / (k + 1); - } - } - } - img->coeffs.emplace_back(std::move(plane)); - } -} - -void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, - j_compress_ptr cinfo) { - cinfo->image_width = input.xsize; - cinfo->image_height = input.ysize; - cinfo->input_components = input.components; - if (jparams.xyb_mode) { - jpegli_set_xyb_mode(cinfo); - } - if (jparams.libjpeg_mode) { - jpegli_enable_adaptive_quantization(cinfo, FALSE); - jpegli_use_standard_quant_tables(cinfo); - jpegli_set_progressive_level(cinfo, 0); - } - jpegli_set_defaults(cinfo); - cinfo->in_color_space = static_cast(input.color_space); - jpegli_default_colorspace(cinfo); - if (jparams.override_JFIF >= 0) { - cinfo->write_JFIF_header = jparams.override_JFIF; - } - if (jparams.override_Adobe >= 0) { - cinfo->write_Adobe_marker = jparams.override_Adobe; - } - if (jparams.set_jpeg_colorspace) { - jpegli_set_colorspace(cinfo, - static_cast(jparams.jpeg_color_space)); - } - if (!jparams.comp_ids.empty()) { - for (int c = 0; c < cinfo->num_components; ++c) { - cinfo->comp_info[c].component_id = jparams.comp_ids[c]; - } - } - if (!jparams.h_sampling.empty()) { - for (int c = 0; c < cinfo->num_components; ++c) { - cinfo->comp_info[c].h_samp_factor = jparams.h_sampling[c]; - cinfo->comp_info[c].v_samp_factor = jparams.v_sampling[c]; - } - } - jpegli_set_quality(cinfo, jparams.quality, TRUE); - if (!jparams.quant_indexes.empty()) { - for (int c = 0; c < cinfo->num_components; ++c) { - cinfo->comp_info[c].quant_tbl_no = jparams.quant_indexes[c]; - } - for (const auto& table : jparams.quant_tables) { - if (table.add_raw) { - cinfo->quant_tbl_ptrs[table.slot_idx] = - jpegli_alloc_quant_table(reinterpret_cast(cinfo)); - for (int k = 0; k < DCTSIZE2; ++k) { - cinfo->quant_tbl_ptrs[table.slot_idx]->quantval[k] = - table.quantval[k]; - } - cinfo->quant_tbl_ptrs[table.slot_idx]->sent_table = FALSE; - } else { - jpegli_add_quant_table(cinfo, table.slot_idx, table.basic_table.data(), - table.scale_factor, - TO_JXL_BOOL(table.force_baseline)); - } - } - } - if (jparams.simple_progression) { - jpegli_simple_progression(cinfo); - Check(jparams.progressive_mode == -1); - } - if (jparams.progressive_mode > 2) { - const ScanScript& script = kTestScript[jparams.progressive_mode - 3]; - cinfo->scan_info = script.scans; - cinfo->num_scans = script.num_scans; - } else if (jparams.progressive_mode >= 0) { - jpegli_set_progressive_level(cinfo, jparams.progressive_mode); - } - jpegli_set_input_format(cinfo, input.data_type, input.endianness); - jpegli_enable_adaptive_quantization( - cinfo, TO_JXL_BOOL(jparams.use_adaptive_quantization)); - cinfo->restart_interval = jparams.restart_interval; - cinfo->restart_in_rows = jparams.restart_in_rows; - cinfo->smoothing_factor = jparams.smoothing_factor; - if (jparams.optimize_coding == 1) { - cinfo->optimize_coding = TRUE; - } else if (jparams.optimize_coding == 0) { - cinfo->optimize_coding = FALSE; - } - cinfo->raw_data_in = TO_JXL_BOOL(!input.raw_data.empty()); - if (jparams.optimize_coding == 0 && jparams.use_flat_dc_luma_code) { - JHUFF_TBL* tbl = cinfo->dc_huff_tbl_ptrs[0]; - memset(tbl, 0, sizeof(*tbl)); - tbl->bits[4] = 15; - for (int i = 0; i < 15; ++i) tbl->huffval[i] = i; - } - if (input.coeffs.empty()) { - bool write_all_tables = TRUE; - if (jparams.optimize_coding == 0 && !jparams.use_flat_dc_luma_code && - jparams.omit_standard_tables) { - write_all_tables = FALSE; - cinfo->dc_huff_tbl_ptrs[0]->sent_table = TRUE; - cinfo->dc_huff_tbl_ptrs[1]->sent_table = TRUE; - cinfo->ac_huff_tbl_ptrs[0]->sent_table = TRUE; - cinfo->ac_huff_tbl_ptrs[1]->sent_table = TRUE; - } - jpegli_start_compress(cinfo, TO_JXL_BOOL(write_all_tables)); - if (jparams.add_marker) { - jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData, - sizeof(kMarkerData)); - jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData)); - for (uint8_t c : kMarkerData) { - jpegli_write_m_byte(cinfo, c); - } - for (size_t i = 0; i < kMarkerSequenceLen; ++i) { - jpegli_write_marker(cinfo, kMarkerSequence[i], kMarkerData, - ((i + 2) % sizeof(kMarkerData))); - } - } - if (!jparams.icc.empty()) { - jpegli_write_icc_profile(cinfo, jparams.icc.data(), jparams.icc.size()); - } - } - if (cinfo->raw_data_in) { - // Need to copy because jpeg API requires non-const pointers. - std::vector> raw_data = input.raw_data; - size_t max_lines = jparams.max_v_sample() * DCTSIZE; - std::vector> rowdata(cinfo->num_components); - std::vector data(cinfo->num_components); - for (int c = 0; c < cinfo->num_components; ++c) { - rowdata[c].resize(jparams.v_samp(c) * DCTSIZE); - data[c] = rowdata[c].data(); - } - while (cinfo->next_scanline < cinfo->image_height) { - for (int c = 0; c < cinfo->num_components; ++c) { - size_t cwidth = cinfo->comp_info[c].width_in_blocks * DCTSIZE; - size_t cheight = cinfo->comp_info[c].height_in_blocks * DCTSIZE; - size_t num_lines = jparams.v_samp(c) * DCTSIZE; - size_t y0 = (cinfo->next_scanline / max_lines) * num_lines; - for (size_t i = 0; i < num_lines; ++i) { - rowdata[c][i] = - (y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr); - } - } - size_t num_lines = jpegli_write_raw_data(cinfo, data.data(), max_lines); - Check(num_lines == max_lines); - } - } else if (!input.coeffs.empty()) { - j_common_ptr comptr = reinterpret_cast(cinfo); - jvirt_barray_ptr* coef_arrays = reinterpret_cast(( - *cinfo->mem->alloc_small)( - comptr, JPOOL_IMAGE, cinfo->num_components * sizeof(jvirt_barray_ptr))); - for (int c = 0; c < cinfo->num_components; ++c) { - size_t xsize_blocks = jparams.comp_width(input, c) / DCTSIZE; - size_t ysize_blocks = jparams.comp_height(input, c) / DCTSIZE; - coef_arrays[c] = (*cinfo->mem->request_virt_barray)( - comptr, JPOOL_IMAGE, FALSE, xsize_blocks, ysize_blocks, - cinfo->comp_info[c].v_samp_factor); - } - jpegli_write_coefficients(cinfo, coef_arrays); - if (jparams.add_marker) { - jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData, - sizeof(kMarkerData)); - jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData)); - for (uint8_t c : kMarkerData) { - jpegli_write_m_byte(cinfo, c); - } - } - for (int c = 0; c < cinfo->num_components; ++c) { - jpeg_component_info* comp = &cinfo->comp_info[c]; - for (size_t by = 0; by < comp->height_in_blocks; ++by) { - JBLOCKARRAY blocks = (*cinfo->mem->access_virt_barray)( - comptr, coef_arrays[c], by, 1, TRUE); - size_t stride = comp->width_in_blocks * sizeof(JBLOCK); - size_t offset = by * comp->width_in_blocks * DCTSIZE2; - memcpy(blocks[0], &input.coeffs[c][offset], stride); - } - } - } else { - size_t stride = cinfo->image_width * cinfo->input_components * - jpegli_bytes_per_sample(input.data_type); - std::vector row_bytes(stride); - for (size_t y = 0; y < cinfo->image_height; ++y) { - memcpy(row_bytes.data(), &input.pixels[y * stride], stride); - JSAMPROW row[] = {row_bytes.data()}; - jpegli_write_scanlines(cinfo, row, 1); - } - } - jpegli_finish_compress(cinfo); -} - -bool EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, - std::vector* compressed) { - uint8_t* buffer = nullptr; - unsigned long buffer_size = 0; // NOLINT - jpeg_compress_struct cinfo; - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &buffer, &buffer_size); - EncodeWithJpegli(input, jparams, &cinfo); - return true; - }; - bool success = try_catch_block(); - jpegli_destroy_compress(&cinfo); - if (success) { - compressed->resize(buffer_size); - std::copy_n(buffer, buffer_size, compressed->data()); - } - if (buffer) std::free(buffer); - return success; -} - -int NumTestScanScripts() { return kNumTestScripts; } - -void DumpImage(const TestImage& image, const std::string& fn) { - Check(image.components == 1 || image.components == 3); - size_t bytes_per_sample = jpegli_bytes_per_sample(image.data_type); - uint32_t maxval = (1u << (8 * bytes_per_sample)) - 1; - char type = image.components == 1 ? '5' : '6'; - std::ofstream out(fn.c_str(), std::ofstream::binary); - out << "P" << type << "\n" - << image.xsize << " " << image.ysize << "\n" - << maxval << "\n"; - out.write(reinterpret_cast(image.pixels.data()), - image.pixels.size()); - out.close(); -} - -double DistanceRms(const TestImage& input, const TestImage& output, - size_t start_line, size_t num_lines, double* max_diff) { - size_t stride = input.xsize * input.components; - size_t start_offset = start_line * stride; - auto get_sample = [&](const TestImage& im, const std::vector& data, - size_t idx) -> double { - size_t bytes_per_sample = jpegli_bytes_per_sample(im.data_type); - bool is_little_endian = - (im.endianness == JPEGLI_LITTLE_ENDIAN || - (im.endianness == JPEGLI_NATIVE_ENDIAN && IsLittleEndian())); - size_t offset = start_offset + idx * bytes_per_sample; - Check(offset < data.size()); - const uint8_t* p = &data[offset]; - if (im.data_type == JPEGLI_TYPE_UINT8) { - static const double mul8 = 1.0 / 255.0; - return p[0] * mul8; - } else if (im.data_type == JPEGLI_TYPE_UINT16) { - static const double mul16 = 1.0 / 65535.0; - return (is_little_endian ? LoadLE16(p) : LoadBE16(p)) * mul16; - } else if (im.data_type == JPEGLI_TYPE_FLOAT) { - return (is_little_endian ? LoadLEFloat(p) : LoadBEFloat(p)); - } - return 0.0; - }; - double diff2 = 0.0; - size_t num_samples = 0; - if (max_diff) *max_diff = 0.0; - if (!input.pixels.empty() && !output.pixels.empty()) { - num_samples = num_lines * stride; - for (size_t i = 0; i < num_samples; ++i) { - double sample_orig = get_sample(input, input.pixels, i); - double sample_output = get_sample(output, output.pixels, i); - double diff = sample_orig - sample_output; - if (max_diff) *max_diff = std::max(*max_diff, 255.0 * std::abs(diff)); - diff2 += diff * diff; - } - } else { - Check(!input.raw_data.empty()); - Check(!output.raw_data.empty()); - for (size_t c = 0; c < input.raw_data.size(); ++c) { - Check(c < output.raw_data.size()); - num_samples += input.raw_data[c].size(); - for (size_t i = 0; i < input.raw_data[c].size(); ++i) { - double sample_orig = get_sample(input, input.raw_data[c], i); - double sample_output = get_sample(output, output.raw_data[c], i); - double diff = sample_orig - sample_output; - if (max_diff) *max_diff = std::max(*max_diff, 255.0 * std::abs(diff)); - diff2 += diff * diff; - } - } - } - return std::sqrt(diff2 / num_samples) * 255.0; -} - -double DistanceRms(const TestImage& input, const TestImage& output, - double* max_diff) { - return DistanceRms(input, output, 0, output.ysize, max_diff); -} - -void VerifyOutputImage(const TestImage& input, const TestImage& output, - size_t start_line, size_t num_lines, double max_rms, - double max_diff) { - double max_d; - double rms = DistanceRms(input, output, start_line, num_lines, &max_d); - printf("rms: %f, max_rms: %f, max_d: %f, max_diff: %f\n", rms, max_rms, - max_d, max_diff); - Check(rms <= max_rms); - Check(max_d <= max_diff); -} - -void VerifyOutputImage(const TestImage& input, const TestImage& output, - double max_rms, double max_diff) { - Check(output.xsize == input.xsize); - Check(output.ysize == input.ysize); - Check(output.components == input.components); - Check(output.color_space == input.color_space); - if (!input.coeffs.empty()) { - Check(input.coeffs.size() == input.components); - Check(output.coeffs.size() == input.components); - for (size_t c = 0; c < input.components; ++c) { - Check(output.coeffs[c].size() == input.coeffs[c].size()); - Check(0 == memcmp(input.coeffs[c].data(), output.coeffs[c].data(), - input.coeffs[c].size())); - } - } else { - VerifyOutputImage(input, output, 0, output.ysize, max_rms, max_diff); - } -} - -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils.h b/third_party/jpeg-xl/lib/jpegli/test_utils.h deleted file mode 100644 index 0ebd7ffbc8224..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/test_utils.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_TEST_UTILS_H_ -#define LIB_JPEGLI_TEST_UTILS_H_ - -#include -#include -#include -#include - -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/types.h" -#include "lib/jxl/base/include_jpeglib.h" // NOLINT -#include "lib/jxl/base/status.h" - -namespace jpegli { - -#define ERROR_HANDLER_SETUP(flavor) \ - jpeg_error_mgr jerr; \ - jmp_buf env; \ - cinfo.err = flavor##_std_error(&jerr); \ - if (setjmp(env)) { \ - return false; \ - } \ - cinfo.client_data = reinterpret_cast(&env); \ - cinfo.err->error_exit = [](j_common_ptr cinfo) { \ - (*cinfo->err->output_message)(cinfo); \ - jmp_buf* env = reinterpret_cast(cinfo->client_data); \ - flavor##_destroy(cinfo); \ - longjmp(*env, 1); \ - }; - -std::string IOMethodName(JpegliDataType data_type, JpegliEndianness endianness); - -std::string ColorSpaceName(J_COLOR_SPACE colorspace); - -std::ostream& operator<<(std::ostream& os, const TestImage& input); - -std::ostream& operator<<(std::ostream& os, const CompressParams& jparams); - -int NumTestScanScripts(); - -void VerifyHeader(const CompressParams& jparams, j_decompress_ptr cinfo); -void VerifyScanHeader(const CompressParams& jparams, j_decompress_ptr cinfo); - -void SetDecompressParams(const DecompressParams& dparams, - j_decompress_ptr cinfo); - -void SetScanDecompressParams(const DecompressParams& dparams, - j_decompress_ptr cinfo, int scan_number); - -void CopyCoefficients(j_decompress_ptr cinfo, jvirt_barray_ptr* coef_arrays, - TestImage* output); - -void UnmapColors(uint8_t* row, size_t xsize, int components, - JSAMPARRAY colormap, size_t num_colors); - -std::string GetTestDataPath(const std::string& filename); -jxl::StatusOr> ReadTestData(const std::string& filename); - -class PNMParser { - public: - explicit PNMParser(const uint8_t* data, const size_t len) - : pos_(data), end_(data + len) {} - - // Sets "pos" to the first non-header byte/pixel on success. - bool ParseHeader(const uint8_t** pos, size_t* xsize, size_t* ysize, - size_t* num_channels, size_t* bitdepth); - - private: - static bool IsLineBreak(const uint8_t c) { return c == '\r' || c == '\n'; } - static bool IsWhitespace(const uint8_t c) { - return IsLineBreak(c) || c == '\t' || c == ' '; - } - - bool ParseUnsigned(size_t* number); - - bool SkipWhitespace(); - - const uint8_t* pos_; - const uint8_t* const end_; -}; - -bool ReadPNM(const std::vector& data, size_t* xsize, size_t* ysize, - size_t* num_channels, size_t* bitdepth, - std::vector* pixels); - -jxl::Status SetNumChannels(J_COLOR_SPACE colorspace, size_t* channels); - -void ConvertToGrayscale(TestImage* img); - -void GeneratePixels(TestImage* img); - -void GenerateRawData(const CompressParams& jparams, TestImage* img); - -void GenerateCoeffs(const CompressParams& jparams, TestImage* img); - -void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, - j_compress_ptr cinfo); - -bool EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, - std::vector* compressed); - -double DistanceRms(const TestImage& input, const TestImage& output, - size_t start_line, size_t num_lines, - double* max_diff = nullptr); - -double DistanceRms(const TestImage& input, const TestImage& output, - double* max_diff = nullptr); - -void VerifyOutputImage(const TestImage& input, const TestImage& output, - size_t start_line, size_t num_lines, double max_rms, - double max_diff = 255.0); - -void VerifyOutputImage(const TestImage& input, const TestImage& output, - double max_rms, double max_diff = 255.0); - -} // namespace jpegli - -#endif // LIB_JPEGLI_TEST_UTILS_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/testing.h b/third_party/jpeg-xl/lib/jpegli/testing.h deleted file mode 100644 index 58df563f06644..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/testing.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_TESTING_H_ -#define LIB_JPEGLI_TESTING_H_ - -// GTest specific macros / wrappers. - -#include "gtest/gtest.h" - -// googletest before 1.10 didn't define INSTANTIATE_TEST_SUITE_P() but instead -// used INSTANTIATE_TEST_CASE_P which is now deprecated. -#ifdef INSTANTIATE_TEST_SUITE_P -#define JPEGLI_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_SUITE_P -#else -#define JPEGLI_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_CASE_P -#endif - -// Replacement for ASSERT_TRUE inside try-catch blocks. -#define JPEGLI_TEST_ENSURE_TRUE(C) \ - if (!(C)) return false; - -#define QUIT(M) FAIL() << M - -// Ensures that we don't make our test bounds too lax, effectively disabling the -// tests. -#define EXPECT_SLIGHTLY_BELOW(A, E) \ - { \ - double _actual = (A); \ - double _expected = (E); \ - EXPECT_LE(_actual, _expected); \ - EXPECT_GE(_actual, 0.75 * _expected); \ - } - -#endif // LIB_JPEGLI_TESTING_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/transcode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/transcode_api_test.cc deleted file mode 100644 index 42d7e4f7fe324..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/transcode_api_test.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jpegli/decode.h" -#include "lib/jpegli/encode.h" -#include "lib/jpegli/libjpeg_test_util.h" -#include "lib/jpegli/test_params.h" -#include "lib/jpegli/test_utils.h" -#include "lib/jpegli/testing.h" - -namespace jpegli { -namespace { - -void TranscodeWithJpegli(const std::vector& jpeg_input, - const CompressParams& jparams, - std::vector* jpeg_output) { - jpeg_decompress_struct dinfo = {}; - jpeg_compress_struct cinfo = {}; - uint8_t* transcoded_data = nullptr; - unsigned long transcoded_size; // NOLINT - const auto try_catch_block = [&]() -> bool { - ERROR_HANDLER_SETUP(jpegli); - dinfo.err = cinfo.err; - dinfo.client_data = cinfo.client_data; - jpegli_create_decompress(&dinfo); - jpegli_mem_src(&dinfo, jpeg_input.data(), jpeg_input.size()); - EXPECT_EQ(JPEG_REACHED_SOS, - jpegli_read_header(&dinfo, /*require_image=*/TRUE)); - jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&dinfo); - JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr); - jpegli_create_compress(&cinfo); - jpegli_mem_dest(&cinfo, &transcoded_data, &transcoded_size); - jpegli_copy_critical_parameters(&dinfo, &cinfo); - jpegli_set_progressive_level(&cinfo, jparams.progressive_mode); - cinfo.optimize_coding = jparams.optimize_coding; - jpegli_write_coefficients(&cinfo, coef_arrays); - jpegli_finish_compress(&cinfo); - jpegli_finish_decompress(&dinfo); - return true; - }; - ASSERT_TRUE(try_catch_block()); - jpegli_destroy_decompress(&dinfo); - jpegli_destroy_compress(&cinfo); - if (transcoded_data) { - jpeg_output->assign(transcoded_data, transcoded_data + transcoded_size); - free(transcoded_data); - } -} - -struct TestConfig { - TestImage input; - CompressParams jparams; -}; - -class TranscodeAPITestParam : public ::testing::TestWithParam {}; - -TEST_P(TranscodeAPITestParam, TestAPI) { - TestConfig config = GetParam(); - CompressParams& jparams = config.jparams; - GeneratePixels(&config.input); - - // Start with sequential non-optimized jpeg. - jparams.progressive_mode = 0; - jparams.optimize_coding = 0; - std::vector compressed; - ASSERT_TRUE(EncodeWithJpegli(config.input, jparams, &compressed)); - TestImage output0; - DecodeWithLibjpeg(jparams, DecompressParams(), compressed, &output0); - - // Transcode first to a sequential optimized jpeg, and then further to - // a progressive jpeg. - for (int progr : {0, 2}) { - std::vector transcoded; - jparams.progressive_mode = progr; - jparams.optimize_coding = 1; - TranscodeWithJpegli(compressed, jparams, &transcoded); - - // We expect a size reduction of at least 2%. - EXPECT_LT(transcoded.size(), compressed.size() * 0.98f); - - // Verify that transcoding is lossless. - TestImage output1; - DecodeWithLibjpeg(jparams, DecompressParams(), transcoded, &output1); - ASSERT_EQ(output0.pixels.size(), output1.pixels.size()); - EXPECT_EQ(0, memcmp(output0.pixels.data(), output1.pixels.data(), - output0.pixels.size())); - compressed = transcoded; - } -} - -std::vector GenerateTests() { - std::vector all_tests; - const size_t xsize0 = 1024; - const size_t ysize0 = 768; - for (int dxsize : {0, 1, 8, 9}) { - for (int dysize : {0, 1, 8, 9}) { - for (int h_sampling : {1, 2}) { - for (int v_sampling : {1, 2}) { - TestConfig config; - config.input.xsize = xsize0 + dxsize; - config.input.ysize = ysize0 + dysize; - config.jparams.h_sampling = {h_sampling, 1, 1}; - config.jparams.v_sampling = {v_sampling, 1, 1}; - all_tests.push_back(config); - } - } - } - } - return all_tests; -} - -std::ostream& operator<<(std::ostream& os, const TestConfig& c) { - os << c.input; - os << c.jparams; - return os; -} - -std::string TestDescription( - const testing::TestParamInfo& info) { - std::stringstream name; - name << info.param; - return name.str(); -} - -JPEGLI_INSTANTIATE_TEST_SUITE_P(TranscodeAPITest, TranscodeAPITestParam, - testing::ValuesIn(GenerateTests()), - TestDescription); - -} // namespace -} // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h b/third_party/jpeg-xl/lib/jpegli/transpose-inl.h deleted file mode 100644 index cdc289f96c4c8..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#if defined(LIB_JPEGLI_TRANSPOSE_INL_H_) == defined(HWY_TARGET_TOGGLE) -#ifdef LIB_JPEGLI_TRANSPOSE_INL_H_ -#undef LIB_JPEGLI_TRANSPOSE_INL_H_ -#else -#define LIB_JPEGLI_TRANSPOSE_INL_H_ -#endif - -#include "lib/jxl/base/compiler_specific.h" - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { -namespace { - -#if HWY_CAP_GE256 -JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, - float* JXL_RESTRICT to) { - const HWY_CAPPED(float, 8) d; - auto i0 = Load(d, from); - auto i1 = Load(d, from + 1 * 8); - auto i2 = Load(d, from + 2 * 8); - auto i3 = Load(d, from + 3 * 8); - auto i4 = Load(d, from + 4 * 8); - auto i5 = Load(d, from + 5 * 8); - auto i6 = Load(d, from + 6 * 8); - auto i7 = Load(d, from + 7 * 8); - - const auto q0 = InterleaveLower(d, i0, i2); - const auto q1 = InterleaveLower(d, i1, i3); - const auto q2 = InterleaveUpper(d, i0, i2); - const auto q3 = InterleaveUpper(d, i1, i3); - const auto q4 = InterleaveLower(d, i4, i6); - const auto q5 = InterleaveLower(d, i5, i7); - const auto q6 = InterleaveUpper(d, i4, i6); - const auto q7 = InterleaveUpper(d, i5, i7); - - const auto r0 = InterleaveLower(d, q0, q1); - const auto r1 = InterleaveUpper(d, q0, q1); - const auto r2 = InterleaveLower(d, q2, q3); - const auto r3 = InterleaveUpper(d, q2, q3); - const auto r4 = InterleaveLower(d, q4, q5); - const auto r5 = InterleaveUpper(d, q4, q5); - const auto r6 = InterleaveLower(d, q6, q7); - const auto r7 = InterleaveUpper(d, q6, q7); - - i0 = ConcatLowerLower(d, r4, r0); - i1 = ConcatLowerLower(d, r5, r1); - i2 = ConcatLowerLower(d, r6, r2); - i3 = ConcatLowerLower(d, r7, r3); - i4 = ConcatUpperUpper(d, r4, r0); - i5 = ConcatUpperUpper(d, r5, r1); - i6 = ConcatUpperUpper(d, r6, r2); - i7 = ConcatUpperUpper(d, r7, r3); - - Store(i0, d, to); - Store(i1, d, to + 1 * 8); - Store(i2, d, to + 2 * 8); - Store(i3, d, to + 3 * 8); - Store(i4, d, to + 4 * 8); - Store(i5, d, to + 5 * 8); - Store(i6, d, to + 6 * 8); - Store(i7, d, to + 7 * 8); -} -#elif HWY_TARGET != HWY_SCALAR -JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, - float* JXL_RESTRICT to) { - const HWY_CAPPED(float, 4) d; - for (size_t n = 0; n < 8; n += 4) { - for (size_t m = 0; m < 8; m += 4) { - auto p0 = Load(d, from + n * 8 + m); - auto p1 = Load(d, from + (n + 1) * 8 + m); - auto p2 = Load(d, from + (n + 2) * 8 + m); - auto p3 = Load(d, from + (n + 3) * 8 + m); - const auto q0 = InterleaveLower(d, p0, p2); - const auto q1 = InterleaveLower(d, p1, p3); - const auto q2 = InterleaveUpper(d, p0, p2); - const auto q3 = InterleaveUpper(d, p1, p3); - - const auto r0 = InterleaveLower(d, q0, q1); - const auto r1 = InterleaveUpper(d, q0, q1); - const auto r2 = InterleaveLower(d, q2, q3); - const auto r3 = InterleaveUpper(d, q2, q3); - Store(r0, d, to + m * 8 + n); - Store(r1, d, to + (1 + m) * 8 + n); - Store(r2, d, to + (2 + m) * 8 + n); - Store(r3, d, to + (3 + m) * 8 + n); - } - } -} -#else -static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, - float* JXL_RESTRICT to) { - for (size_t n = 0; n < 8; ++n) { - for (size_t m = 0; m < 8; ++m) { - to[8 * n + m] = from[8 * m + n]; - } - } -} -#endif - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); -#endif // LIB_JPEGLI_TRANSPOSE_INL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/types.h b/third_party/jpeg-xl/lib/jpegli/types.h deleted file mode 100644 index c0c0450c1865d..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/types.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_TYPES_H_ -#define LIB_JPEGLI_TYPES_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -// -// New API structs and functions that are not available in libjpeg -// -// NOTE: This part of the API is still experimental and will probably change in -// the future. -// - -typedef enum { - JPEGLI_TYPE_FLOAT = 0, - JPEGLI_TYPE_UINT8 = 2, - JPEGLI_TYPE_UINT16 = 3, -} JpegliDataType; - -typedef enum { - JPEGLI_NATIVE_ENDIAN = 0, - JPEGLI_LITTLE_ENDIAN = 1, - JPEGLI_BIG_ENDIAN = 2, -} JpegliEndianness; - -int jpegli_bytes_per_sample(JpegliDataType data_type); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // LIB_JPEGLI_TYPES_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/upsample.cc b/third_party/jpeg-xl/lib/jpegli/upsample.cc deleted file mode 100644 index 7dae841b8a359..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/upsample.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jpegli/upsample.h" - -#include - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jpegli/upsample.cc" -#include -#include - -HWY_BEFORE_NAMESPACE(); -namespace jpegli { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::Vec; - -#if HWY_CAP_GE512 -using hwy::HWY_NAMESPACE::Half; -using hwy::HWY_NAMESPACE::Vec; -template -HWY_INLINE Vec>> Quarter(const DF df, V v) { - using HF = Half; - using HHF = Half; - auto half = i >= 2 ? UpperHalf(HF(), v) : LowerHalf(HF(), v); - return i & 1 ? UpperHalf(HHF(), half) : LowerHalf(HHF(), half); -} - -template -HWY_INLINE Vec Concat4(const DF df, V v0, V v1, V v2, V v3) { - using HF = Half; - return Combine(DF(), Combine(HF(), v3, v2), Combine(HF(), v1, v0)); -} - -#endif - -// Stores v0[0], v1[0], v0[1], v1[1], ... to mem, in this order. Mem must be -// aligned. -template -void StoreInterleaved(const DF df, V v0, V v1, T* mem) { - static_assert(sizeof(T) == 4, "only use StoreInterleaved for 4-byte types"); -#if HWY_TARGET == HWY_SCALAR - Store(v0, df, mem); - Store(v1, df, mem + 1); -#elif !HWY_CAP_GE256 - Store(InterleaveLower(df, v0, v1), df, mem); - Store(InterleaveUpper(df, v0, v1), df, mem + Lanes(df)); -#else - if (!HWY_CAP_GE512 || Lanes(df) == 8) { - auto t0 = InterleaveLower(df, v0, v1); - auto t1 = InterleaveUpper(df, v0, v1); - Store(ConcatLowerLower(df, t1, t0), df, mem); - Store(ConcatUpperUpper(df, t1, t0), df, mem + Lanes(df)); - } else { -#if HWY_CAP_GE512 - auto t0 = InterleaveLower(df, v0, v1); - auto t1 = InterleaveUpper(df, v0, v1); - Store(Concat4(df, Quarter<0>(df, t0), Quarter<0>(df, t1), - Quarter<1>(df, t0), Quarter<1>(df, t1)), - df, mem); - Store(Concat4(df, Quarter<2>(df, t0), Quarter<2>(df, t1), - Quarter<3>(df, t0), Quarter<3>(df, t1)), - df, mem + Lanes(df)); -#endif - } -#endif -} - -void Upsample2Horizontal(float* JXL_RESTRICT row, - float* JXL_RESTRICT scratch_space, size_t len_out) { - HWY_FULL(float) df; - auto threefour = Set(df, 0.75f); - auto onefour = Set(df, 0.25f); - const size_t len_in = (len_out + 1) >> 1; - memcpy(scratch_space, row, len_in * sizeof(row[0])); - scratch_space[-1] = scratch_space[0]; - scratch_space[len_in] = scratch_space[len_in - 1]; - for (size_t x = 0; x < len_in; x += Lanes(df)) { - auto current = Mul(Load(df, scratch_space + x), threefour); - auto prev = LoadU(df, scratch_space + x - 1); - auto next = LoadU(df, scratch_space + x + 1); - auto left = MulAdd(onefour, prev, current); - auto right = MulAdd(onefour, next, current); - StoreInterleaved(df, left, right, row + x * 2); - } -} - -void Upsample2Vertical(const float* JXL_RESTRICT row_top, - const float* JXL_RESTRICT row_mid, - const float* JXL_RESTRICT row_bot, - float* JXL_RESTRICT row_out0, - float* JXL_RESTRICT row_out1, size_t len) { - HWY_FULL(float) df; - auto threefour = Set(df, 0.75f); - auto onefour = Set(df, 0.25f); - for (size_t x = 0; x < len; x += Lanes(df)) { - auto it = Load(df, row_top + x); - auto im = Load(df, row_mid + x); - auto ib = Load(df, row_bot + x); - auto im_scaled = Mul(im, threefour); - Store(MulAdd(it, onefour, im_scaled), df, row_out0 + x); - Store(MulAdd(ib, onefour, im_scaled), df, row_out1 + x); - } -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jpegli -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jpegli { - -HWY_EXPORT(Upsample2Horizontal); -HWY_EXPORT(Upsample2Vertical); - -void Upsample2Horizontal(float* JXL_RESTRICT row, - float* JXL_RESTRICT scratch_space, size_t len_out) { - HWY_DYNAMIC_DISPATCH(Upsample2Horizontal)(row, scratch_space, len_out); -} - -void Upsample2Vertical(const float* JXL_RESTRICT row_top, - const float* JXL_RESTRICT row_mid, - const float* JXL_RESTRICT row_bot, - float* JXL_RESTRICT row_out0, - float* JXL_RESTRICT row_out1, size_t len) { - HWY_DYNAMIC_DISPATCH(Upsample2Vertical) - (row_top, row_mid, row_bot, row_out0, row_out1, len); -} -} // namespace jpegli -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jpegli/upsample.h b/third_party/jpeg-xl/lib/jpegli/upsample.h deleted file mode 100644 index 1a057208dcc25..0000000000000 --- a/third_party/jpeg-xl/lib/jpegli/upsample.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JPEGLI_UPSAMPLE_H_ -#define LIB_JPEGLI_UPSAMPLE_H_ - -#include - -#include "lib/jxl/base/compiler_specific.h" - -namespace jpegli { - -void Upsample2Horizontal(float* JXL_RESTRICT row, - float* JXL_RESTRICT scratch_space, size_t len_out); - -void Upsample2Vertical(const float* JXL_RESTRICT row_top, - const float* JXL_RESTRICT row_mid, - const float* JXL_RESTRICT row_bot, - float* JXL_RESTRICT row_out0, - float* JXL_RESTRICT row_out1, size_t len); - -} // namespace jpegli - -#endif // LIB_JPEGLI_UPSAMPLE_H_ diff --git a/third_party/jpeg-xl/lib/jxl.cmake b/third_party/jpeg-xl/lib/jxl.cmake deleted file mode 100644 index 59b2cca4265ab..0000000000000 --- a/third_party/jpeg-xl/lib/jxl.cmake +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright (c) the JPEG XL Project Authors. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include(jxl_lists.cmake) - -if (JPEGXL_ENABLE_TOOLS OR JPEGXL_ENABLE_DEVTOOLS OR JPEGXL_ENABLE_BOXES) -list(APPEND JPEGXL_INTERNAL_DEC_SOURCES ${JPEGXL_INTERNAL_DEC_BOX_SOURCES}) -endif() - -if (JPEGXL_ENABLE_TRANSCODE_JPEG OR JPEGXL_ENABLE_TOOLS OR JPEGXL_ENABLE_DEVTOOLS) -list(APPEND JPEGXL_INTERNAL_DEC_SOURCES ${JPEGXL_INTERNAL_DEC_JPEG_SOURCES}) -endif() - -set(FJXL_COMPILE_FLAGS "-O3") - -set_source_files_properties(jxl/enc_fast_lossless.cc PROPERTIES COMPILE_FLAGS "${FJXL_COMPILE_FLAGS}") - -set(JPEGXL_DEC_INTERNAL_LIBS - hwy - Threads::Threads - ${ATOMICS_LIBRARIES} -) - -if (JPEGXL_ENABLE_TRANSCODE_JPEG OR JPEGXL_ENABLE_BOXES) -list(APPEND JPEGXL_DEC_INTERNAL_LIBS brotlidec brotlicommon) -endif() - -set(JPEGXL_INTERNAL_LIBS - ${JPEGXL_DEC_INTERNAL_LIBS} - brotlienc -) - -if (JPEGXL_ENABLE_TRANSCODE_JPEG) - list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_TRANSCODE_JPEG=1) -else() - list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_TRANSCODE_JPEG=0) -endif () - -if (JPEGXL_ENABLE_BOXES) - list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_BOXES=1) -else() - list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_BOXES=0) -endif () - -set(OBJ_COMPILE_DEFINITIONS - # Used to determine if we are building the library when defined or just - # including the library when not defined. This is public so libjxl shared - # library gets this define too. - JXL_INTERNAL_LIBRARY_BUILD -) - -# Generate version.h -configure_file("jxl/version.h.in" "include/jxl/version.h") - -list(APPEND JPEGXL_INTERNAL_PUBLIC_HEADERS - ${CMAKE_CURRENT_BINARY_DIR}/include/jxl/version.h) - -# Headers for exporting/importing public headers -include(GenerateExportHeader) - -# CMake does not allow generate_export_header for INTERFACE library, so we -# add this stub library just for file generation. -add_library(jxl_export OBJECT ${JPEGXL_INTERNAL_PUBLIC_HEADERS} nothing.cc) -set_target_properties(jxl_export PROPERTIES - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN 1 - DEFINE_SYMBOL JXL_INTERNAL_LIBRARY_BUILD - LINKER_LANGUAGE CXX -) -generate_export_header(jxl_export - BASE_NAME JXL - EXPORT_FILE_NAME include/jxl/jxl_export.h) -# Place all public headers in a single directory. -foreach(path ${JPEGXL_INTERNAL_PUBLIC_HEADERS}) - configure_file( - ${path} - ${path} - COPYONLY - ) -endforeach() - -add_library(jxl_base INTERFACE) -target_include_directories(jxl_base SYSTEM BEFORE INTERFACE - "$" -) -target_include_directories(jxl_base BEFORE INTERFACE - ${PROJECT_SOURCE_DIR} - ${JXL_HWY_INCLUDE_DIRS} -) - -# On android, link with log to use android-related log functions. -if(CMAKE_SYSTEM_NAME STREQUAL "Android") - find_library(log-lib log) - if(log-lib) - target_link_libraries(jxl_base INTERFACE ${log-lib}) - target_compile_definitions(jxl_base INTERFACE USE_ANDROID_LOGGER) - endif() -endif() - -add_dependencies(jxl_base jxl_export) - -# Decoder-only object library -add_library(jxl_dec-obj OBJECT ${JPEGXL_INTERNAL_DEC_SOURCES}) -target_compile_options(jxl_dec-obj PRIVATE ${JPEGXL_INTERNAL_FLAGS}) -target_compile_options(jxl_dec-obj PUBLIC ${JPEGXL_COVERAGE_FLAGS}) -set_property(TARGET jxl_dec-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_include_directories(jxl_dec-obj BEFORE PUBLIC - "$" - "${JXL_HWY_INCLUDE_DIRS}" - "$>" -) -target_compile_definitions(jxl_dec-obj PUBLIC - ${OBJ_COMPILE_DEFINITIONS} -) -target_link_libraries(jxl_dec-obj PUBLIC jxl_base) - -# Object library. This is used to hold the set of objects and properties. -add_library(jxl_enc-obj OBJECT ${JPEGXL_INTERNAL_ENC_SOURCES}) -target_compile_options(jxl_enc-obj PRIVATE ${JPEGXL_INTERNAL_FLAGS}) -target_compile_options(jxl_enc-obj PUBLIC ${JPEGXL_COVERAGE_FLAGS}) -set_property(TARGET jxl_enc-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_include_directories(jxl_enc-obj BEFORE PUBLIC - ${PROJECT_SOURCE_DIR} - ${JXL_HWY_INCLUDE_DIRS} - $ -) -target_compile_definitions(jxl_enc-obj PUBLIC - ${OBJ_COMPILE_DEFINITIONS} -) -target_link_libraries(jxl_enc-obj PUBLIC jxl_base) - -set_target_properties(jxl_dec-obj PROPERTIES - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN 1 - DEFINE_SYMBOL JXL_INTERNAL_LIBRARY_BUILD -) - -set_target_properties(jxl_enc-obj PROPERTIES - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN 1 - DEFINE_SYMBOL JXL_INTERNAL_LIBRARY_BUILD -) - -# Private static library. This exposes all the internal functions and is used -# for tests. -add_library(jxl_dec-internal STATIC - $ - ${JXL_CMS_OBJECTS} -) -target_link_libraries(jxl_dec-internal PUBLIC - ${JPEGXL_COVERAGE_FLAGS} - ${JPEGXL_DEC_INTERNAL_LIBS} - jxl_base -) - -# The list of objects in the static and shared libraries. -set(JPEGXL_INTERNAL_OBJECTS - $ - $ -) - -# Private static library. This exposes all the internal functions and is used -# for tests. -# TODO(lode): once the source files are correctly split so that it is possible -# to do, remove $ here and depend on jxl_dec-internal -add_library(jxl-internal STATIC - ${JPEGXL_INTERNAL_OBJECTS} -) -target_link_libraries(jxl-internal PUBLIC - ${JPEGXL_COVERAGE_FLAGS} - ${JPEGXL_INTERNAL_LIBS} - jxl_cms - jxl_base -) -target_include_directories(jxl-internal BEFORE PUBLIC - "$") - -target_compile_definitions(jxl-internal INTERFACE -DJXL_STATIC_DEFINE) -target_compile_definitions(jxl_dec-internal INTERFACE -DJXL_STATIC_DEFINE) -target_compile_definitions(jxl-internal INTERFACE -DJXL_STATIC_DEFINE) -target_compile_definitions(jxl_dec-internal INTERFACE -DJXL_STATIC_DEFINE) - -# TODO(deymo): Move TCMalloc linkage to the tools/ directory since the library -# shouldn't do any allocs anyway. -if(JPEGXL_ENABLE_TCMALLOC) - pkg_check_modules(TCMallocMinimal REQUIRED IMPORTED_TARGET - libtcmalloc_minimal) - # tcmalloc 2.8 has concurrency issues that makes it sometimes return nullptr - # for large allocs. See https://github.com/gperftools/gperftools/issues/1204 - # for details. - if(TCMallocMinimal_VERSION VERSION_EQUAL 2.8) - message(FATAL_ERROR - "tcmalloc version 2.8 has a concurrency bug. You have installed " - "version ${TCMallocMinimal_VERSION}, please either downgrade tcmalloc " - "to version 2.7, upgrade to 2.8.1 or newer or pass " - "-DJPEGXL_ENABLE_TCMALLOC=OFF to jpeg-xl cmake line. See the following " - "bug for details:\n" - " https://github.com/gperftools/gperftools/issues/1204\n") - endif() - target_link_libraries(jxl-internal PUBLIC PkgConfig::TCMallocMinimal) -endif() # JPEGXL_ENABLE_TCMALLOC - -# Public library. -add_library(jxl ${JPEGXL_INTERNAL_OBJECTS}) -strip_internal(JPEGXL_INTERNAL_SHARED_LIBS JPEGXL_INTERNAL_LIBS) -target_link_libraries(jxl PUBLIC ${JPEGXL_COVERAGE_FLAGS} jxl_base) -target_link_libraries(jxl PUBLIC jxl_cms) -target_link_libraries(jxl PRIVATE ${JPEGXL_INTERNAL_SHARED_LIBS}) -set_target_properties(jxl PROPERTIES - VERSION ${JPEGXL_LIBRARY_VERSION} - SOVERSION ${JPEGXL_LIBRARY_SOVERSION}) - -# Public decoder library. -add_library(jxl_dec $) -strip_internal(JPEGXL_DEC_INTERNAL_SHARED_LIBS JPEGXL_DEC_INTERNAL_LIBS) -target_link_libraries(jxl_dec PUBLIC ${JPEGXL_COVERAGE_FLAGS} jxl_base) -target_link_libraries(jxl_dec PRIVATE ${JPEGXL_DEC_INTERNAL_SHARED_LIBS}) -set_target_properties(jxl_dec PROPERTIES - VERSION ${JPEGXL_LIBRARY_VERSION} - SOVERSION ${JPEGXL_LIBRARY_SOVERSION}) - -# Check whether the linker support excluding libs -set(LINKER_EXCLUDE_LIBS_FLAG "-Wl,--exclude-libs=ALL") -include(CheckCSourceCompiles) -list(APPEND CMAKE_EXE_LINKER_FLAGS ${LINKER_EXCLUDE_LIBS_FLAG}) -check_c_source_compiles("int main(){return 0;}" LINKER_SUPPORT_EXCLUDE_LIBS) -list(REMOVE_ITEM CMAKE_EXE_LINKER_FLAGS ${LINKER_EXCLUDE_LIBS_FLAG}) - -if(NOT BUILD_SHARED_LIBS) - target_compile_definitions(jxl PUBLIC -DJXL_STATIC_DEFINE) - target_compile_definitions(jxl_dec PUBLIC -DJXL_STATIC_DEFINE) -endif() - -# Add a jxl.version file as a version script to tag symbols with the -# appropriate version number. This script is also used to limit what's exposed -# in the shared library from the static dependencies bundled here. -foreach(target IN ITEMS jxl jxl_dec) - set_target_properties(${target} PROPERTIES - LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/jxl/jxl.version) - if(APPLE) - set_property(TARGET ${target} APPEND_STRING PROPERTY - LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/jxl/jxl_osx.syms") - elseif(WIN32) - # Nothing needed here, we use __declspec(dllexport) (jxl_export.h) - else() - set_property(TARGET ${target} APPEND_STRING PROPERTY - LINK_FLAGS " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/jxl/jxl.version") - endif() # APPLE - # This hides the default visibility symbols from static libraries bundled into - # the shared library. In particular this prevents exposing symbols from hwy - # and skcms in the shared library. - if(LINKER_SUPPORT_EXCLUDE_LIBS) - set_property(TARGET ${target} APPEND_STRING PROPERTY - LINK_FLAGS " ${LINKER_EXCLUDE_LIBS_FLAG}") - endif() -endforeach() - -# Only install libjxl public library. The libjxl_dec is not installed since it -# contains symbols also in libjxl which would conflict if programs try to use -# both. -install(TARGETS jxl - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -# Add a pkg-config file for libjxl. -set(JPEGXL_LIBRARY_REQUIRES - "libhwy libbrotlienc libbrotlidec libjxl_cms") - -if (BUILD_SHARED_LIBS) - set(JPEGXL_REQUIRES_TYPE "Requires.private") - set(JPEGXL_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}") -else() - set(JPEGXL_REQUIRES_TYPE "Requires") - set(JPEGXL_PUBLIC_LIBS "-lm ${PKGCONFIG_CXX_LIB}") -endif() - -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl.pc.in" - "libjxl.pc" @ONLY) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libjxl.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/third_party/jpeg-xl/lib/jxl/ac_context.h b/third_party/jpeg-xl/lib/jxl/ac_context.h deleted file mode 100644 index 6529a9bb88bcb..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ac_context.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_AC_CONTEXT_H_ -#define LIB_JXL_AC_CONTEXT_H_ - -#include -#include - -#include "lib/jxl/base/bits.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/coeff_order_fwd.h" - -namespace jxl { - -// Block context used for scanning order, number of non-zeros, AC coefficients. -// Equal to the channel. -constexpr uint32_t kDCTOrderContextStart = 0; - -// The number of predicted nonzeros goes from 0 to 1008. We use -// ceil(log2(predicted+1)) as a context for the number of nonzeros, so from 0 to -// 10, inclusive. -constexpr uint32_t kNonZeroBuckets = 37; - -static const uint16_t kCoeffFreqContext[64] = { - 0xBAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, - 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, -}; - -static const uint16_t kCoeffNumNonzeroContext[64] = { - 0xBAD, 0, 31, 62, 62, 93, 93, 93, 93, 123, 123, 123, 123, - 152, 152, 152, 152, 152, 152, 152, 152, 180, 180, 180, 180, 180, - 180, 180, 180, 180, 180, 180, 180, 206, 206, 206, 206, 206, 206, - 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, - 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, -}; - -// Supremum of ZeroDensityContext(x, y) + 1, when x + y < 64. -constexpr int kZeroDensityContextCount = 458; -// Supremum of ZeroDensityContext(x, y) + 1. -constexpr int kZeroDensityContextLimit = 474; - -/* This function is used for entropy-sources pre-clustering. - * - * Ideally, each combination of |nonzeros_left| and |k| should go to its own - * bucket; but it implies (64 * 63 / 2) == 2016 buckets. If there is other - * dimension (e.g. block context), then number of primary clusters becomes too - * big. - * - * To solve this problem, |nonzeros_left| and |k| values are clustered. It is - * known that their sum is at most 64, consequently, the total number buckets - * is at most A(64) * B(64). - */ -// TODO(user): investigate, why disabling pre-clustering makes entropy code -// less dense. Perhaps we would need to add HQ clustering algorithm that would -// be able to squeeze better by spending more CPU cycles. -static JXL_INLINE size_t ZeroDensityContext(size_t nonzeros_left, size_t k, - size_t covered_blocks, - size_t log2_covered_blocks, - size_t prev) { - JXL_DASSERT((static_cast(1) << log2_covered_blocks) == - covered_blocks); - nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks; - k >>= log2_covered_blocks; - JXL_DASSERT(k > 0); - JXL_DASSERT(k < 64); - JXL_DASSERT(nonzeros_left > 0); - // Asserting nonzeros_left + k < 65 here causes crashes in debug mode with - // invalid input, since the (hot) decoding loop does not check this condition. - // As no out-of-bound memory reads are issued even if that condition is - // broken, we check this simpler condition which holds anyway. The decoder - // will still mark a file in which that condition happens as not valid at the - // end of the decoding loop, as `nzeros` will not be `0`. - JXL_DASSERT(nonzeros_left < 64); - return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 + - prev; -} - -struct BlockCtxMap { - std::vector dc_thresholds[3]; - std::vector qf_thresholds; - std::vector ctx_map; - size_t num_ctxs, num_dc_ctxs; - - static constexpr uint8_t kDefaultCtxMap[] = { - // Default ctx map clusters all the large transforms together. - 0, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 6, 6, // - 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14, // - 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14, // - }; - static_assert(3 * kNumOrders == - sizeof(kDefaultCtxMap) / sizeof *kDefaultCtxMap, - "Update default context map"); - - size_t Context(int dc_idx, uint32_t qf, size_t ord, size_t c) const { - size_t qf_idx = 0; - for (uint32_t t : qf_thresholds) { - if (qf > t) qf_idx++; - } - size_t idx = c < 2 ? c ^ 1 : 2; - idx = idx * kNumOrders + ord; - idx = idx * (qf_thresholds.size() + 1) + qf_idx; - idx = idx * num_dc_ctxs + dc_idx; - return ctx_map[idx]; - } - // Non-zero context is based on number of non-zeros and block context. - // For better clustering, contexts with same number of non-zeros are grouped. - constexpr uint32_t ZeroDensityContextsOffset(uint32_t block_ctx) const { - return static_cast(num_ctxs * kNonZeroBuckets + - kZeroDensityContextCount * block_ctx); - } - - // Context map for AC coefficients consists of 2 blocks: - // |num_ctxs x : context for number of non-zeros in the block - // kNonZeroBuckets| computed from block context and predicted - // value (based top and left values) - // |num_ctxs x : context for AC coefficient symbols, - // kZeroDensityContextCount| computed from block context, - // number of non-zeros left and - // index in scan order - constexpr uint32_t NumACContexts() const { - return static_cast(num_ctxs * - (kNonZeroBuckets + kZeroDensityContextCount)); - } - - // Non-zero context is based on number of non-zeros and block context. - // For better clustering, contexts with same number of non-zeros are grouped. - inline uint32_t NonZeroContext(uint32_t non_zeros, uint32_t block_ctx) const { - uint32_t ctx; - if (non_zeros >= 64) non_zeros = 64; - if (non_zeros < 8) { - ctx = non_zeros; - } else { - ctx = 4 + non_zeros / 2; - } - return static_cast(ctx * num_ctxs + block_ctx); - } - - BlockCtxMap() { - ctx_map.assign(std::begin(kDefaultCtxMap), std::end(kDefaultCtxMap)); - num_ctxs = *std::max_element(ctx_map.begin(), ctx_map.end()) + 1; - num_dc_ctxs = 1; - } -}; - -} // namespace jxl - -#endif // LIB_JXL_AC_CONTEXT_H_ diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc deleted file mode 100644 index 69e8ae6f470c1..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/ac_strategy.h" - -#include - -#include -#include -#include - -#include "lib/jxl/base/bits.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jxl { - -// Tries to generalize zig-zag order to non-square blocks. Surprisingly, in -// square block frequency along the (i + j == const) diagonals is roughly the -// same. For historical reasons, consecutive diagonals are traversed -// in alternating directions - so called "zig-zag" (or "snake") order. -template -static void CoeffOrderAndLut(AcStrategy acs, coeff_order_t* out) { - size_t cx = acs.covered_blocks_x(); - size_t cy = acs.covered_blocks_y(); - CoefficientLayout(&cy, &cx); - - // CoefficientLayout ensures cx >= cy. - // We compute the zigzag order for a cx x cx block, then discard all the - // lines that are not multiple of the ratio between cx and cy. - size_t xs = cx / cy; - size_t xsm = xs - 1; - size_t xss = CeilLog2Nonzero(xs); - // First half of the block - size_t cur = cx * cy; - for (size_t i = 0; i < cx * kBlockDim; i++) { - for (size_t j = 0; j <= i; j++) { - size_t x = j; - size_t y = i - j; - if (i % 2) std::swap(x, y); - if ((y & xsm) != 0) continue; - y >>= xss; - size_t val = 0; - if (x < cx && y < cy) { - val = y * cx + x; - } else { - val = cur++; - } - if (is_lut) { - out[y * cx * kBlockDim + x] = val; - } else { - out[val] = y * cx * kBlockDim + x; - } - } - } - // Second half - for (size_t ip = cx * kBlockDim - 1; ip > 0; ip--) { - size_t i = ip - 1; - for (size_t j = 0; j <= i; j++) { - size_t x = cx * kBlockDim - 1 - (i - j); - size_t y = cx * kBlockDim - 1 - j; - if (i % 2) std::swap(x, y); - if ((y & xsm) != 0) continue; - y >>= xss; - size_t val = cur++; - if (is_lut) { - out[y * cx * kBlockDim + x] = val; - } else { - out[val] = y * cx * kBlockDim + x; - } - } - } -} - -void AcStrategy::ComputeNaturalCoeffOrder(coeff_order_t* order) const { - CoeffOrderAndLut(*this, order); -} -void AcStrategy::ComputeNaturalCoeffOrderLut(coeff_order_t* lut) const { - CoeffOrderAndLut(*this, lut); -} - -#if JXL_CXX_LANG < JXL_CXX_17 -constexpr size_t AcStrategy::kMaxCoeffBlocks; -constexpr size_t AcStrategy::kMaxBlockDim; -constexpr size_t AcStrategy::kMaxCoeffArea; -#endif - -StatusOr AcStrategyImage::Create( - JxlMemoryManager* memory_manager, size_t xsize, size_t ysize) { - AcStrategyImage img; - JXL_ASSIGN_OR_RETURN(img.layers_, - ImageB::Create(memory_manager, xsize, ysize)); - img.row_ = img.layers_.Row(0); - img.stride_ = img.layers_.PixelsPerRow(); - return img; -} - -size_t AcStrategyImage::CountBlocks(AcStrategyType type) const { - size_t ret = 0; - for (size_t y = 0; y < layers_.ysize(); y++) { - const uint8_t* JXL_RESTRICT row = layers_.ConstRow(y); - for (size_t x = 0; x < layers_.xsize(); x++) { - if (row[x] == ((static_cast(type) << 1) | 1)) ret++; - } - } - return ret; -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.h b/third_party/jpeg-xl/lib/jxl/ac_strategy.h deleted file mode 100644 index 8c3191c791a90..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_AC_STRATEGY_H_ -#define LIB_JXL_AC_STRATEGY_H_ - -#include - -#include -#include -#include // kMaxVectorSize - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/frame_dimensions.h" -#include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" - -// Defines the different kinds of transforms, and heuristics to choose between -// them. -// `AcStrategy` represents what transform should be used, and which sub-block of -// that transform we are currently in. Note that DCT4x4 is applied on all four -// 4x4 sub-blocks of an 8x8 block. -// `AcStrategyImage` defines which strategy should be used for each 8x8 block -// of the image. The highest 4 bits represent the strategy to be used, the -// lowest 4 represent the index of the block inside that strategy. - -namespace jxl { - -// Raw strategy types. -enum class AcStrategyType : uint32_t { - // Regular block size DCT - DCT = 0, - // Encode pixels without transforming - IDENTITY = 1, - // Use 2-by-2 DCT - DCT2X2 = 2, - // Use 4-by-4 DCT - DCT4X4 = 3, - // Use 16-by-16 DCT - DCT16X16 = 4, - // Use 32-by-32 DCT - DCT32X32 = 5, - // Use 16-by-8 DCT - DCT16X8 = 6, - // Use 8-by-16 DCT - DCT8X16 = 7, - // Use 32-by-8 DCT - DCT32X8 = 8, - // Use 8-by-32 DCT - DCT8X32 = 9, - // Use 32-by-16 DCT - DCT32X16 = 10, - // Use 16-by-32 DCT - DCT16X32 = 11, - // 4x8 and 8x4 DCT - DCT4X8 = 12, - DCT8X4 = 13, - // Corner-DCT. - AFV0 = 14, - AFV1 = 15, - AFV2 = 16, - AFV3 = 17, - // Larger DCTs - DCT64X64 = 18, - DCT64X32 = 19, - DCT32X64 = 20, - // No transforms smaller than 64x64 are allowed below. - DCT128X128 = 21, - DCT128X64 = 22, - DCT64X128 = 23, - DCT256X256 = 24, - DCT256X128 = 25, - DCT128X256 = 26 -}; - -class AcStrategy { - public: - // Extremal values for the number of blocks/coefficients of a single strategy. - static constexpr size_t kMaxCoeffBlocks = 32; - static constexpr size_t kMaxBlockDim = kBlockDim * kMaxCoeffBlocks; - // Maximum number of coefficients in a block. Guaranteed to be a multiple of - // the vector size. - static constexpr size_t kMaxCoeffArea = kMaxBlockDim * kMaxBlockDim; - static_assert((kMaxCoeffArea * sizeof(float)) % hwy::kMaxVectorSize == 0, - "Coefficient area is not a multiple of vector size"); - - static constexpr uint8_t kNumValidStrategies = - static_cast(AcStrategyType::DCT128X256) + 1; - - static constexpr uint32_t TypeBit(const AcStrategyType type) { - return 1u << static_cast(type); - } - - // Returns true if this block is the first 8x8 block (i.e. top-left) of a - // possibly multi-block strategy. - JXL_INLINE bool IsFirstBlock() const { return is_first_; } - - JXL_INLINE bool IsMultiblock() const { - constexpr uint32_t bits = - TypeBit(AcStrategyType::DCT16X16) | TypeBit(AcStrategyType::DCT32X32) | - TypeBit(AcStrategyType::DCT16X8) | TypeBit(AcStrategyType::DCT8X16) | - TypeBit(AcStrategyType::DCT32X8) | TypeBit(AcStrategyType::DCT8X32) | - TypeBit(AcStrategyType::DCT16X32) | TypeBit(AcStrategyType::DCT32X16) | - TypeBit(AcStrategyType::DCT32X64) | TypeBit(AcStrategyType::DCT64X32) | - TypeBit(AcStrategyType::DCT64X64) | TypeBit(AcStrategyType::DCT64X128) | - TypeBit(AcStrategyType::DCT128X64) | - TypeBit(AcStrategyType::DCT128X128) | - TypeBit(AcStrategyType::DCT128X256) | - TypeBit(AcStrategyType::DCT256X128) | - TypeBit(AcStrategyType::DCT256X256); - return ((1u << static_cast(Strategy())) & bits) != 0; - } - - // Returns the raw strategy value. Should only be used for tokenization. - JXL_INLINE uint8_t RawStrategy() const { - return static_cast(strategy_); - } - - JXL_INLINE AcStrategyType Strategy() const { return strategy_; } - - // Inverse check - static JXL_INLINE constexpr bool IsRawStrategyValid(int raw_strategy) { - return raw_strategy < kNumValidStrategies && raw_strategy >= 0; - } - static JXL_INLINE AcStrategy FromRawStrategy(uint8_t raw_strategy) { - return FromRawStrategy(static_cast(raw_strategy)); - } - static JXL_INLINE AcStrategy FromRawStrategy(AcStrategyType raw_strategy) { - JXL_DASSERT(IsRawStrategyValid(static_cast(raw_strategy))); - return AcStrategy(raw_strategy, /*is_first=*/true); - } - - // "Natural order" means the order of increasing of "anisotropic" frequency of - // continuous version of DCT basis. - // Round-trip, for any given strategy s: - // X = NaturalCoeffOrder(s)[NaturalCoeffOrderLutN(s)[X]] - // X = NaturalCoeffOrderLut(s)[NaturalCoeffOrderN(s)[X]] - void ComputeNaturalCoeffOrder(coeff_order_t* order) const; - void ComputeNaturalCoeffOrderLut(coeff_order_t* lut) const; - - // Number of 8x8 blocks that this strategy will cover. 0 for non-top-left - // blocks inside a multi-block transform. - JXL_INLINE size_t covered_blocks_x() const { - static constexpr uint8_t kLut[] = {1, 1, 1, 1, 2, 4, 1, 2, 1, - 4, 2, 4, 1, 1, 1, 1, 1, 1, - 8, 4, 8, 16, 8, 16, 32, 16, 32}; - static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, - "Update LUT"); - return kLut[static_cast(strategy_)]; - } - - JXL_INLINE size_t covered_blocks_y() const { - static constexpr uint8_t kLut[] = {1, 1, 1, 1, 2, 4, 2, 1, 4, - 1, 4, 2, 1, 1, 1, 1, 1, 1, - 8, 8, 4, 16, 16, 8, 32, 32, 16}; - static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, - "Update LUT"); - return kLut[static_cast(strategy_)]; - } - - JXL_INLINE size_t log2_covered_blocks() const { - static constexpr uint8_t kLut[] = {0, 0, 0, 0, 2, 4, 1, 1, 2, - 2, 3, 3, 0, 0, 0, 0, 0, 0, - 6, 5, 5, 8, 7, 7, 10, 9, 9}; - static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, - "Update LUT"); - return kLut[static_cast(strategy_)]; - } - - private: - friend class AcStrategyRow; - JXL_INLINE AcStrategy(AcStrategyType strategy, bool is_first) - : strategy_(strategy), is_first_(is_first) { - JXL_DASSERT(IsMultiblock() || is_first == true); - } - - AcStrategyType strategy_; - bool is_first_; -}; - -// Class to use a certain row of the AC strategy. -class AcStrategyRow { - public: - explicit AcStrategyRow(const uint8_t* row) : row_(row) {} - AcStrategy operator[](size_t x) const { - AcStrategyType strategy = static_cast(row_[x] >> 1); - bool is_first = static_cast(row_[x] & 1); - return AcStrategy(strategy, is_first); - } - - private: - const uint8_t* JXL_RESTRICT row_; -}; - -class AcStrategyImage { - public: - AcStrategyImage() = default; - static StatusOr Create(JxlMemoryManager* memory_manager, - size_t xsize, size_t ysize); - - AcStrategyImage(AcStrategyImage&&) = default; - AcStrategyImage& operator=(AcStrategyImage&&) = default; - - void FillDCT8(const Rect& rect) { - FillPlane((static_cast(AcStrategyType::DCT) << 1) | 1, - &layers_, rect); - } - void FillDCT8() { FillDCT8(Rect(layers_)); } - - void FillInvalid() { FillImage(INVALID, &layers_); } - - Status Set(size_t x, size_t y, AcStrategyType type) { -#if (JXL_IS_DEBUG_BUILD) - AcStrategy acs = AcStrategy::FromRawStrategy(type); - JXL_DASSERT(y + acs.covered_blocks_y() <= layers_.ysize()); - JXL_DASSERT(x + acs.covered_blocks_x() <= layers_.xsize()); -#endif - JXL_RETURN_IF_ERROR(SetNoBoundsCheck(x, y, type, /*check=*/false)); - return true; - } - - Status SetNoBoundsCheck(size_t x, size_t y, AcStrategyType type, - bool check = true) { - AcStrategy acs = AcStrategy::FromRawStrategy(type); - for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { - for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) { - size_t pos = (y + iy) * stride_ + x + ix; - if (check && row_[pos] != INVALID) { - return JXL_FAILURE("Invalid AC strategy: block overlap"); - } - row_[pos] = - (static_cast(type) << 1) | ((iy | ix) == 0 ? 1 : 0); - } - } - return true; - } - - bool IsValid(size_t x, size_t y) { return row_[y * stride_ + x] != INVALID; } - - AcStrategyRow ConstRow(size_t y, size_t x_prefix = 0) const { - return AcStrategyRow(layers_.ConstRow(y) + x_prefix); - } - - AcStrategyRow ConstRow(const Rect& rect, size_t y) const { - return ConstRow(rect.y0() + y, rect.x0()); - } - - size_t PixelsPerRow() const { return layers_.PixelsPerRow(); } - - size_t xsize() const { return layers_.xsize(); } - size_t ysize() const { return layers_.ysize(); } - - // Count the number of blocks of a given type. - size_t CountBlocks(AcStrategyType type) const; - - JxlMemoryManager* memory_manager() const { return layers_.memory_manager(); } - - private: - ImageB layers_; - uint8_t* JXL_RESTRICT row_; - size_t stride_; - - // A value that does not represent a valid combined AC strategy - // value. Used as a sentinel. - static constexpr uint8_t INVALID = 0xFF; -}; - -} // namespace jxl - -#endif // LIB_JXL_AC_STRATEGY_H_ diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc deleted file mode 100644 index e6405fc86b25a..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/ac_strategy.h" - -#include - -#include -#include -#include // HWY_ALIGN_MAX -#include - -#include "lib/jxl/base/random.h" -#include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/dec_transforms_testonly.h" -#include "lib/jxl/enc_transforms.h" -#include "lib/jxl/memory_manager_internal.h" -#include "lib/jxl/simd_util.h" -#include "lib/jxl/test_memory_manager.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace { - -// Test that DCT -> IDCT is a noop. -class AcStrategyRoundtrip : public ::hwy::TestWithParamTargetAndT { - protected: - void Run() { - JxlMemoryManager* memory_manager = test::MemoryManager(); - const AcStrategyType type = static_cast(GetParam()); - const AcStrategy acs = AcStrategy::FromRawStrategy(type); - const size_t dct_scratch_size = - 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; - - size_t mem_bytes = - (4 * AcStrategy::kMaxCoeffArea + dct_scratch_size) * sizeof(float); - JXL_TEST_ASSIGN_OR_DIE(AlignedMemory mem, - AlignedMemory::Create(memory_manager, mem_bytes)); - float* coeffs = mem.address(); - float* idct = coeffs + AcStrategy::kMaxCoeffArea; - float* input = idct + AcStrategy::kMaxCoeffArea; - float* scratch_space = input + AcStrategy::kMaxCoeffArea; - - Rng rng(static_cast(type) * 65537 + 13); - - for (size_t j = 0; j < 64; j++) { - size_t i = (acs.log2_covered_blocks() - ? rng.UniformU(0, 64u << acs.log2_covered_blocks()) - : j); - std::fill_n(input, AcStrategy::kMaxCoeffArea, 0); - input[i] = 0.2f; - TransformFromPixels(type, input, acs.covered_blocks_x() * 8, coeffs, - scratch_space); - ASSERT_NEAR(coeffs[0], 0.2 / (64 << acs.log2_covered_blocks()), 1e-6) - << " i = " << i; - TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8, - scratch_space); - for (size_t j = 0; j < 64u << acs.log2_covered_blocks(); j++) { - ASSERT_NEAR(idct[j], j == i ? 0.2f : 0, 2e-6) - << "j = " << j << " i = " << i << " acs " << static_cast(type); - } - } - // Test DC. - std::fill_n(idct, AcStrategy::kMaxCoeffArea, 0); - for (size_t y = 0; y < acs.covered_blocks_y(); y++) { - for (size_t x = 0; x < acs.covered_blocks_x(); x++) { - float* dc = idct + AcStrategy::kMaxCoeffArea; - std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0); - dc[y * acs.covered_blocks_x() * 8 + x] = 0.2; - LowestFrequenciesFromDC(type, dc, acs.covered_blocks_x() * 8, coeffs, - scratch_space); - DCFromLowestFrequencies(type, coeffs, idct, acs.covered_blocks_x() * 8); - std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0); - dc[y * acs.covered_blocks_x() * 8 + x] = 0.2; - for (size_t j = 0; j < 64u << acs.log2_covered_blocks(); j++) { - ASSERT_NEAR(idct[j], dc[j], 1e-6) - << "j = " << j << " x = " << x << " y = " << y << " acs " - << static_cast(type); - } - } - } - } -}; - -HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( - AcStrategyRoundtrip, - ::testing::Range(0, static_cast(AcStrategy::kNumValidStrategies))); - -TEST_P(AcStrategyRoundtrip, Test) { Run(); } - -// Test that DC(2x2) -> DCT coefficients -> IDCT -> downsampled IDCT is a noop. -class AcStrategyRoundtripDownsample - : public ::hwy::TestWithParamTargetAndT { - protected: - void Run() { - JxlMemoryManager* memory_manager = test::MemoryManager(); - const AcStrategyType type = static_cast(GetParam()); - const AcStrategy acs = AcStrategy::FromRawStrategy(type); - const size_t dct_scratch_size = - 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; - - size_t mem_bytes = - (4 * AcStrategy::kMaxCoeffArea + dct_scratch_size) * sizeof(float); - JXL_TEST_ASSIGN_OR_DIE(AlignedMemory mem, - AlignedMemory::Create(memory_manager, mem_bytes)); - float* coeffs = mem.address(); - float* idct = coeffs + AcStrategy::kMaxCoeffArea; - float* dc = idct + AcStrategy::kMaxCoeffArea; - float* scratch_space = dc + AcStrategy::kMaxCoeffArea; - - std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0.0f); - Rng rng(static_cast(type) * 65537 + 13); - - for (size_t y = 0; y < acs.covered_blocks_y(); y++) { - for (size_t x = 0; x < acs.covered_blocks_x(); x++) { - if (x > 4 || y > 4) { - if (rng.Bernoulli(0.9f)) continue; - } - std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0); - dc[y * acs.covered_blocks_x() * 8 + x] = 0.2f; - LowestFrequenciesFromDC(type, dc, acs.covered_blocks_x() * 8, coeffs, - scratch_space); - TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8, - scratch_space); - std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0.0f); - std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0); - dc[y * acs.covered_blocks_x() * 8 + x] = 0.2f; - // Downsample - for (size_t dy = 0; dy < acs.covered_blocks_y(); dy++) { - for (size_t dx = 0; dx < acs.covered_blocks_x(); dx++) { - float sum = 0; - for (size_t iy = 0; iy < 8; iy++) { - for (size_t ix = 0; ix < 8; ix++) { - sum += idct[(dy * 8 + iy) * 8 * acs.covered_blocks_x() + - dx * 8 + ix]; - } - } - sum /= 64.0f; - ASSERT_NEAR(sum, dc[dy * 8 * acs.covered_blocks_x() + dx], 1e-6) - << "acs " << static_cast(type); - } - } - } - } - } -}; - -HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( - AcStrategyRoundtripDownsample, - ::testing::Range(0, static_cast(AcStrategy::kNumValidStrategies))); - -TEST_P(AcStrategyRoundtripDownsample, Test) { Run(); } - -// Test that IDCT(block with zeros in the non-topleft corner) -> downsampled -// IDCT is the same as IDCT -> DC(2x2) of the same block. -class AcStrategyDownsample : public ::hwy::TestWithParamTargetAndT { - protected: - void Run() { - JxlMemoryManager* memory_manager = test::MemoryManager(); - const AcStrategyType type = static_cast(GetParam()); - const AcStrategy acs = AcStrategy::FromRawStrategy(type); - const size_t dct_scratch_size = - 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; - size_t cx = acs.covered_blocks_y(); - size_t cy = acs.covered_blocks_x(); - CoefficientLayout(&cy, &cx); - - size_t mem_bytes = - (4 * AcStrategy::kMaxCoeffArea + dct_scratch_size) * sizeof(float); - JXL_TEST_ASSIGN_OR_DIE(AlignedMemory mem, - AlignedMemory::Create(memory_manager, mem_bytes)); - float* idct = mem.address(); - float* idct_acs_downsampled = idct + AcStrategy::kMaxCoeffArea; - float* coeffs = idct + AcStrategy::kMaxCoeffArea; - float* scratch_space = coeffs + AcStrategy::kMaxCoeffArea; - - Rng rng(static_cast(type) * 65537 + 13); - - for (size_t y = 0; y < cy; y++) { - for (size_t x = 0; x < cx; x++) { - if (x > 4 || y > 4) { - if (rng.Bernoulli(0.9f)) continue; - } - float* coeffs = idct + AcStrategy::kMaxCoeffArea; - std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0); - coeffs[y * cx * 8 + x] = 0.2f; - TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8, - scratch_space); - std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0); - coeffs[y * cx * 8 + x] = 0.2f; - DCFromLowestFrequencies(type, coeffs, idct_acs_downsampled, - acs.covered_blocks_x() * 8); - // Downsample - for (size_t dy = 0; dy < acs.covered_blocks_y(); dy++) { - for (size_t dx = 0; dx < acs.covered_blocks_x(); dx++) { - float sum = 0; - for (size_t iy = 0; iy < 8; iy++) { - for (size_t ix = 0; ix < 8; ix++) { - sum += idct[(dy * 8 + iy) * 8 * acs.covered_blocks_x() + - dx * 8 + ix]; - } - } - sum /= 64; - ASSERT_NEAR( - sum, idct_acs_downsampled[dy * 8 * acs.covered_blocks_x() + dx], - 1e-6) - << " acs " << static_cast(type); - } - } - } - } - } -}; - -HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( - AcStrategyDownsample, - ::testing::Range(0, static_cast(AcStrategy::kNumValidStrategies))); - -TEST_P(AcStrategyDownsample, Test) { Run(); } - -class AcStrategyTargetTest : public ::hwy::TestWithParamTarget {}; -HWY_TARGET_INSTANTIATE_TEST_SUITE_P(AcStrategyTargetTest); - -TEST_P(AcStrategyTargetTest, RoundtripAFVDCT) { - HWY_ALIGN_MAX float idct[16]; - for (size_t i = 0; i < 16; i++) { - HWY_ALIGN_MAX float pixels[16] = {}; - pixels[i] = 1; - HWY_ALIGN_MAX float coeffs[16] = {}; - - AFVDCT4x4(pixels, coeffs); - AFVIDCT4x4(coeffs, idct); - for (size_t j = 0; j < 16; j++) { - EXPECT_NEAR(idct[j], pixels[j], 1e-6); - } - } -} - -TEST_P(AcStrategyTargetTest, BenchmarkAFV) { - JxlMemoryManager* memory_manager = test::MemoryManager(); - const AcStrategyType type = AcStrategyType::AFV0; - HWY_ALIGN_MAX float pixels[64] = {1}; - HWY_ALIGN_MAX float coeffs[64] = {}; - const size_t dct_scratch_size = - 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; - size_t mem_bytes = (64 + dct_scratch_size) * sizeof(float); - JXL_TEST_ASSIGN_OR_DIE(AlignedMemory mem, - AlignedMemory::Create(memory_manager, mem_bytes)); - float* scratch_space = mem.address(); - for (size_t i = 0; i < 1 << 14; i++) { - TransformToPixels(type, coeffs, pixels, 8, scratch_space); - TransformFromPixels(type, pixels, 8, coeffs, scratch_space); - } - EXPECT_NEAR(pixels[0], 0.0, 1E-6); -} - -TEST_P(AcStrategyTargetTest, BenchmarkAFVDCT) { - HWY_ALIGN_MAX float pixels[64] = {1}; - HWY_ALIGN_MAX float coeffs[64] = {}; - for (size_t i = 0; i < 1 << 14; i++) { - AFVDCT4x4(pixels, coeffs); - AFVIDCT4x4(coeffs, pixels); - } - EXPECT_NEAR(pixels[0], 1.0, 1E-6); -} - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/alpha.cc b/third_party/jpeg-xl/lib/jxl/alpha.cc deleted file mode 100644 index 48d7e7ee921f9..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/alpha.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/alpha.h" - -#include - -#include - -namespace jxl { - -static float Clamp(float x) { return std::max(std::min(1.0f, x), 0.0f); } - -void PerformAlphaBlending(const AlphaBlendingInputLayer& bg, - const AlphaBlendingInputLayer& fg, - const AlphaBlendingOutput& out, size_t num_pixels, - bool alpha_is_premultiplied, bool clamp) { - if (alpha_is_premultiplied) { - for (size_t x = 0; x < num_pixels; ++x) { - float fga = clamp ? Clamp(fg.a[x]) : fg.a[x]; - out.r[x] = (fg.r[x] + bg.r[x] * (1.f - fga)); - out.g[x] = (fg.g[x] + bg.g[x] * (1.f - fga)); - out.b[x] = (fg.b[x] + bg.b[x] * (1.f - fga)); - out.a[x] = (1.f - (1.f - fga) * (1.f - bg.a[x])); - } - } else { - for (size_t x = 0; x < num_pixels; ++x) { - float fga = clamp ? Clamp(fg.a[x]) : fg.a[x]; - const float new_a = 1.f - (1.f - fga) * (1.f - bg.a[x]); - const float rnew_a = (new_a > 0 ? 1.f / new_a : 0.f); - out.r[x] = (fg.r[x] * fga + bg.r[x] * bg.a[x] * (1.f - fga)) * rnew_a; - out.g[x] = (fg.g[x] * fga + bg.g[x] * bg.a[x] * (1.f - fga)) * rnew_a; - out.b[x] = (fg.b[x] * fga + bg.b[x] * bg.a[x] * (1.f - fga)) * rnew_a; - out.a[x] = new_a; - } - } -} -void PerformAlphaBlending(const float* bg, const float* bga, const float* fg, - const float* fga, float* out, size_t num_pixels, - bool alpha_is_premultiplied, bool clamp) { - if (bg == bga && fg == fga) { - for (size_t x = 0; x < num_pixels; ++x) { - float fa = clamp ? fga[x] : Clamp(fga[x]); - out[x] = (1.f - (1.f - fa) * (1.f - bga[x])); - } - } else { - if (alpha_is_premultiplied) { - for (size_t x = 0; x < num_pixels; ++x) { - float fa = clamp ? fga[x] : Clamp(fga[x]); - out[x] = (fg[x] + bg[x] * (1.f - fa)); - } - } else { - for (size_t x = 0; x < num_pixels; ++x) { - float fa = clamp ? fga[x] : Clamp(fga[x]); - const float new_a = 1.f - (1.f - fa) * (1.f - bga[x]); - const float rnew_a = (new_a > 0 ? 1.f / new_a : 0.f); - out[x] = (fg[x] * fa + bg[x] * bga[x] * (1.f - fa)) * rnew_a; - } - } - } -} - -void PerformAlphaWeightedAdd(const float* bg, const float* fg, const float* fga, - float* out, size_t num_pixels, bool clamp) { - if (fg == fga) { - memcpy(out, bg, num_pixels * sizeof(*out)); - } else if (clamp) { - for (size_t x = 0; x < num_pixels; ++x) { - out[x] = bg[x] + fg[x] * Clamp(fga[x]); - } - } else { - for (size_t x = 0; x < num_pixels; ++x) { - out[x] = bg[x] + fg[x] * fga[x]; - } - } -} - -void PerformMulBlending(const float* bg, const float* fg, float* out, - size_t num_pixels, bool clamp) { - if (clamp) { - for (size_t x = 0; x < num_pixels; ++x) { - out[x] = bg[x] * Clamp(fg[x]); - } - } else { - for (size_t x = 0; x < num_pixels; ++x) { - out[x] = bg[x] * fg[x]; - } - } -} - -void PremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g, - float* JXL_RESTRICT b, const float* JXL_RESTRICT a, - size_t num_pixels) { - for (size_t x = 0; x < num_pixels; ++x) { - const float multiplier = std::max(kSmallAlpha, a[x]); - r[x] *= multiplier; - g[x] *= multiplier; - b[x] *= multiplier; - } -} - -void UnpremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g, - float* JXL_RESTRICT b, const float* JXL_RESTRICT a, - size_t num_pixels) { - for (size_t x = 0; x < num_pixels; ++x) { - const float multiplier = 1.f / std::max(kSmallAlpha, a[x]); - r[x] *= multiplier; - g[x] *= multiplier; - b[x] *= multiplier; - } -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/alpha.h b/third_party/jpeg-xl/lib/jxl/alpha.h deleted file mode 100644 index efb76c800f862..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/alpha.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_ALPHA_H_ -#define LIB_JXL_ALPHA_H_ - -#include -#include - -#include - -#include "lib/jxl/base/compiler_specific.h" - -namespace jxl { - -// A very small value to avoid divisions by zero when converting to -// unpremultiplied alpha. Page 21 of the technical introduction to OpenEXR -// (https://www.openexr.com/documentation/TechnicalIntroduction.pdf) recommends -// "a power of two" that is "less than half of the smallest positive 16-bit -// floating-point value". That smallest value happens to be the denormal number -// 2^-24, so 2^-26 should be a good choice. -static constexpr float kSmallAlpha = 1.f / (1u << 26u); - -struct AlphaBlendingInputLayer { - const float* r; - const float* g; - const float* b; - const float* a; -}; - -struct AlphaBlendingOutput { - float* r; - float* g; - float* b; - float* a; -}; - -// Note: The pointers in `out` are allowed to alias those in `bg` or `fg`. -// No pointer shall be null. -void PerformAlphaBlending(const AlphaBlendingInputLayer& bg, - const AlphaBlendingInputLayer& fg, - const AlphaBlendingOutput& out, size_t num_pixels, - bool alpha_is_premultiplied, bool clamp); -// Single plane alpha blending -void PerformAlphaBlending(const float* bg, const float* bga, const float* fg, - const float* fga, float* out, size_t num_pixels, - bool alpha_is_premultiplied, bool clamp); - -void PerformAlphaWeightedAdd(const float* bg, const float* fg, const float* fga, - float* out, size_t num_pixels, bool clamp); - -void PerformMulBlending(const float* bg, const float* fg, float* out, - size_t num_pixels, bool clamp); - -void PremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g, - float* JXL_RESTRICT b, const float* JXL_RESTRICT a, - size_t num_pixels); -void UnpremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g, - float* JXL_RESTRICT b, const float* JXL_RESTRICT a, - size_t num_pixels); - -} // namespace jxl - -#endif // LIB_JXL_ALPHA_H_ diff --git a/third_party/jpeg-xl/lib/jxl/alpha_test.cc b/third_party/jpeg-xl/lib/jxl/alpha_test.cc deleted file mode 100644 index 6e26e77be6a7c..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/alpha_test.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/alpha.h" - -#include - -#include "lib/jxl/base/common.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace { - -AlphaBlendingInputLayer makeAbil(const Color& rgb, const float& a) { - const float* data = rgb.data(); - return {data, data + 1, data + 2, &a}; -} - -AlphaBlendingOutput makeAbo(Color& rgb, float& a) { - float* data = rgb.data(); - return {data, data + 1, data + 2, &a}; -} - -TEST(AlphaTest, BlendingWithNonPremultiplied) { - const Color bg_rgb{100, 110, 120}; - const float bg_a = 180.f / 255; - const Color fg_rgb{25, 21, 23}; - const float fg_a = 15420.f / 65535; - const float fg_a2 = 2.0f; - Color out_rgb; - float out_a; - PerformAlphaBlending( - /*bg=*/makeAbil(bg_rgb, bg_a), - /*fg=*/makeAbil(fg_rgb, fg_a), - /*out=*/makeAbo(out_rgb, out_a), 1, - /*alpha_is_premultiplied=*/false, /*clamp=*/false); - EXPECT_ARRAY_NEAR(out_rgb, (Color{77.2f, 83.0f, 90.6f}), 0.05f); - EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5); - PerformAlphaBlending( - /*bg=*/makeAbil(bg_rgb, bg_a), - /*fg=*/makeAbil(fg_rgb, fg_a2), - /*out=*/makeAbo(out_rgb, out_a), 1, - /*alpha_is_premultiplied=*/false, /*clamp=*/true); - EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f); - EXPECT_NEAR(out_a, 1.0f, 1e-5); -} - -TEST(AlphaTest, BlendingWithPremultiplied) { - const Color bg_rgb{100, 110, 120}; - const float bg_a = 180.f / 255; - const Color fg_rgb{25, 21, 23}; - const float fg_a = 15420.f / 65535; - const float fg_a2 = 2.0f; - Color out_rgb; - float out_a; - PerformAlphaBlending( - /*bg=*/makeAbil(bg_rgb, bg_a), - /*fg=*/makeAbil(fg_rgb, fg_a), - /*out=*/makeAbo(out_rgb, out_a), 1, - /*alpha_is_premultiplied=*/true, /*clamp=*/false); - EXPECT_ARRAY_NEAR(out_rgb, (Color{101.5f, 105.1f, 114.8f}), 0.05f); - EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5); - PerformAlphaBlending( - /*bg=*/makeAbil(bg_rgb, bg_a), - /*fg=*/makeAbil(fg_rgb, fg_a2), - /*out=*/makeAbo(out_rgb, out_a), 1, - /*alpha_is_premultiplied=*/true, /*clamp=*/true); - EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f); - EXPECT_NEAR(out_a, 1.0f, 1e-5); -} - -TEST(AlphaTest, Mul) { - const float bg = 100; - const float fg = 25; - float out; - PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/false); - EXPECT_NEAR(out, fg * bg, .05f); - PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/true); - EXPECT_NEAR(out, bg, .05f); -} - -TEST(AlphaTest, PremultiplyAndUnpremultiply) { - using F4 = std::array; - const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f}; - F4 r{120, 130, 140, 150}; - F4 g{124, 134, 144, 154}; - F4 b{127, 137, 147, 157}; - - PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); - EXPECT_ARRAY_NEAR(r, (F4{0.0f, 130 * 63.f / 255, 140 * 127.f / 255, 150}), - 1e-5f); - EXPECT_ARRAY_NEAR(g, (F4{0.0f, 134 * 63.f / 255, 144 * 127.f / 255, 154}), - 1e-5f); - EXPECT_ARRAY_NEAR(b, (F4{0.0f, 137 * 63.f / 255, 147 * 127.f / 255, 157}), - 1e-5f); - - UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); - EXPECT_ARRAY_NEAR(r, (F4{120, 130, 140, 150}), 1e-4f); - EXPECT_ARRAY_NEAR(g, (F4{124, 134, 144, 154}), 1e-4f); - EXPECT_ARRAY_NEAR(b, (F4{127, 137, 147, 157}), 1e-4f); -} - -TEST(AlphaTest, UnpremultiplyAndPremultiply) { - using F4 = std::array; - const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f}; - F4 r{50, 60, 70, 80}; - F4 g{54, 64, 74, 84}; - F4 b{57, 67, 77, 87}; - - UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); - EXPECT_ARRAY_NEAR( - r, (F4{50.0f * (1 << 26), 60 * 255.f / 63, 70 * 255.f / 127, 80}), 1e-4f); - EXPECT_ARRAY_NEAR( - g, (F4{54.0f * (1 << 26), 64 * 255.f / 63, 74 * 255.f / 127, 84}), 1e-4f); - EXPECT_ARRAY_NEAR( - b, (F4{57.0f * (1 << 26), 67 * 255.f / 63, 77 * 255.f / 127, 87}), 1e-4f); - - PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); - EXPECT_ARRAY_NEAR(r, (F4{50, 60, 70, 80}), 1e-4); - EXPECT_ARRAY_NEAR(g, (F4{54, 64, 74, 84}), 1e-4); - EXPECT_ARRAY_NEAR(b, (F4{57, 67, 77, 87}), 1e-4); -} - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/ans_common.cc b/third_party/jpeg-xl/lib/jxl/ans_common.cc deleted file mode 100644 index 306ecb7276782..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ans_common.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/ans_common.h" - -#include -#include -#include -#include - -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -std::vector CreateFlatHistogram(int length, int total_count) { - JXL_DASSERT(length > 0); - JXL_DASSERT(length <= total_count); - const int count = total_count / length; - std::vector result(length, count); - const int rem_counts = total_count % length; - for (int i = 0; i < rem_counts; ++i) { - ++result[i]; - } - return result; -} - -// First, all trailing non-occurring symbols are removed from the distribution; -// if this leaves the distribution empty, a placeholder symbol with max weight -// is added. This ensures that the resulting distribution sums to total table -// size. Then, `entry_size` is chosen to be the largest power of two so that -// `table_size` = ANS_TAB_SIZE/`entry_size` is at least as big as the -// distribution size. -// Note that each entry will only ever contain two different symbols, and -// consecutive ranges of offsets, which allows us to use a compact -// representation. -// Each entry is initialized with only the (symbol=i, offset) pairs; then -// positions for which the entry overflows (i.e. distribution[i] > entry_size) -// or is not full are computed, and put into a stack in increasing order. -// Missing symbols in the distribution are padded with 0 (because `table_size` -// >= number of symbols). The `cutoff` value for each entry is initialized to -// the number of occupied slots in that entry (i.e. `distributions[i]`). While -// the overflowing-symbol stack is not empty (which implies that the -// underflowing-symbol stack also is not), the top overfull and underfull -// positions are popped from the stack; the empty slots in the underfull entry -// are then filled with as many slots as needed from the overfull entry; such -// slots are placed after the slots in the overfull entry, and `offsets[1]` is -// computed accordingly. The formerly underfull entry is thus now neither -// underfull nor overfull, and represents exactly two symbols. The overfull -// entry might be either overfull or underfull, and is pushed into the -// corresponding stack. -Status InitAliasTable(std::vector distribution, uint32_t log_range, - size_t log_alpha_size, - AliasTable::Entry* JXL_RESTRICT a) { - const uint32_t range = 1 << log_range; - const size_t table_size = 1 << log_alpha_size; - JXL_ENSURE(table_size <= range); - while (!distribution.empty() && distribution.back() == 0) { - distribution.pop_back(); - } - // Ensure that a valid table is always returned, even for an empty - // alphabet. Otherwise, a specially-crafted stream might crash the - // decoder. - if (distribution.empty()) { - distribution.emplace_back(range); - } - JXL_ENSURE(distribution.size() <= table_size); - const uint32_t entry_size = range >> log_alpha_size; // this is exact - int single_symbol = -1; - int sum = 0; - // Special case for single-symbol distributions, that ensures that the state - // does not change when decoding from such a distribution. Note that, since we - // hardcode offset0 == 0, it is not straightforward (if at all possible) to - // fix the general case to produce this result. - for (size_t sym = 0; sym < distribution.size(); sym++) { - int32_t v = distribution[sym]; - sum += v; - if (v == ANS_TAB_SIZE) { - JXL_ENSURE(single_symbol == -1); - single_symbol = sym; - } - } - JXL_ENSURE(static_cast(sum) == range); - if (single_symbol != -1) { - uint8_t sym = single_symbol; - JXL_ENSURE(single_symbol == sym); - for (size_t i = 0; i < table_size; i++) { - a[i].right_value = sym; - a[i].cutoff = 0; - a[i].offsets1 = entry_size * i; - a[i].freq0 = 0; - a[i].freq1_xor_freq0 = ANS_TAB_SIZE; - } - return true; - } - - std::vector underfull_posn; - std::vector overfull_posn; - std::vector cutoffs(1 << log_alpha_size); - // Initialize entries. - for (size_t i = 0; i < distribution.size(); i++) { - cutoffs[i] = distribution[i]; - if (cutoffs[i] > entry_size) { - overfull_posn.push_back(i); - } else if (cutoffs[i] < entry_size) { - underfull_posn.push_back(i); - } - } - for (uint32_t i = distribution.size(); i < table_size; i++) { - cutoffs[i] = 0; - underfull_posn.push_back(i); - } - // Reassign overflow/underflow values. - while (!overfull_posn.empty()) { - uint32_t overfull_i = overfull_posn.back(); - overfull_posn.pop_back(); - JXL_ENSURE(!underfull_posn.empty()); - uint32_t underfull_i = underfull_posn.back(); - underfull_posn.pop_back(); - uint32_t underfull_by = entry_size - cutoffs[underfull_i]; - cutoffs[overfull_i] -= underfull_by; - // overfull positions have their original symbols - a[underfull_i].right_value = overfull_i; - a[underfull_i].offsets1 = cutoffs[overfull_i]; - // Slots in the right part of entry underfull_i were taken from the end - // of the symbols in entry overfull_i. - if (cutoffs[overfull_i] < entry_size) { - underfull_posn.push_back(overfull_i); - } else if (cutoffs[overfull_i] > entry_size) { - overfull_posn.push_back(overfull_i); - } - } - for (uint32_t i = 0; i < table_size; i++) { - // cutoffs[i] is properly initialized but the clang-analyzer doesn't infer - // it since it is partially initialized across two for-loops. - // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) - if (cutoffs[i] == entry_size) { - a[i].right_value = i; - a[i].offsets1 = 0; - a[i].cutoff = 0; - } else { - // Note that, if cutoff is not equal to entry_size, - // a[i].offsets1 was initialized with (overfull cutoff) - - // (entry_size - a[i].cutoff). Thus, subtracting - // a[i].cutoff cannot make it negative. - a[i].offsets1 -= cutoffs[i]; - a[i].cutoff = cutoffs[i]; - } - const size_t freq0 = i < distribution.size() ? distribution[i] : 0; - const size_t i1 = a[i].right_value; - const size_t freq1 = i1 < distribution.size() ? distribution[i1] : 0; - a[i].freq0 = static_cast(freq0); - a[i].freq1_xor_freq0 = static_cast(freq1 ^ freq0); - } - return true; -} - -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/ans_common.h b/third_party/jpeg-xl/lib/jxl/ans_common.h deleted file mode 100644 index 20897028f026a..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ans_common.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_ANS_COMMON_H_ -#define LIB_JXL_ANS_COMMON_H_ - -#include -#include -#include -#include -#include -#include // Prefetch -#include - -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -// Returns the precision (number of bits) that should be used to store -// a histogram count such that Log2Floor(count) == logcount. -static JXL_MAYBE_UNUSED JXL_INLINE uint32_t -GetPopulationCountPrecision(uint32_t logcount, uint32_t shift) { - int32_t r = std::min( - logcount, static_cast(shift) - - static_cast((ANS_LOG_TAB_SIZE - logcount) >> 1)); - if (r < 0) return 0; - return r; -} - -// Returns a histogram where the counts are positive, differ by at most 1, -// and add up to total_count. The bigger counts (if any) are at the beginning -// of the histogram. -std::vector CreateFlatHistogram(int length, int total_count); - -// An alias table implements a mapping from the [0, ANS_TAB_SIZE) range into -// the [0, ANS_MAX_ALPHABET_SIZE) range, satisfying the following conditions: -// - each symbol occurs as many times as specified by any valid distribution -// of frequencies of the symbols. A valid distribution here is an array of -// ANS_MAX_ALPHABET_SIZE that contains numbers in the range [0, ANS_TAB_SIZE], -// and whose sum is ANS_TAB_SIZE. -// - lookups can be done in constant time, and also return how many smaller -// input values map into the same symbol, according to some well-defined order -// of input values. -// - the space used by the alias table is given by a small constant times the -// index of the largest symbol with nonzero probability in the distribution. -// Each of the entries in the table covers a range of `entry_size` values in the -// [0, ANS_TAB_SIZE) range; consecutive entries represent consecutive -// sub-ranges. In the range covered by entry `i`, the first `cutoff` values map -// to symbol `i`, while the others map to symbol `right_value`. -// -// TODO(veluca): consider making the order used for computing offsets easier to -// define - it is currently defined by the algorithm to compute the alias table. -// Beware of breaking the implicit assumption that symbols that come after the -// cutoff value should have an offset at least as big as the cutoff. - -struct AliasTable { - struct Symbol { - size_t value; - size_t offset; - size_t freq; - }; - -// Working set size matters here (~64 tables x 256 entries). -// offsets0 is always zero (beginning of [0] side among the same symbol). -// offsets1 is an offset of (pos >= cutoff) side decremented by cutoff. -#pragma pack(push, 1) - struct Entry { - uint8_t cutoff; // < kEntrySizeMinus1 when used by ANS. - uint8_t right_value; // < alphabet size. - uint16_t freq0; - - // Only used if `greater` (see Lookup) - uint16_t offsets1; // <= ANS_TAB_SIZE - uint16_t freq1_xor_freq0; // for branchless ternary in Lookup - }; -#pragma pack(pop) - - // Dividing `value` by `entry_size` determines `i`, the entry which is - // responsible for the input. If the remainder is below `cutoff`, then the - // mapped symbol is `i`; since `offsets[0]` stores the number of occurrences - // of `i` "before" the start of this entry, the offset of the input will be - // `offsets[0] + remainder`. If the remainder is above cutoff, the mapped - // symbol is `right_value`; since `offsets[1]` stores the number of - // occurrences of `right_value` "before" this entry, minus the `cutoff` value, - // the input offset is then `remainder + offsets[1]`. - static JXL_INLINE Symbol Lookup(const Entry* JXL_RESTRICT table, size_t value, - size_t log_entry_size, - size_t entry_size_minus_1) { - const size_t i = value >> log_entry_size; - const size_t pos = value & entry_size_minus_1; - -#if JXL_BYTE_ORDER_LITTLE - uint64_t entry; - memcpy(&entry, &table[i].cutoff, sizeof(entry)); - const size_t cutoff = entry & 0xFF; // = MOVZX - const size_t right_value = (entry >> 8) & 0xFF; // = MOVZX - const size_t freq0 = (entry >> 16) & 0xFFFF; -#else - // Generates multiple loads with complex addressing. - const size_t cutoff = table[i].cutoff; - const size_t right_value = table[i].right_value; - const size_t freq0 = table[i].freq0; -#endif - - const bool greater = pos >= cutoff; - -#if JXL_BYTE_ORDER_LITTLE - const uint64_t conditional = greater ? entry : 0; // = CMOV - const size_t offsets1_or_0 = (conditional >> 32) & 0xFFFF; - const size_t freq1_xor_freq0_or_0 = conditional >> 48; -#else - const size_t offsets1_or_0 = greater ? table[i].offsets1 : 0; - const size_t freq1_xor_freq0_or_0 = greater ? table[i].freq1_xor_freq0 : 0; -#endif - - // WARNING: moving this code may interfere with CMOV heuristics. - Symbol s; - s.value = greater ? right_value : i; - s.offset = offsets1_or_0 + pos; - s.freq = freq0 ^ freq1_xor_freq0_or_0; // = greater ? freq1 : freq0 - // XOR avoids implementation-defined conversion from unsigned to signed. - // Alternatives considered: BEXTR is 2 cycles on HSW, SET+shift causes - // spills, simple ternary has a long dependency chain. - - return s; - } - - static HWY_INLINE void Prefetch(const Entry* JXL_RESTRICT table, size_t value, - size_t log_entry_size) { - const size_t i = value >> log_entry_size; - hwy::Prefetch(table + i); - } -}; - -// Computes an alias table for a given distribution. -Status InitAliasTable(std::vector distribution, uint32_t log_range, - size_t log_alpha_size, AliasTable::Entry* JXL_RESTRICT a); - -} // namespace jxl - -#endif // LIB_JXL_ANS_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/jxl/ans_common_test.cc b/third_party/jpeg-xl/lib/jxl/ans_common_test.cc deleted file mode 100644 index b97f5186f9fae..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ans_common_test.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/ans_common.h" - -#include - -#include "lib/jxl/ans_params.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace { - -void VerifyAliasDistribution(const std::vector& distribution, - uint32_t log_range) { - constexpr size_t log_alpha_size = 8; - AliasTable::Entry table[1 << log_alpha_size]; - ASSERT_TRUE(InitAliasTable(distribution, log_range, log_alpha_size, table)); - uint32_t range = 1 << log_range; - std::vector> offsets(distribution.size()); - for (uint32_t i = 0; i < range; i++) { - AliasTable::Symbol s = AliasTable::Lookup( - table, i, ANS_LOG_TAB_SIZE - 8, (1 << (ANS_LOG_TAB_SIZE - 8)) - 1); - offsets[s.value].push_back(s.offset); - } - for (uint32_t i = 0; i < distribution.size(); i++) { - ASSERT_EQ(static_cast(distribution[i]), offsets[i].size()); - std::sort(offsets[i].begin(), offsets[i].end()); - for (uint32_t j = 0; j < offsets[i].size(); j++) { - ASSERT_EQ(offsets[i][j], j); - } - } -} - -TEST(ANSCommonTest, AliasDistributionSmoke) { - VerifyAliasDistribution({ANS_TAB_SIZE / 2, ANS_TAB_SIZE / 2}, - ANS_LOG_TAB_SIZE); - VerifyAliasDistribution({ANS_TAB_SIZE}, ANS_LOG_TAB_SIZE); - VerifyAliasDistribution({0, 0, 0, ANS_TAB_SIZE, 0}, ANS_LOG_TAB_SIZE); -} - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/ans_params.h b/third_party/jpeg-xl/lib/jxl/ans_params.h deleted file mode 100644 index 4bbc284c0bf49..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ans_params.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_ANS_PARAMS_H_ -#define LIB_JXL_ANS_PARAMS_H_ - -// Common parameters that are needed for both the ANS entropy encoding and -// decoding methods. - -#include -#include - -namespace jxl { - -// TODO(veluca): decide if 12 is the best constant here (valid range is up to -// 16). This requires recomputing the Huffman tables in {enc,dec}_ans.cc -// 14 gives a 0.2% improvement at d1 and makes d8 slightly worse. This is -// likely not worth the increase in encoder complexity. -#define ANS_LOG_TAB_SIZE 12u -#define ANS_TAB_SIZE (1 << ANS_LOG_TAB_SIZE) -#define ANS_TAB_MASK (ANS_TAB_SIZE - 1) - -// Largest possible symbol to be encoded by either ANS or prefix coding. -#define PREFIX_MAX_ALPHABET_SIZE 4096 -#define ANS_MAX_ALPHABET_SIZE 256 - -// Max number of bits for prefix coding. -#define PREFIX_MAX_BITS 15 - -#define ANS_SIGNATURE 0x13 // Initial state, used as CRC. - -} // namespace jxl - -#endif // LIB_JXL_ANS_PARAMS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/ans_test.cc b/third_party/jpeg-xl/lib/jxl/ans_test.cc deleted file mode 100644 index 8bed98895f4c1..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/ans_test.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include - -#include -#include -#include - -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/random.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/dec_ans.h" -#include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/enc_ans.h" -#include "lib/jxl/enc_aux_out.h" -#include "lib/jxl/enc_bit_writer.h" -#include "lib/jxl/test_memory_manager.h" -#include "lib/jxl/test_utils.h" -#include "lib/jxl/testing.h" - -namespace jxl { -namespace { - -void RoundtripTestcase(int n_histograms, int alphabet_size, - const std::vector& input_values) { - JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); - constexpr uint16_t kMagic1 = 0x9e33; - constexpr uint16_t kMagic2 = 0x8b04; - - BitWriter writer{memory_manager}; - // Space for magic bytes. - ASSERT_TRUE(writer.WithMaxBits(16, LayerType::Header, nullptr, [&] { - writer.Write(16, kMagic1); - return true; - })); - - std::vector context_map; - EntropyEncodingData codes; - std::vector> input_values_vec; - input_values_vec.push_back(input_values); - - JXL_TEST_ASSIGN_OR_DIE( - size_t cost, - BuildAndEncodeHistograms(memory_manager, HistogramParams(), n_histograms, - input_values_vec, &codes, &context_map, &writer, - LayerType::Header, nullptr)); - (void)cost; - ASSERT_TRUE(WriteTokens(input_values_vec[0], codes, context_map, 0, &writer, - LayerType::Header, nullptr)); - - // Magic bytes + padding - ASSERT_TRUE(writer.WithMaxBits(24, LayerType::Header, nullptr, [&] { - writer.Write(16, kMagic2); - writer.ZeroPadToByte(); - return true; - })); - - // We do not truncate the output. Reading past the end reads out zeroes - // anyway. - BitReader br(writer.GetSpan()); - - ASSERT_EQ(br.ReadBits(16), kMagic1); - - std::vector dec_context_map; - ANSCode decoded_codes; - ASSERT_TRUE(DecodeHistograms(memory_manager, &br, n_histograms, - &decoded_codes, &dec_context_map)); - ASSERT_EQ(dec_context_map, context_map); - JXL_TEST_ASSIGN_OR_DIE(ANSSymbolReader reader, - ANSSymbolReader::Create(&decoded_codes, &br)); - - for (const Token& symbol : input_values) { - uint32_t read_symbol = - reader.ReadHybridUint(symbol.context, &br, dec_context_map); - ASSERT_EQ(read_symbol, symbol.value); - } - ASSERT_TRUE(reader.CheckANSFinalState()); - - ASSERT_EQ(br.ReadBits(16), kMagic2); - EXPECT_TRUE(br.Close()); -} - -TEST(ANSTest, EmptyRoundtrip) { - RoundtripTestcase(2, ANS_MAX_ALPHABET_SIZE, std::vector()); -} - -TEST(ANSTest, SingleSymbolRoundtrip) { - for (uint32_t i = 0; i < ANS_MAX_ALPHABET_SIZE; i++) { - RoundtripTestcase(2, ANS_MAX_ALPHABET_SIZE, {{0, i}}); - } - for (uint32_t i = 0; i < ANS_MAX_ALPHABET_SIZE; i++) { - RoundtripTestcase(2, ANS_MAX_ALPHABET_SIZE, - std::vector(1024, {0, i})); - } -} - -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(THREAD_SANITIZER) -constexpr size_t kReps = 3; -#else -constexpr size_t kReps = 10; -#endif - -void RoundtripRandomStream(int alphabet_size, size_t reps = kReps, - size_t num = 1 << 18) { - constexpr int kNumHistograms = 3; - Rng rng(0); - for (size_t i = 0; i < reps; i++) { - std::vector symbols; - for (size_t j = 0; j < num; j++) { - int context = rng.UniformI(0, kNumHistograms); - int value = rng.UniformU(0, alphabet_size); - symbols.emplace_back(context, value); - } - RoundtripTestcase(kNumHistograms, alphabet_size, symbols); - } -} - -void RoundtripRandomUnbalancedStream(int alphabet_size) { - constexpr int kNumHistograms = 3; - constexpr int kPrecision = 1 << 10; - Rng rng(0); - for (size_t i = 0; i < kReps; i++) { - std::vector distributions[kNumHistograms] = {}; - for (auto& distr : distributions) { - distr.resize(kPrecision); - int symbol = 0; - int remaining = 1; - for (int k = 0; k < kPrecision; k++) { - if (remaining == 0) { - if (symbol < alphabet_size - 1) symbol++; - // There is no meaning behind this distribution: it's anything that - // will create a nonuniform distribution and won't have too few - // symbols usually. Also we want different distributions we get to be - // sufficiently dissimilar. - remaining = rng.UniformU(0, kPrecision - k + 1); - } - distr[k] = symbol; - remaining--; - } - } - std::vector symbols; - for (int j = 0; j < 1 << 18; j++) { - int context = rng.UniformI(0, kNumHistograms); - int value = rng.UniformU(0, kPrecision); - symbols.emplace_back(context, value); - } - RoundtripTestcase(kNumHistograms + 1, alphabet_size, symbols); - } -} - -TEST(ANSTest, RandomStreamRoundtrip3Small) { RoundtripRandomStream(3, 1, 16); } - -TEST(ANSTest, RandomStreamRoundtrip3) { RoundtripRandomStream(3); } - -TEST(ANSTest, RandomStreamRoundtripBig) { - RoundtripRandomStream(ANS_MAX_ALPHABET_SIZE); -} - -TEST(ANSTest, RandomUnbalancedStreamRoundtrip3) { - RoundtripRandomUnbalancedStream(3); -} - -TEST(ANSTest, RandomUnbalancedStreamRoundtripBig) { - RoundtripRandomUnbalancedStream(ANS_MAX_ALPHABET_SIZE); -} - -TEST(ANSTest, UintConfigRoundtrip) { - JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); - for (size_t log_alpha_size = 5; log_alpha_size <= 8; log_alpha_size++) { - std::vector uint_config; - std::vector uint_config_dec; - for (size_t i = 0; i < log_alpha_size; i++) { - for (size_t j = 0; j <= i; j++) { - for (size_t k = 0; k <= i - j; k++) { - uint_config.emplace_back(i, j, k); - } - } - } - uint_config.emplace_back(log_alpha_size, 0, 0); - uint_config_dec.resize(uint_config.size()); - BitWriter writer{memory_manager}; - ASSERT_TRUE(writer.WithMaxBits( - 10 * uint_config.size(), LayerType::Header, nullptr, [&] { - EncodeUintConfigs(uint_config, &writer, log_alpha_size); - return true; - })); - writer.ZeroPadToByte(); - BitReader br(writer.GetSpan()); - EXPECT_TRUE(DecodeUintConfigs(log_alpha_size, &uint_config_dec, &br)); - EXPECT_TRUE(br.Close()); - for (size_t i = 0; i < uint_config.size(); i++) { - EXPECT_EQ(uint_config[i].split_token, uint_config_dec[i].split_token); - EXPECT_EQ(uint_config[i].msb_in_token, uint_config_dec[i].msb_in_token); - EXPECT_EQ(uint_config[i].lsb_in_token, uint_config_dec[i].lsb_in_token); - } - } -} - -void TestCheckpointing(bool ans, bool lz77) { - JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); - std::vector> input_values(1); - for (size_t i = 0; i < 1024; i++) { - input_values[0].emplace_back(0, i % 4); - } - // up to lz77 window size. - for (size_t i = 0; i < (1 << 20) - 1022; i++) { - input_values[0].emplace_back(0, (i % 5) + 4); - } - // Ensure that when the window wraps around, new values are different. - input_values[0].emplace_back(0, 0); - for (size_t i = 0; i < 1024; i++) { - input_values[0].emplace_back(0, i % 4); - } - - std::vector context_map; - EntropyEncodingData codes; - HistogramParams params; - params.lz77_method = lz77 ? HistogramParams::LZ77Method::kLZ77 - : HistogramParams::LZ77Method::kNone; - params.force_huffman = !ans; - - BitWriter writer{memory_manager}; - { - auto input_values_copy = input_values; - JXL_TEST_ASSIGN_OR_DIE( - size_t cost, BuildAndEncodeHistograms( - memory_manager, params, 1, input_values_copy, &codes, - &context_map, &writer, LayerType::Header, nullptr)); - (void)cost; - ASSERT_TRUE(WriteTokens(input_values_copy[0], codes, context_map, 0, - &writer, LayerType::Header, nullptr)); - writer.ZeroPadToByte(); - } - - // We do not truncate the output. Reading past the end reads out zeroes - // anyway. - BitReader br(writer.GetSpan()); - Status status = true; - { - BitReaderScopedCloser bc(br, status); - - std::vector dec_context_map; - ANSCode decoded_codes; - ASSERT_TRUE(DecodeHistograms(memory_manager, &br, 1, &decoded_codes, - &dec_context_map)); - ASSERT_EQ(dec_context_map, context_map); - JXL_TEST_ASSIGN_OR_DIE(ANSSymbolReader reader, - ANSSymbolReader::Create(&decoded_codes, &br)); - - ANSSymbolReader::Checkpoint checkpoint; - size_t br_pos = 0; - constexpr size_t kInterval = ANSSymbolReader::kMaxCheckpointInterval - 2; - for (size_t i = 0; i < input_values[0].size(); i++) { - if (i % kInterval == 0 && i > 0) { - reader.Restore(checkpoint); - ASSERT_TRUE(br.Close()); - br = BitReader(writer.GetSpan()); - br.SkipBits(br_pos); - for (size_t j = i - kInterval; j < i; j++) { - Token symbol = input_values[0][j]; - uint32_t read_symbol = - reader.ReadHybridUint(symbol.context, &br, dec_context_map); - ASSERT_EQ(read_symbol, symbol.value) << "j = " << j; - } - } - if (i % kInterval == 0) { - reader.Save(&checkpoint); - br_pos = br.TotalBitsConsumed(); - } - Token symbol = input_values[0][i]; - uint32_t read_symbol = - reader.ReadHybridUint(symbol.context, &br, dec_context_map); - ASSERT_EQ(read_symbol, symbol.value) << "i = " << i; - } - ASSERT_TRUE(reader.CheckANSFinalState()); - } - EXPECT_TRUE(status); -} - -TEST(ANSTest, TestCheckpointingANS) { - TestCheckpointing(/*ans=*/true, /*lz77=*/false); -} - -TEST(ANSTest, TestCheckpointingPrefix) { - TestCheckpointing(/*ans=*/false, /*lz77=*/false); -} - -TEST(ANSTest, TestCheckpointingANSLZ77) { - TestCheckpointing(/*ans=*/true, /*lz77=*/true); -} - -TEST(ANSTest, TestCheckpointingPrefixLZ77) { - TestCheckpointing(/*ans=*/false, /*lz77=*/true); -} - -} // namespace -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/base/arch_macros.h b/third_party/jpeg-xl/lib/jxl/base/arch_macros.h deleted file mode 100644 index a98301915e5b8..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/arch_macros.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_ARCH_MACROS_H_ -#define LIB_JXL_BASE_ARCH_MACROS_H_ - -// Defines the JXL_ARCH_* macros. - -namespace jxl { - -#if defined(__x86_64__) || defined(_M_X64) -#define JXL_ARCH_X64 1 -#else -#define JXL_ARCH_X64 0 -#endif - -#if defined(__powerpc64__) || defined(_M_PPC) -#define JXL_ARCH_PPC 1 -#else -#define JXL_ARCH_PPC 0 -#endif - -#if defined(__aarch64__) || defined(__arm__) -#define JXL_ARCH_ARM 1 -#else -#define JXL_ARCH_ARM 0 -#endif - -} // namespace jxl - -#endif // LIB_JXL_BASE_ARCH_MACROS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/bits.h b/third_party/jpeg-xl/lib/jxl/base/bits.h deleted file mode 100644 index a79fdc2c99144..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/bits.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_BITS_H_ -#define LIB_JXL_BASE_BITS_H_ - -// Specialized instructions for processing register-sized bit arrays. - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" - -#if JXL_COMPILER_MSVC -#include -#endif - -#include -#include - -namespace jxl { - -// Empty struct used as a size tag type. -template -struct SizeTag {}; - -template -constexpr bool IsSigned() { - // TODO(eustas): remove dupes - return static_cast(0) > static_cast(-1); -} - -// Undefined results for x == 0. -static JXL_INLINE JXL_MAYBE_UNUSED size_t -Num0BitsAboveMS1Bit_Nonzero(SizeTag<4> /* tag */, const uint32_t x) { - JXL_DASSERT(x != 0); -#if JXL_COMPILER_MSVC - unsigned long index; - _BitScanReverse(&index, x); - return 31 - index; -#else - return static_cast(__builtin_clz(x)); -#endif -} -static JXL_INLINE JXL_MAYBE_UNUSED size_t -Num0BitsAboveMS1Bit_Nonzero(SizeTag<8> /* tag */, const uint64_t x) { - JXL_DASSERT(x != 0); -#if JXL_COMPILER_MSVC -#if JXL_ARCH_X64 - unsigned long index; - _BitScanReverse64(&index, x); - return 63 - index; -#else // JXL_ARCH_X64 - // _BitScanReverse64 not available - uint32_t msb = static_cast(x >> 32u); - unsigned long index; - if (msb == 0) { - uint32_t lsb = static_cast(x & 0xFFFFFFFF); - _BitScanReverse(&index, lsb); - return 63 - index; - } else { - _BitScanReverse(&index, msb); - return 31 - index; - } -#endif // JXL_ARCH_X64 -#else - return static_cast(__builtin_clzll(x)); -#endif -} -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t -Num0BitsAboveMS1Bit_Nonzero(const T x) { - static_assert(!IsSigned(), "Num0BitsAboveMS1Bit_Nonzero: use unsigned"); - return Num0BitsAboveMS1Bit_Nonzero(SizeTag(), x); -} - -// Undefined results for x == 0. -static JXL_INLINE JXL_MAYBE_UNUSED size_t -Num0BitsBelowLS1Bit_Nonzero(SizeTag<4> /* tag */, const uint32_t x) { - JXL_DASSERT(x != 0); -#if JXL_COMPILER_MSVC - unsigned long index; - _BitScanForward(&index, x); - return index; -#else - return static_cast(__builtin_ctz(x)); -#endif -} -static JXL_INLINE JXL_MAYBE_UNUSED size_t -Num0BitsBelowLS1Bit_Nonzero(SizeTag<8> /* tag */, const uint64_t x) { - JXL_DASSERT(x != 0); -#if JXL_COMPILER_MSVC -#if JXL_ARCH_X64 - unsigned long index; - _BitScanForward64(&index, x); - return index; -#else // JXL_ARCH_64 - // _BitScanForward64 not available - uint32_t lsb = static_cast(x & 0xFFFFFFFF); - unsigned long index; - if (lsb == 0) { - uint32_t msb = static_cast(x >> 32u); - _BitScanForward(&index, msb); - return 32 + index; - } else { - _BitScanForward(&index, lsb); - return index; - } -#endif // JXL_ARCH_X64 -#else - return static_cast(__builtin_ctzll(x)); -#endif -} -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t Num0BitsBelowLS1Bit_Nonzero(T x) { - static_assert(!IsSigned(), "Num0BitsBelowLS1Bit_Nonzero: use unsigned"); - return Num0BitsBelowLS1Bit_Nonzero(SizeTag(), x); -} - -// Returns bit width for x == 0. -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t Num0BitsAboveMS1Bit(const T x) { - return (x == 0) ? sizeof(T) * 8 : Num0BitsAboveMS1Bit_Nonzero(x); -} - -// Returns bit width for x == 0. -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t Num0BitsBelowLS1Bit(const T x) { - return (x == 0) ? sizeof(T) * 8 : Num0BitsBelowLS1Bit_Nonzero(x); -} - -// Returns base-2 logarithm, rounded down. -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t FloorLog2Nonzero(const T x) { - return (sizeof(T) * 8 - 1) ^ Num0BitsAboveMS1Bit_Nonzero(x); -} - -// Returns base-2 logarithm, rounded up. -template -static JXL_INLINE JXL_MAYBE_UNUSED size_t CeilLog2Nonzero(const T x) { - const size_t floor_log2 = FloorLog2Nonzero(x); - if ((x & (x - 1)) == 0) return floor_log2; // power of two - return floor_log2 + 1; -} - -} // namespace jxl - -#endif // LIB_JXL_BASE_BITS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/byte_order.h b/third_party/jpeg-xl/lib/jxl/base/byte_order.h deleted file mode 100644 index cf8d7db082ca2..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/byte_order.h +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_BYTE_ORDER_H_ -#define LIB_JXL_BASE_BYTE_ORDER_H_ - -#include -#include -#include // memcpy - -#include "lib/jxl/base/compiler_specific.h" - -#if JXL_COMPILER_MSVC -#include // _byteswap_* -#endif - -#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) -#define JXL_BYTE_ORDER_LITTLE 1 -#else -// This means that we don't know that the byte order is little endian, in -// this case we use endian-neutral code that works for both little- and -// big-endian. -#define JXL_BYTE_ORDER_LITTLE 0 -#endif - -// Returns whether the system is little-endian (least-significant byte first). -#if JXL_BYTE_ORDER_LITTLE -static constexpr bool IsLittleEndian() { return true; } -#else -static inline bool IsLittleEndian() { - const uint32_t multibyte = 1; - uint8_t byte; - memcpy(&byte, &multibyte, 1); - return byte == 1; -} -#endif - -static inline bool SwapEndianness(JxlEndianness endianness) { - return ((endianness == JXL_BIG_ENDIAN && IsLittleEndian()) || - (endianness == JXL_LITTLE_ENDIAN && !IsLittleEndian())); -} - -#if JXL_COMPILER_MSVC -#define JXL_BSWAP16(x) _byteswap_ushort(x) -#define JXL_BSWAP32(x) _byteswap_ulong(x) -#define JXL_BSWAP64(x) _byteswap_uint64(x) -#else -#define JXL_BSWAP16(x) __builtin_bswap16(x) -#define JXL_BSWAP32(x) __builtin_bswap32(x) -#define JXL_BSWAP64(x) __builtin_bswap64(x) -#endif - -static JXL_INLINE uint32_t LoadBE16(const uint8_t* p) { - const uint32_t byte1 = p[0]; - const uint32_t byte0 = p[1]; - return (byte1 << 8) | byte0; -} - -static JXL_INLINE uint32_t LoadLE16(const uint8_t* p) { - const uint32_t byte0 = p[0]; - const uint32_t byte1 = p[1]; - return (byte1 << 8) | byte0; -} - -static JXL_INLINE uint32_t LoadBE32(const uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - uint32_t big; - memcpy(&big, p, 4); - return JXL_BSWAP32(big); -#else - // Byte-order-independent - can't assume this machine is big endian. - const uint32_t byte3 = p[0]; - const uint32_t byte2 = p[1]; - const uint32_t byte1 = p[2]; - const uint32_t byte0 = p[3]; - return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; -#endif -} - -static JXL_INLINE uint64_t LoadBE64(const uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - uint64_t big; - memcpy(&big, p, 8); - return JXL_BSWAP64(big); -#else - // Byte-order-independent - can't assume this machine is big endian. - const uint64_t byte7 = p[0]; - const uint64_t byte6 = p[1]; - const uint64_t byte5 = p[2]; - const uint64_t byte4 = p[3]; - const uint64_t byte3 = p[4]; - const uint64_t byte2 = p[5]; - const uint64_t byte1 = p[6]; - const uint64_t byte0 = p[7]; - return (byte7 << 56ull) | (byte6 << 48ull) | (byte5 << 40ull) | - (byte4 << 32ull) | (byte3 << 24ull) | (byte2 << 16ull) | - (byte1 << 8ull) | byte0; -#endif -} - -static JXL_INLINE uint32_t LoadLE32(const uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - uint32_t little; - memcpy(&little, p, 4); - return little; -#else - // Byte-order-independent - can't assume this machine is big endian. - const uint32_t byte0 = p[0]; - const uint32_t byte1 = p[1]; - const uint32_t byte2 = p[2]; - const uint32_t byte3 = p[3]; - return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; -#endif -} - -static JXL_INLINE uint64_t LoadLE64(const uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - uint64_t little; - memcpy(&little, p, 8); - return little; -#else - // Byte-order-independent - can't assume this machine is big endian. - const uint64_t byte0 = p[0]; - const uint64_t byte1 = p[1]; - const uint64_t byte2 = p[2]; - const uint64_t byte3 = p[3]; - const uint64_t byte4 = p[4]; - const uint64_t byte5 = p[5]; - const uint64_t byte6 = p[6]; - const uint64_t byte7 = p[7]; - return (byte7 << 56) | (byte6 << 48) | (byte5 << 40) | (byte4 << 32) | - (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; -#endif -} - -// Loads a Big-Endian float -static JXL_INLINE float LoadBEFloat(const uint8_t* p) { - uint32_t u = LoadBE32(p); - float result; - memcpy(&result, &u, 4); - return result; -} - -// Loads a Little-Endian float -static JXL_INLINE float LoadLEFloat(const uint8_t* p) { - uint32_t u = LoadLE32(p); - float result; - memcpy(&result, &u, 4); - return result; -} - -static JXL_INLINE void StoreBE16(const uint32_t native, uint8_t* p) { - p[0] = (native >> 8) & 0xFF; - p[1] = native & 0xFF; -} - -static JXL_INLINE void StoreLE16(const uint32_t native, uint8_t* p) { - p[1] = (native >> 8) & 0xFF; - p[0] = native & 0xFF; -} - -static JXL_INLINE void StoreBE32(const uint32_t native, uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - const uint32_t big = JXL_BSWAP32(native); - memcpy(p, &big, 4); -#else - // Byte-order-independent - can't assume this machine is big endian. - p[0] = native >> 24; - p[1] = (native >> 16) & 0xFF; - p[2] = (native >> 8) & 0xFF; - p[3] = native & 0xFF; -#endif -} - -static JXL_INLINE void StoreBE64(const uint64_t native, uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - const uint64_t big = JXL_BSWAP64(native); - memcpy(p, &big, 8); -#else - // Byte-order-independent - can't assume this machine is big endian. - p[0] = native >> 56ull; - p[1] = (native >> 48ull) & 0xFF; - p[2] = (native >> 40ull) & 0xFF; - p[3] = (native >> 32ull) & 0xFF; - p[4] = (native >> 24ull) & 0xFF; - p[5] = (native >> 16ull) & 0xFF; - p[6] = (native >> 8ull) & 0xFF; - p[7] = native & 0xFF; -#endif -} - -static JXL_INLINE void StoreLE32(const uint32_t native, uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - const uint32_t little = native; - memcpy(p, &little, 4); -#else - // Byte-order-independent - can't assume this machine is big endian. - p[3] = native >> 24; - p[2] = (native >> 16) & 0xFF; - p[1] = (native >> 8) & 0xFF; - p[0] = native & 0xFF; -#endif -} - -static JXL_INLINE void StoreLE64(const uint64_t native, uint8_t* p) { -#if JXL_BYTE_ORDER_LITTLE - const uint64_t little = native; - memcpy(p, &little, 8); -#else - // Byte-order-independent - can't assume this machine is big endian. - p[7] = native >> 56; - p[6] = (native >> 48) & 0xFF; - p[5] = (native >> 40) & 0xFF; - p[4] = (native >> 32) & 0xFF; - p[3] = (native >> 24) & 0xFF; - p[2] = (native >> 16) & 0xFF; - p[1] = (native >> 8) & 0xFF; - p[0] = native & 0xFF; -#endif -} - -static JXL_INLINE float BSwapFloat(float x) { - uint32_t u; - memcpy(&u, &x, 4); - uint32_t uswap = JXL_BSWAP32(u); - float xswap; - memcpy(&xswap, &uswap, 4); - return xswap; -} - -// Big/Little Endian order. -struct OrderBE {}; -struct OrderLE {}; - -// Wrappers for calling from generic code. -static JXL_INLINE void Store16(OrderBE /*tag*/, const uint32_t native, - uint8_t* p) { - StoreBE16(native, p); -} - -static JXL_INLINE void Store16(OrderLE /*tag*/, const uint32_t native, - uint8_t* p) { - StoreLE16(native, p); -} - -static JXL_INLINE void Store32(OrderBE /*tag*/, const uint32_t native, - uint8_t* p) { - StoreBE32(native, p); -} - -static JXL_INLINE void Store32(OrderLE /*tag*/, const uint32_t native, - uint8_t* p) { - StoreLE32(native, p); -} - -static JXL_INLINE uint32_t Load16(OrderBE /*tag*/, const uint8_t* p) { - return LoadBE16(p); -} - -static JXL_INLINE uint32_t Load16(OrderLE /*tag*/, const uint8_t* p) { - return LoadLE16(p); -} - -static JXL_INLINE uint32_t Load32(OrderBE /*tag*/, const uint8_t* p) { - return LoadBE32(p); -} - -static JXL_INLINE uint32_t Load32(OrderLE /*tag*/, const uint8_t* p) { - return LoadLE32(p); -} - -#endif // LIB_JXL_BASE_BYTE_ORDER_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/c_callback_support.h b/third_party/jpeg-xl/lib/jxl/base/c_callback_support.h deleted file mode 100644 index aee0ce5346592..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/c_callback_support.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_C_CALLBACK_SUPPORT_H_ -#define LIB_JXL_BASE_C_CALLBACK_SUPPORT_H_ - -#include - -namespace jxl { -namespace detail { - -template -struct MethodToCCallbackHelper {}; - -template -struct MethodToCCallbackHelper { - template - static R Call(void *opaque, Args... args) { - return (reinterpret_cast(opaque)->*method)( - std::forward(args)...); - } -}; - -} // namespace detail -} // namespace jxl - -#define METHOD_TO_C_CALLBACK(method) \ - ::jxl::detail::MethodToCCallbackHelper::Call - -#endif // LIB_JXL_BASE_C_CALLBACK_SUPPORT_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/common.h b/third_party/jpeg-xl/lib/jxl/base/common.h deleted file mode 100644 index 0893ef26b5a5a..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/common.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_COMMON_H_ -#define LIB_JXL_BASE_COMMON_H_ - -// Shared constants and helper functions. - -#include -#include -#include -#include -#include -#include -#include - -#include "lib/jxl/base/compiler_specific.h" - -namespace jxl { -// Some enums and typedefs used by more than one header file. - -constexpr size_t kBitsPerByte = 8; // more clear than CHAR_BIT - -constexpr inline size_t RoundUpBitsToByteMultiple(size_t bits) { - return (bits + 7) & ~static_cast(7); -} - -constexpr inline size_t RoundUpToBlockDim(size_t dim) { - return (dim + 7) & ~static_cast(7); -} - -static inline bool JXL_MAYBE_UNUSED SafeAdd(const uint64_t a, const uint64_t b, - uint64_t& sum) { - sum = a + b; - return sum >= a; // no need to check b - either sum >= both or < both. -} - -template -constexpr inline T1 DivCeil(T1 a, T2 b) { - return (a + b - 1) / b; -} - -// Works for any `align`; if a power of two, compiler emits ADD+AND. -constexpr inline size_t RoundUpTo(size_t what, size_t align) { - return DivCeil(what, align) * align; -} - -constexpr double kPi = 3.14159265358979323846264338327950288; - -// Reasonable default for sRGB, matches common monitors. We map white to this -// many nits (cd/m^2) by default. Butteraugli was tuned for 250 nits, which is -// very close. -// NB: This constant is not very "base", but it is shared between modules. -static constexpr float kDefaultIntensityTarget = 255; - -template -constexpr T Pi(T multiplier) { - return static_cast(multiplier * kPi); -} - -// Prior to C++14 (i.e. C++11): provide our own make_unique -#if __cplusplus < 201402L -template -std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} -#else -using std::make_unique; -#endif - -typedef std::array Color; - -// Backported std::experimental::to_array - -template -using remove_cv_t = typename std::remove_cv::type; - -template -struct index_sequence {}; - -template -struct make_index_sequence : make_index_sequence {}; - -template -struct make_index_sequence<0, I...> : index_sequence {}; - -namespace detail { - -template -constexpr auto to_array(T (&&arr)[N], index_sequence _) - -> std::array, N> { - return {{std::move(arr[I])...}}; -} - -} // namespace detail - -template -constexpr auto to_array(T (&&arr)[N]) -> std::array, N> { - return detail::to_array(std::move(arr), make_index_sequence()); -} - -template -JXL_INLINE T Clamp1(T val, T low, T hi) { - return val < low ? low : val > hi ? hi : val; -} - -// conversion from integer to string. -template -std::string ToString(T n) { - char data[32] = {}; - if (std::is_floating_point::value) { - // float - snprintf(data, sizeof(data), "%g", static_cast(n)); - } else if (std::is_unsigned::value) { - // unsigned - snprintf(data, sizeof(data), "%llu", static_cast(n)); - } else { - // signed - snprintf(data, sizeof(data), "%lld", static_cast(n)); - } - return data; -} - -#define JXL_JOIN(x, y) JXL_DO_JOIN(x, y) -#define JXL_DO_JOIN(x, y) x##y - -} // namespace jxl - -#endif // LIB_JXL_BASE_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/compiler_specific.h b/third_party/jpeg-xl/lib/jxl/base/compiler_specific.h deleted file mode 100644 index cb1a8a038641f..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/compiler_specific.h +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_COMPILER_SPECIFIC_H_ -#define LIB_JXL_BASE_COMPILER_SPECIFIC_H_ - -// Macros for compiler version + nonstandard keywords, e.g. __builtin_expect. - -#include - -#include "lib/jxl/base/sanitizer_definitions.h" - -#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER -#include "sanitizer/common_interface_defs.h" // __sanitizer_print_stack_trace -#endif // defined(*_SANITIZER) - -// #if is shorter and safer than #ifdef. *_VERSION are zero if not detected, -// otherwise 100 * major + minor version. Note that other packages check for -// #ifdef COMPILER_MSVC, so we cannot use that same name. - -#ifdef _MSC_VER -#define JXL_COMPILER_MSVC _MSC_VER -#else -#define JXL_COMPILER_MSVC 0 -#endif - -#ifdef __GNUC__ -#define JXL_COMPILER_GCC (__GNUC__ * 100 + __GNUC_MINOR__) -#else -#define JXL_COMPILER_GCC 0 -#endif - -#ifdef __clang__ -#define JXL_COMPILER_CLANG (__clang_major__ * 100 + __clang_minor__) -// Clang pretends to be GCC for compatibility. -#undef JXL_COMPILER_GCC -#define JXL_COMPILER_GCC 0 -#else -#define JXL_COMPILER_CLANG 0 -#endif - -#if JXL_COMPILER_MSVC -#define JXL_RESTRICT __restrict -#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG -#define JXL_RESTRICT __restrict__ -#else -#define JXL_RESTRICT -#endif - -#if JXL_COMPILER_MSVC -#define JXL_INLINE __forceinline -#define JXL_NOINLINE __declspec(noinline) -#else -#define JXL_INLINE inline __attribute__((always_inline)) -#define JXL_NOINLINE __attribute__((noinline)) -#endif - -#if JXL_COMPILER_MSVC -#define JXL_NORETURN __declspec(noreturn) -#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG -#define JXL_NORETURN __attribute__((noreturn)) -#else -#define JXL_NORETURN -#endif - -#if JXL_COMPILER_MSVC -#define JXL_MAYBE_UNUSED -#else -// Encountered "attribute list cannot appear here" when using the C++17 -// [[maybe_unused]], so only use the old style attribute for now. -#define JXL_MAYBE_UNUSED __attribute__((unused)) -#endif - -// MSAN execution won't hurt if some code it not inlined, but this can greatly -// improve compilation time. Unfortunately this macro can not be used just -// everywhere - inside header files it leads to "multiple definition" error; -// though it would be better not to have JXL_INLINE in header overall. -#if JXL_MEMORY_SANITIZER || JXL_ADDRESS_SANITIZER || JXL_THREAD_SANITIZER -#define JXL_MAYBE_INLINE JXL_MAYBE_UNUSED -#else -#define JXL_MAYBE_INLINE JXL_INLINE -#endif - -#if JXL_COMPILER_MSVC -// Unsupported, __assume is not the same. -#define JXL_LIKELY(expr) expr -#define JXL_UNLIKELY(expr) expr -#else -#define JXL_LIKELY(expr) __builtin_expect(!!(expr), 1) -#define JXL_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#endif - -#if JXL_COMPILER_MSVC -#include -using ssize_t = intptr_t; -#endif - -// Returns a void* pointer which the compiler then assumes is N-byte aligned. -// Example: float* JXL_RESTRICT aligned = (float*)JXL_ASSUME_ALIGNED(in, 32); -// -// The assignment semantics are required by GCC/Clang. ICC provides an in-place -// __assume_aligned, whereas MSVC's __assume appears unsuitable. -#if JXL_COMPILER_CLANG -// Early versions of Clang did not support __builtin_assume_aligned. -#define JXL_HAS_ASSUME_ALIGNED __has_builtin(__builtin_assume_aligned) -#elif JXL_COMPILER_GCC -#define JXL_HAS_ASSUME_ALIGNED 1 -#else -#define JXL_HAS_ASSUME_ALIGNED 0 -#endif - -#if JXL_HAS_ASSUME_ALIGNED -#define JXL_ASSUME_ALIGNED(ptr, align) __builtin_assume_aligned((ptr), (align)) -#else -#define JXL_ASSUME_ALIGNED(ptr, align) (ptr) /* not supported */ -#endif - -#ifdef __has_attribute -#define JXL_HAVE_ATTRIBUTE(x) __has_attribute(x) -#else -#define JXL_HAVE_ATTRIBUTE(x) 0 -#endif - -// Raises warnings if the function return value is unused. Should appear as the -// first part of a function definition/declaration. -#if JXL_HAVE_ATTRIBUTE(nodiscard) -#define JXL_MUST_USE_RESULT [[nodiscard]] -#elif JXL_COMPILER_CLANG && JXL_HAVE_ATTRIBUTE(warn_unused_result) -#define JXL_MUST_USE_RESULT __attribute__((warn_unused_result)) -#else -#define JXL_MUST_USE_RESULT -#endif - -// Disable certain -fsanitize flags for functions that are expected to include -// things like unsigned integer overflow. For example use in the function -// declaration JXL_NO_SANITIZE("unsigned-integer-overflow") to silence unsigned -// integer overflow ubsan messages. -#if JXL_COMPILER_CLANG && JXL_HAVE_ATTRIBUTE(no_sanitize) -#define JXL_NO_SANITIZE(X) __attribute__((no_sanitize(X))) -#else -#define JXL_NO_SANITIZE(X) -#endif - -#if JXL_HAVE_ATTRIBUTE(__format__) -#define JXL_FORMAT(idx_fmt, idx_arg) \ - __attribute__((__format__(__printf__, idx_fmt, idx_arg))) -#else -#define JXL_FORMAT(idx_fmt, idx_arg) -#endif - -// C++ standard. -#if defined(_MSC_VER) && !defined(__clang__) && defined(_MSVC_LANG) && \ - _MSVC_LANG > __cplusplus -#define JXL_CXX_LANG _MSVC_LANG -#else -#define JXL_CXX_LANG __cplusplus -#endif - -// Known / distinguished C++ standards. -#define JXL_CXX_17 201703 - -// In most cases we consider build as "debug". Use `NDEBUG` for release build. -#if defined(JXL_IS_DEBUG_BUILD) -#undef JXL_IS_DEBUG_BUILD -#define JXL_IS_DEBUG_BUILD 1 -#elif defined(NDEBUG) -#define JXL_IS_DEBUG_BUILD 0 -#else -#define JXL_IS_DEBUG_BUILD 1 -#endif - -#if defined(JXL_CRASH_ON_ERROR) -#undef JXL_CRASH_ON_ERROR -#define JXL_CRASH_ON_ERROR 1 -#else -#define JXL_CRASH_ON_ERROR 0 -#endif - -#if JXL_CRASH_ON_ERROR && !JXL_IS_DEBUG_BUILD -#error "JXL_CRASH_ON_ERROR requires JXL_IS_DEBUG_BUILD" -#endif - -// Pass -DJXL_DEBUG_ON_ALL_ERROR at compile time to print debug messages on -// all error (fatal and non-fatal) status. -#if defined(JXL_DEBUG_ON_ALL_ERROR) -#undef JXL_DEBUG_ON_ALL_ERROR -#define JXL_DEBUG_ON_ALL_ERROR 1 -#else -#define JXL_DEBUG_ON_ALL_ERROR 0 -#endif - -#if JXL_DEBUG_ON_ALL_ERROR && !JXL_IS_DEBUG_BUILD -#error "JXL_DEBUG_ON_ALL_ERROR requires JXL_IS_DEBUG_BUILD" -#endif - -// Pass -DJXL_DEBUG_ON_ABORT={0} to disable the debug messages on -// (debug) JXL_ENSURE and JXL_DASSERT. -#if !defined(JXL_DEBUG_ON_ABORT) -#define JXL_DEBUG_ON_ABORT JXL_IS_DEBUG_BUILD -#endif // JXL_DEBUG_ON_ABORT - -#if JXL_DEBUG_ON_ABORT && !JXL_IS_DEBUG_BUILD -#error "JXL_DEBUG_ON_ABORT requires JXL_IS_DEBUG_BUILD" -#endif - -#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER -#define JXL_PRINT_STACK_TRACE() __sanitizer_print_stack_trace(); -#else -#define JXL_PRINT_STACK_TRACE() -#endif - -#if JXL_COMPILER_MSVC -#define JXL_CRASH() __debugbreak(), (void)abort() -#else -#define JXL_CRASH() (void)__builtin_trap() -#endif - -#endif // LIB_JXL_BASE_COMPILER_SPECIFIC_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/data_parallel.h b/third_party/jpeg-xl/lib/jxl/base/data_parallel.h deleted file mode 100644 index e39f46c75b929..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/data_parallel.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_DATA_PARALLEL_H_ -#define LIB_JXL_BASE_DATA_PARALLEL_H_ - -// Portable, low-overhead C++11 ThreadPool alternative to OpenMP for -// data-parallel computations. - -#include - -#include -#include -#include - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" -#if JXL_COMPILER_MSVC -// suppress warnings about the const & applied to function types -#pragma warning(disable : 4180) -#endif - -namespace jxl { - -class ThreadPool { - public: - ThreadPool(JxlParallelRunner runner, void* runner_opaque) - : runner_(runner), - runner_opaque_(runner ? runner_opaque : static_cast(this)) {} - - ThreadPool(const ThreadPool&) = delete; - ThreadPool& operator&(const ThreadPool&) = delete; - - JxlParallelRunner runner() const { return runner_; } - void* runner_opaque() const { return runner_opaque_; } - - // Runs init_func(num_threads) followed by data_func(task, thread) on worker - // thread(s) for every task in [begin, end). init_func() must return a Status - // indicating whether the initialization succeeded. - // "thread" is an integer smaller than num_threads. - // Not thread-safe - no two calls to Run may overlap. - // Subsequent calls will reuse the same threads. - // - // Precondition: begin <= end. - template - Status Run(uint32_t begin, uint32_t end, const InitFunc& init_func, - const DataFunc& data_func, const char* caller) { - JXL_ENSURE(begin <= end); - if (begin == end) return true; - RunCallState call_state(init_func, data_func); - // The runner_ uses the C convention and returns 0 in case of error, so we - // convert it to a Status. - if (!runner_) { - void* jpegxl_opaque = static_cast(&call_state); - if (call_state.CallInitFunc(jpegxl_opaque, 1) != - JXL_PARALLEL_RET_SUCCESS) { - return JXL_FAILURE("Failed to initialize thread"); - } - for (uint32_t i = begin; i < end; i++) { - call_state.CallDataFunc(jpegxl_opaque, i, 0); - } - if (call_state.HasError()) { - return JXL_FAILURE("[%s] failed", caller); - } - return true; - } - JxlParallelRetCode ret = (*runner_)( - runner_opaque_, static_cast(&call_state), - &call_state.CallInitFunc, &call_state.CallDataFunc, begin, end); - - if (ret != JXL_PARALLEL_RET_SUCCESS || call_state.HasError()) { - return JXL_FAILURE("[%s] failed", caller); - } - return true; - } - - // Use this as init_func when no initialization is needed. - static Status NoInit(size_t num_threads) { return true; } - - private: - // class holding the state of a Run() call to pass to the runner_ as an - // opaque_jpegxl pointer. - template - class RunCallState final { - public: - RunCallState(const InitFunc& init_func, const DataFunc& data_func) - : init_func_(init_func), data_func_(data_func) {} - - // JxlParallelRunInit interface. - static int CallInitFunc(void* jpegxl_opaque, size_t num_threads) { - auto* self = - static_cast*>(jpegxl_opaque); - // Returns -1 when the internal init function returns false Status to - // indicate an error. - if (!self->init_func_(num_threads)) { - self->has_error_ = true; - return JXL_PARALLEL_RET_RUNNER_ERROR; - } - return JXL_PARALLEL_RET_SUCCESS; - } - - // JxlParallelRunFunction interface. - static void CallDataFunc(void* jpegxl_opaque, uint32_t value, - size_t thread_id) { - auto* self = - static_cast*>(jpegxl_opaque); - if (self->has_error_) return; - if (!self->data_func_(value, thread_id)) { - self->has_error_ = true; - } - } - - bool HasError() const { return has_error_; } - - private: - const InitFunc& init_func_; - const DataFunc& data_func_; - std::atomic has_error_{false}; - }; - - // The caller supplied runner function and its opaque void*. - const JxlParallelRunner runner_; - void* const runner_opaque_; -}; - -template -Status RunOnPool(ThreadPool* pool, const uint32_t begin, const uint32_t end, - const InitFunc& init_func, const DataFunc& data_func, - const char* caller) { - if (pool == nullptr) { - ThreadPool default_pool(nullptr, nullptr); - return default_pool.Run(begin, end, init_func, data_func, caller); - } else { - return pool->Run(begin, end, init_func, data_func, caller); - } -} - -} // namespace jxl -#if JXL_COMPILER_MSVC -#pragma warning(default : 4180) -#endif - -#endif // LIB_JXL_BASE_DATA_PARALLEL_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/exif.h b/third_party/jpeg-xl/lib/jxl/base/exif.h deleted file mode 100644 index acaa1a1ce4361..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/exif.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_EXIF_H_ -#define LIB_JXL_BASE_EXIF_H_ - -// Basic parsing of Exif (just enough for the render-impacting things -// like orientation) - -#include - -#include -#include -#include - -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" - -namespace jxl { - -constexpr uint16_t kExifOrientationTag = 274; - -// Checks if a blob looks like Exif, and if so, sets bigendian -// according to the tiff endianness -JXL_INLINE bool IsExif(const std::vector& exif, bool* bigendian) { - if (exif.size() < 12) return false; // not enough bytes for a valid exif blob - const uint8_t* t = exif.data(); - if (LoadLE32(t) == 0x2A004D4D) { - *bigendian = true; - return true; - } else if (LoadLE32(t) == 0x002A4949) { - *bigendian = false; - return true; - } - return false; // not a valid tiff header -} - -// Finds the position of an Exif tag, or 0 if it is not found -JXL_INLINE size_t FindExifTagPosition(const std::vector& exif, - uint16_t tagname) { - bool bigendian; - if (!IsExif(exif, &bigendian)) return 0; - const uint8_t* t = exif.data() + 4; - uint64_t offset = (bigendian ? LoadBE32(t) : LoadLE32(t)); - if (exif.size() < 12 + offset + 2 || offset < 8) return 0; - t += offset - 4; - if (offset + 2 >= exif.size()) return 0; - uint16_t nb_tags = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - while (nb_tags > 0) { - if (t + 12 >= exif.data() + exif.size()) return 0; - uint16_t tag = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - if (tag == tagname) return static_cast(t - exif.data()); - t += 10; - nb_tags--; - } - return 0; -} - -// TODO(jon): tag 1 can be used to represent Adobe RGB 1998 if it has value -// "R03" -// TODO(jon): set intrinsic dimensions according to -// https://discourse.wicg.io/t/proposal-exif-image-resolution-auto-and-from-image/4326/24 -// Parses the Exif data just enough to extract any render-impacting info. -// If the Exif data is invalid or could not be parsed, then it is treated -// as a no-op. -JXL_INLINE void InterpretExif(const std::vector& exif, - JxlOrientation* orientation) { - bool bigendian; - if (!IsExif(exif, &bigendian)) return; - size_t o_pos = FindExifTagPosition(exif, kExifOrientationTag); - if (o_pos) { - const uint8_t* t = exif.data() + o_pos; - uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 2; - uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t)); - t += 4; - uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t)); - if (type == 3 && count == 1 && value >= 1 && value <= 8) { - *orientation = static_cast(value); - } - } -} - -} // namespace jxl - -#endif // LIB_JXL_BASE_EXIF_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/fast_math-inl.h b/third_party/jpeg-xl/lib/jxl/base/fast_math-inl.h deleted file mode 100644 index fa749cc25703d..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/fast_math-inl.h +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Fast SIMD math ops (log2, encoder only, cos, erf for splines) - -#if defined(LIB_JXL_BASE_FAST_MATH_INL_H_) == defined(HWY_TARGET_TOGGLE) -#ifdef LIB_JXL_BASE_FAST_MATH_INL_H_ -#undef LIB_JXL_BASE_FAST_MATH_INL_H_ -#else -#define LIB_JXL_BASE_FAST_MATH_INL_H_ -#endif - -#include - -#include "lib/jxl/base/common.h" -#include "lib/jxl/base/rational_polynomial-inl.h" -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Abs; -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::Eq; -using hwy::HWY_NAMESPACE::Floor; -using hwy::HWY_NAMESPACE::Ge; -using hwy::HWY_NAMESPACE::GetLane; -using hwy::HWY_NAMESPACE::IfThenElse; -using hwy::HWY_NAMESPACE::IfThenZeroElse; -using hwy::HWY_NAMESPACE::Le; -using hwy::HWY_NAMESPACE::Min; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::NegMulAdd; -using hwy::HWY_NAMESPACE::Rebind; -using hwy::HWY_NAMESPACE::ShiftLeft; -using hwy::HWY_NAMESPACE::ShiftRight; -using hwy::HWY_NAMESPACE::Sub; -using hwy::HWY_NAMESPACE::Xor; - -// Computes base-2 logarithm like std::log2. Undefined if negative / NaN. -// L1 error ~3.9E-6 -template -V FastLog2f(const DF df, V x) { - // 2,2 rational polynomial approximation of std::log1p(x) / std::log(2). - HWY_ALIGN const float p[4 * (2 + 1)] = {HWY_REP4(-1.8503833400518310E-06f), - HWY_REP4(1.4287160470083755E+00f), - HWY_REP4(7.4245873327820566E-01f)}; - HWY_ALIGN const float q[4 * (2 + 1)] = {HWY_REP4(9.9032814277590719E-01f), - HWY_REP4(1.0096718572241148E+00f), - HWY_REP4(1.7409343003366853E-01f)}; - - const Rebind di; - const auto x_bits = BitCast(di, x); - - // Range reduction to [-1/3, 1/3] - 3 integer, 2 float ops - const auto exp_bits = Sub(x_bits, Set(di, 0x3f2aaaab)); // = 2/3 - // Shifted exponent = log2; also used to clear mantissa. - const auto exp_shifted = ShiftRight<23>(exp_bits); - const auto mantissa = BitCast(df, Sub(x_bits, ShiftLeft<23>(exp_shifted))); - const auto exp_val = ConvertTo(df, exp_shifted); - return Add(EvalRationalPolynomial(df, Sub(mantissa, Set(df, 1.0f)), p, q), - exp_val); -} - -// max relative error ~3e-7 -template -V FastPow2f(const DF df, V x) { - const Rebind di; - auto floorx = Floor(x); - auto exp = - BitCast(df, ShiftLeft<23>(Add(ConvertTo(di, floorx), Set(di, 127)))); - auto frac = Sub(x, floorx); - auto num = Add(frac, Set(df, 1.01749063e+01)); - num = MulAdd(num, frac, Set(df, 4.88687798e+01)); - num = MulAdd(num, frac, Set(df, 9.85506591e+01)); - num = Mul(num, exp); - auto den = MulAdd(frac, Set(df, 2.10242958e-01), Set(df, -2.22328856e-02)); - den = MulAdd(den, frac, Set(df, -1.94414990e+01)); - den = MulAdd(den, frac, Set(df, 9.85506633e+01)); - return Div(num, den); -} - -// max relative error ~3e-5 -template -V FastPowf(const DF df, V base, V exponent) { - return FastPow2f(df, Mul(FastLog2f(df, base), exponent)); -} - -// Computes cosine like std::cos. -// L1 error 7e-5. -template -V FastCosf(const DF df, V x) { - // Step 1: range reduction to [0, 2pi) - const auto pi2 = Set(df, kPi * 2.0f); - const auto pi2_inv = Set(df, 0.5f / kPi); - const auto npi2 = Mul(Floor(Mul(x, pi2_inv)), pi2); - const auto xmodpi2 = Sub(x, npi2); - // Step 2: range reduction to [0, pi] - const auto x_pi = Min(xmodpi2, Sub(pi2, xmodpi2)); - // Step 3: range reduction to [0, pi/2] - const auto above_pihalf = Ge(x_pi, Set(df, kPi / 2.0f)); - const auto x_pihalf = IfThenElse(above_pihalf, Sub(Set(df, kPi), x_pi), x_pi); - // Step 4: Taylor-like approximation, scaled by 2**0.75 to make angle - // duplication steps faster, on x/4. - const auto xs = Mul(x_pihalf, Set(df, 0.25f)); - const auto x2 = Mul(xs, xs); - const auto x4 = Mul(x2, x2); - const auto cosx_prescaling = - MulAdd(x4, Set(df, 0.06960438), - MulAdd(x2, Set(df, -0.84087373), Set(df, 1.68179268))); - // Step 5: angle duplication. - const auto cosx_scale1 = - MulAdd(cosx_prescaling, cosx_prescaling, Set(df, -1.414213562)); - const auto cosx_scale2 = MulAdd(cosx_scale1, cosx_scale1, Set(df, -1)); - // Step 6: change sign if needed. - const Rebind du; - auto signbit = ShiftLeft<31>(BitCast(du, VecFromMask(df, above_pihalf))); - return BitCast(df, Xor(signbit, BitCast(du, cosx_scale2))); -} - -// Computes the error function like std::erf. -// L1 error 7e-4. -template -V FastErff(const DF df, V x) { - // Formula from - // https://en.wikipedia.org/wiki/Error_function#Numerical_approximations - // but constants have been recomputed. - const auto xle0 = Le(x, Zero(df)); - const auto absx = Abs(x); - // Compute 1 - 1 / ((((x * a + b) * x + c) * x + d) * x + 1)**4 - const auto denom1 = - MulAdd(absx, Set(df, 7.77394369e-02), Set(df, 2.05260015e-04)); - const auto denom2 = MulAdd(denom1, absx, Set(df, 2.32120216e-01)); - const auto denom3 = MulAdd(denom2, absx, Set(df, 2.77820801e-01)); - const auto denom4 = MulAdd(denom3, absx, Set(df, 1.0f)); - const auto denom5 = Mul(denom4, denom4); - const auto inv_denom5 = Div(Set(df, 1.0f), denom5); - const auto result = NegMulAdd(inv_denom5, inv_denom5, Set(df, 1.0f)); - // Change sign if needed. - const Rebind du; - auto signbit = ShiftLeft<31>(BitCast(du, VecFromMask(df, xle0))); - return BitCast(df, Xor(signbit, BitCast(du, result))); -} - -inline float FastLog2f(float f) { - HWY_CAPPED(float, 1) D; - return GetLane(FastLog2f(D, Set(D, f))); -} - -inline float FastPow2f(float f) { - HWY_CAPPED(float, 1) D; - return GetLane(FastPow2f(D, Set(D, f))); -} - -inline float FastPowf(float b, float e) { - HWY_CAPPED(float, 1) D; - return GetLane(FastPowf(D, Set(D, b), Set(D, e))); -} - -inline float FastCosf(float f) { - HWY_CAPPED(float, 1) D; - return GetLane(FastCosf(D, Set(D, f))); -} - -inline float FastErff(float f) { - HWY_CAPPED(float, 1) D; - return GetLane(FastErff(D, Set(D, f))); -} - -// Returns cbrt(x) + add with 6 ulp max error. -// Modified from vectormath_exp.h, Apache 2 license. -// https://www.agner.org/optimize/vectorclass.zip -template -V CubeRootAndAdd(const V x, const V add) { - const HWY_FULL(float) df; - const HWY_FULL(int32_t) di; - - const auto kExpBias = Set(di, 0x54800000); // cast(1.) + cast(1.) / 3 - const auto kExpMul = Set(di, 0x002AAAAA); // shifted 1/3 - const auto k1_3 = Set(df, 1.0f / 3); - const auto k4_3 = Set(df, 4.0f / 3); - - const auto xa = x; // assume inputs never negative - const auto xa_3 = Mul(k1_3, xa); - - // Multiply exponent by -1/3 - const auto m1 = BitCast(di, xa); - // Special case for 0. 0 is represented with an exponent of 0, so the - // "kExpBias - 1/3 * exp" below gives the wrong result. The IfThenZeroElse() - // sets those values as 0, which prevents having NaNs in the computations - // below. - // TODO(eustas): use fused op - const auto m2 = IfThenZeroElse( - Eq(m1, Zero(di)), Sub(kExpBias, Mul((ShiftRight<23>(m1)), kExpMul))); - auto r = BitCast(df, m2); - - // Newton-Raphson iterations - for (int i = 0; i < 3; i++) { - const auto r2 = Mul(r, r); - r = NegMulAdd(xa_3, Mul(r2, r2), Mul(k4_3, r)); - } - // Final iteration - auto r2 = Mul(r, r); - r = MulAdd(k1_3, NegMulAdd(xa, Mul(r2, r2), r), r); - r2 = Mul(r, r); - r = MulAdd(r2, x, add); - - return r; -} - -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#endif // LIB_JXL_BASE_FAST_MATH_INL_H_ - -#if HWY_ONCE -#ifndef LIB_JXL_BASE_FAST_MATH_ONCE -#define LIB_JXL_BASE_FAST_MATH_ONCE - -namespace jxl { -inline float FastLog2f(float f) { return HWY_STATIC_DISPATCH(FastLog2f)(f); } -inline float FastPow2f(float f) { return HWY_STATIC_DISPATCH(FastPow2f)(f); } -inline float FastPowf(float b, float e) { - return HWY_STATIC_DISPATCH(FastPowf)(b, e); -} -inline float FastCosf(float f) { return HWY_STATIC_DISPATCH(FastCosf)(f); } -inline float FastErff(float f) { return HWY_STATIC_DISPATCH(FastErff)(f); } -} // namespace jxl - -#endif // LIB_JXL_BASE_FAST_MATH_ONCE -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl/base/float.h b/third_party/jpeg-xl/lib/jxl/base/float.h deleted file mode 100644 index 50af582b6f54a..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/float.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_FLOAT_H_ -#define LIB_JXL_BASE_FLOAT_H_ - -#include -#include -#include -#include - -#include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -namespace detail { -// Based on highway scalar implementation, for testing -static JXL_INLINE float LoadFloat16(uint16_t bits16) { - const uint32_t sign = bits16 >> 15; - const uint32_t biased_exp = (bits16 >> 10) & 0x1F; - const uint32_t mantissa = bits16 & 0x3FF; - - // Subnormal or zero - if (biased_exp == 0) { - const float subnormal = - (1.0f / 16384) * (static_cast(mantissa) * (1.0f / 1024)); - return sign ? -subnormal : subnormal; - } - - // Normalized: convert the representation directly (faster than ldexp/tables). - const uint32_t biased_exp32 = biased_exp + (127 - 15); - const uint32_t mantissa32 = mantissa << (23 - 10); - const uint32_t bits32 = (sign << 31) | (biased_exp32 << 23) | mantissa32; - - float result; - memcpy(&result, &bits32, 4); - return result; -} -} // namespace detail - -template -static Status JXL_INLINE LoadFloatRow(const uint8_t* src, size_t count, - size_t stride, JxlDataType type, - bool little_endian, float scale, - SaveFloatAtFn callback) { - switch (type) { - case JXL_TYPE_FLOAT: - if (little_endian) { - for (size_t i = 0; i < count; ++i) { - callback(i, LoadLEFloat(src + stride * i)); - } - } else { - for (size_t i = 0; i < count; ++i) { - callback(i, LoadBEFloat(src + stride * i)); - } - } - return true; - - case JXL_TYPE_UINT8: - for (size_t i = 0; i < count; ++i) { - callback(i, src[stride * i] * scale); - } - return true; - - case JXL_TYPE_UINT16: - if (little_endian) { - for (size_t i = 0; i < count; ++i) { - callback(i, LoadLE16(src + stride * i) * scale); - } - } else { - for (size_t i = 0; i < count; ++i) { - callback(i, LoadBE16(src + stride * i) * scale); - } - } - return true; - - case JXL_TYPE_FLOAT16: - if (little_endian) { - for (size_t i = 0; i < count; ++i) { - callback(i, detail::LoadFloat16(LoadLE16(src + stride * i))); - } - } else { - for (size_t i = 0; i < count; ++i) { - callback(i, detail::LoadFloat16(LoadBE16(src + stride * i))); - } - } - return true; - - default: - return JXL_FAILURE("Unsupported sample format"); - } -} - -} // namespace jxl - -#endif // LIB_JXL_BASE_FLOAT_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/iaca.h b/third_party/jpeg-xl/lib/jxl/base/iaca.h deleted file mode 100644 index e5732dae5c81c..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/iaca.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_IACA_H_ -#define LIB_JXL_BASE_IACA_H_ - -#include "lib/jxl/base/compiler_specific.h" - -// IACA (Intel's Code Analyzer) analyzes instruction latencies, but only for -// code between special markers. These functions embed such markers in an -// executable, but only for reading via IACA - they deliberately trigger a -// crash if executed to ensure they are removed in normal builds. - -#ifndef JXL_IACA_ENABLED -#define JXL_IACA_ENABLED 0 -#endif - -namespace jxl { - -// Call before the region of interest. -static JXL_INLINE void BeginIACA() { -#if JXL_IACA_ENABLED && (JXL_COMPILER_GCC || JXL_COMPILER_CLANG) - asm volatile( - // UD2 "instruction" raises an invalid opcode exception. - ".byte 0x0F, 0x0B\n\t" - // Magic sequence recognized by IACA (MOV + addr32 fs:NOP). This actually - // clobbers EBX, but we don't care because the code won't be run, and we - // want IACA to observe the same code the compiler would have generated - // without this marker. - "movl $111, %%ebx\n\t" - ".byte 0x64, 0x67, 0x90\n\t" - : - : - // (Allegedly) clobbering memory may prevent reordering. - : "memory"); -#endif -} - -// Call after the region of interest. -static JXL_INLINE void EndIACA() { -#if JXL_IACA_ENABLED && (JXL_COMPILER_GCC || JXL_COMPILER_CLANG) - asm volatile( - // See above. - "movl $222, %%ebx\n\t" - ".byte 0x64, 0x67, 0x90\n\t" - // UD2 - ".byte 0x0F, 0x0B\n\t" - : - : - // (Allegedly) clobbering memory may prevent reordering. - : "memory"); -#endif -} - -// Add to a scope to mark a region. -struct ScopeIACA { - JXL_INLINE ScopeIACA() { BeginIACA(); } - JXL_INLINE ~ScopeIACA() { EndIACA(); } -}; - -} // namespace jxl - -#endif // LIB_JXL_BASE_IACA_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/include_jpeglib.h b/third_party/jpeg-xl/lib/jxl/base/include_jpeglib.h deleted file mode 100644 index f72d13d04bad0..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/include_jpeglib.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_INCLUDE_JPEGLIB_H_ -#define LIB_JXL_BASE_INCLUDE_JPEGLIB_H_ - -// Using this header ensures that includes go in the right order, -// not alphabetically sorted. - -// NOLINTBEGIN -/* clang-format off */ -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -/* clang-format on */ -// NOLINTEND - -#endif // LIB_JXL_BASE_INCLUDE_JPEGLIB_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h b/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h deleted file mode 100644 index e1f8753932581..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_MATRIX_OPS_H_ -#define LIB_JXL_BASE_MATRIX_OPS_H_ - -// 3x3 matrix operations. - -#include -#include // abs -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { - -typedef std::array Vector3; -typedef std::array Vector3d; -typedef std::array Matrix3x3; -typedef std::array Matrix3x3d; - -// Computes C = A * B, where A, B, C are 3x3 matrices. -template -void Mul3x3Matrix(const Matrix& a, const Matrix& b, Matrix& c) { - for (size_t x = 0; x < 3; x++) { - alignas(16) Vector3d temp{b[0][x], b[1][x], b[2][x]}; // transpose - for (size_t y = 0; y < 3; y++) { - c[y][x] = a[y][0] * temp[0] + a[y][1] * temp[1] + a[y][2] * temp[2]; - } - } -} - -// Computes C = A * B, where A is 3x3 matrix and B is vector. -template -void Mul3x3Vector(const Matrix& a, const Vector& b, Vector& c) { - for (size_t y = 0; y < 3; y++) { - double e = 0; - for (size_t x = 0; x < 3; x++) { - e += a[y][x] * b[x]; - } - c[y] = e; - } -} - -// Inverts a 3x3 matrix in place. -template -Status Inv3x3Matrix(Matrix& matrix) { - // Intermediate computation is done in double precision. - Matrix3x3d temp; - temp[0][0] = static_cast(matrix[1][1]) * matrix[2][2] - - static_cast(matrix[1][2]) * matrix[2][1]; - temp[0][1] = static_cast(matrix[0][2]) * matrix[2][1] - - static_cast(matrix[0][1]) * matrix[2][2]; - temp[0][2] = static_cast(matrix[0][1]) * matrix[1][2] - - static_cast(matrix[0][2]) * matrix[1][1]; - temp[1][0] = static_cast(matrix[1][2]) * matrix[2][0] - - static_cast(matrix[1][0]) * matrix[2][2]; - temp[1][1] = static_cast(matrix[0][0]) * matrix[2][2] - - static_cast(matrix[0][2]) * matrix[2][0]; - temp[1][2] = static_cast(matrix[0][2]) * matrix[1][0] - - static_cast(matrix[0][0]) * matrix[1][2]; - temp[2][0] = static_cast(matrix[1][0]) * matrix[2][1] - - static_cast(matrix[1][1]) * matrix[2][0]; - temp[2][1] = static_cast(matrix[0][1]) * matrix[2][0] - - static_cast(matrix[0][0]) * matrix[2][1]; - temp[2][2] = static_cast(matrix[0][0]) * matrix[1][1] - - static_cast(matrix[0][1]) * matrix[1][0]; - double det = matrix[0][0] * temp[0][0] + matrix[0][1] * temp[1][0] + - matrix[0][2] * temp[2][0]; - if (std::abs(det) < 1e-10) { - return JXL_FAILURE("Matrix determinant is too close to 0"); - } - double idet = 1.0 / det; - for (size_t j = 0; j < 3; j++) { - for (size_t i = 0; i < 3; i++) { - matrix[j][i] = temp[j][i] * idet; - } - } - return true; -} - -} // namespace jxl - -#endif // LIB_JXL_BASE_MATRIX_OPS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/os_macros.h b/third_party/jpeg-xl/lib/jxl/base/os_macros.h deleted file mode 100644 index 84d0b82bf5c14..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/os_macros.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_OS_MACROS_H_ -#define LIB_JXL_BASE_OS_MACROS_H_ - -// Defines the JXL_OS_* macros. - -#if defined(_WIN32) || defined(_WIN64) -#define JXL_OS_WIN 1 -#else -#define JXL_OS_WIN 0 -#endif - -#ifdef __linux__ -#define JXL_OS_LINUX 1 -#else -#define JXL_OS_LINUX 0 -#endif - -#ifdef __APPLE__ -#define JXL_OS_MAC 1 -#else -#define JXL_OS_MAC 0 -#endif - -#define JXL_OS_IOS 0 -#ifdef __APPLE__ -#include -#if TARGET_OS_IPHONE -#undef JXL_OS_IOS -#define JXL_OS_IOS 1 -#endif -#endif - -#ifdef __FreeBSD__ -#define JXL_OS_FREEBSD 1 -#else -#define JXL_OS_FREEBSD 0 -#endif - -#ifdef __HAIKU__ -#define JXL_OS_HAIKU 1 -#else -#define JXL_OS_HAIKU 0 -#endif - -#endif // LIB_JXL_BASE_OS_MACROS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/override.h b/third_party/jpeg-xl/lib/jxl/base/override.h deleted file mode 100644 index da070e6e62430..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/override.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_OVERRIDE_H_ -#define LIB_JXL_BASE_OVERRIDE_H_ - -#include - -// 'Trool' for command line arguments: force enable/disable, or use default. - -namespace jxl { - -// No effect if kDefault, otherwise forces a feature (typically a FrameHeader -// flag) on or off. -enum class Override : int8_t { kOn = 1, kOff = 0, kDefault = -1 }; - -static inline Override OverrideFromBool(bool flag) { - return flag ? Override::kOn : Override::kOff; -} - -static inline bool ApplyOverride(Override o, bool default_condition) { - if (o == Override::kOn) return true; - if (o == Override::kOff) return false; - return default_condition; -} - -} // namespace jxl - -#endif // LIB_JXL_BASE_OVERRIDE_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/printf_macros.h b/third_party/jpeg-xl/lib/jxl/base/printf_macros.h deleted file mode 100644 index 3215052afdec4..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/printf_macros.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_PRINTF_MACROS_H_ -#define LIB_JXL_BASE_PRINTF_MACROS_H_ - -// Format string macros. These should be included after any other system -// library since those may unconditionally define these, depending on the -// platform. - -// PRIuS and PRIdS macros to print size_t and ssize_t respectively. -#if !defined(PRIdS) -#if defined(_WIN64) -#define PRIdS "lld" -#elif defined(_WIN32) -#define PRIdS "d" -#else -#define PRIdS "zd" -#endif -#endif // PRIdS - -#if !defined(PRIuS) -#if defined(_WIN64) -#define PRIuS "llu" -#elif defined(_WIN32) -#define PRIuS "u" -#else -#define PRIuS "zu" -#endif -#endif // PRIuS - -#endif // LIB_JXL_BASE_PRINTF_MACROS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/random.h b/third_party/jpeg-xl/lib/jxl/base/random.h deleted file mode 100644 index 6922e282e83bc..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/random.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_RANDOM_ -#define LIB_JXL_BASE_RANDOM_ - -// Random number generator + distributions. -// We don't use because the implementation (and thus results) differs -// between libstdc++ and libc++. - -#include -#include - -#include -#include - -#include "lib/jxl/base/status.h" - -namespace jxl { -struct Rng { - explicit Rng(uint64_t seed) - : s{static_cast(0x94D049BB133111EBull), - static_cast(0xBF58476D1CE4E5B9ull) + seed} {} - - // Xorshift128+ adapted from xorshift128+-inl.h - uint64_t operator()() { - uint64_t s1 = s[0]; - const uint64_t s0 = s[1]; - const uint64_t bits = s1 + s0; // b, c - s[0] = s0; - s1 ^= s1 << 23; - s1 ^= s0 ^ (s1 >> 18) ^ (s0 >> 5); - s[1] = s1; - return bits; - } - - // Uniformly distributed int64_t in [begin, end), under the assumption that - // `end-begin` is significantly smaller than 1<<64, otherwise there is some - // bias. - int64_t UniformI(int64_t begin, int64_t end) { - JXL_DASSERT(end > begin); - return static_cast((*this)() % - static_cast(end - begin)) + - begin; - } - - // Same as UniformI, but for uint64_t. - uint64_t UniformU(uint64_t begin, uint64_t end) { - JXL_DASSERT(end > begin); - return (*this)() % (end - begin) + begin; - } - - // Uniformly distributed float in [begin, end) range. Note: only 23 bits of - // randomness. - float UniformF(float begin, float end) { - float f; - // Bits of a random [1, 2) float. - uint32_t u = ((*this)() >> (64 - 23)) | 0x3F800000; - static_assert(sizeof(f) == sizeof(u), - "Float and U32 must have the same size"); - memcpy(&f, &u, sizeof(f)); - // Note: (end-begin) * f + (2*begin-end) may fail to return a number >= - // begin. - return (end - begin) * (f - 1.0f) + begin; - } - - // Bernoulli trial - bool Bernoulli(float p) { return UniformF(0, 1) < p; } - - // State for geometric distributions. - // The stored value is inv_log_1mp - using GeometricDistribution = float; - static GeometricDistribution MakeGeometric(float p) { - return 1.0 / std::log(1 - p); - } - - uint32_t Geometric(const GeometricDistribution& dist) { - float f = UniformF(0, 1); - float inv_log_1mp = dist; - float log = std::log(1 - f) * inv_log_1mp; - return static_cast(log); - } - - template - void Shuffle(T* t, size_t n) { - for (size_t i = 0; i + 1 < n; i++) { - size_t a = UniformU(i, n); - std::swap(t[a], t[i]); - } - } - - private: - uint64_t s[2]; -}; - -} // namespace jxl -#endif // LIB_JXL_BASE_RANDOM_ diff --git a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h b/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h deleted file mode 100644 index 7a89c0bac1e38..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Fast SIMD evaluation of rational polynomials for approximating functions. - -#if defined(LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_) == \ - defined(HWY_TARGET_TOGGLE) -#ifdef LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_ -#undef LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_ -#else -#define LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_ -#endif - -#include -#include - -#include -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { -namespace { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Div; -using hwy::HWY_NAMESPACE::MulAdd; - -// Primary template: default to actual division. -template -struct FastDivision { - HWY_INLINE V operator()(const V n, const V d) const { return n / d; } -}; -// Partial specialization for float vectors. -template -struct FastDivision { - // One Newton-Raphson iteration. - static HWY_INLINE V ReciprocalNR(const V x) { - const auto rcp = ApproximateReciprocal(x); - const auto sum = Add(rcp, rcp); - const auto x_rcp = Mul(x, rcp); - return NegMulAdd(x_rcp, rcp, sum); - } - - V operator()(const V n, const V d) const { -#if JXL_TRUE // Faster on SKX - return Div(n, d); -#else - return n * ReciprocalNR(d); -#endif - } -}; - -// Approximates smooth functions via rational polynomials (i.e. dividing two -// polynomials). Evaluates polynomials via Horner's scheme, which is faster than -// Clenshaw recurrence for Chebyshev polynomials. LoadDup128 allows us to -// specify constants (replicated 4x) independently of the lane count. -template -HWY_INLINE HWY_MAYBE_UNUSED V EvalRationalPolynomial(const D d, const V x, - const T (&p)[NP], - const T (&q)[NQ]) { - constexpr size_t kDegP = NP / 4 - 1; - constexpr size_t kDegQ = NQ / 4 - 1; - auto yp = LoadDup128(d, &p[kDegP * 4]); - auto yq = LoadDup128(d, &q[kDegQ * 4]); - // We use pointer arithmetic to refer to &p[(kDegP - n) * 4] to avoid a - // compiler warning that the index is out of bounds since we are already - // checking that it is not out of bounds with (kDegP >= n) and the access - // will be optimized away. Similarly with q and kDegQ. - HWY_FENCE; - if (kDegP >= 1) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 1) * 4))); - if (kDegQ >= 1) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 1) * 4))); - HWY_FENCE; - if (kDegP >= 2) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 2) * 4))); - if (kDegQ >= 2) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 2) * 4))); - HWY_FENCE; - if (kDegP >= 3) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 3) * 4))); - if (kDegQ >= 3) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 3) * 4))); - HWY_FENCE; - if (kDegP >= 4) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 4) * 4))); - if (kDegQ >= 4) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 4) * 4))); - HWY_FENCE; - if (kDegP >= 5) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 5) * 4))); - if (kDegQ >= 5) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 5) * 4))); - HWY_FENCE; - if (kDegP >= 6) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 6) * 4))); - if (kDegQ >= 6) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 6) * 4))); - HWY_FENCE; - if (kDegP >= 7) yp = MulAdd(yp, x, LoadDup128(d, p + ((kDegP - 7) * 4))); - if (kDegQ >= 7) yq = MulAdd(yq, x, LoadDup128(d, q + ((kDegQ - 7) * 4))); - - static_assert(kDegP < 8, "Polynomial degree is too high"); - static_assert(kDegQ < 8, "Polynomial degree is too high"); - - return FastDivision()(yp, yq); -} - -} // namespace -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); -#endif // LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/rect.h b/third_party/jpeg-xl/lib/jxl/base/rect.h deleted file mode 100644 index 31fe12dfb9529..0000000000000 --- a/third_party/jpeg-xl/lib/jxl/base/rect.h +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_BASE_RECT_H_ -#define LIB_JXL_BASE_RECT_H_ - -#include -#include -#include -#include -#include -#include -#include // std::move - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/status.h" - -namespace jxl { - -// Rectangular region in image(s). Factoring this out of Image instead of -// shifting the pointer by x0/y0 allows this to apply to multiple images with -// different resolutions (e.g. color transform and quantization field). -// Can compare using SameSize(rect1, rect2). -template -class RectT { - public: - // Most windows are xsize_max * ysize_max, except those on the borders where - // begin + size_max > end. - constexpr RectT(T xbegin, T ybegin, size_t xsize_max, size_t ysize_max, - T xend, T yend) - : x0_(xbegin), - y0_(ybegin), - xsize_(ClampedSize(xbegin, xsize_max, xend)), - ysize_(ClampedSize(ybegin, ysize_max, yend)) {} - - // Construct with origin and known size (typically from another Rect). - constexpr RectT(T xbegin, T ybegin, size_t xsize, size_t ysize) - : x0_(xbegin), y0_(ybegin), xsize_(xsize), ysize_(ysize) {} - - // Construct a rect that covers a whole image/plane/ImageBundle etc. - template - explicit RectT(const ImageT& image) - : RectT(0, 0, image.xsize(), image.ysize()) {} - - RectT() : RectT(0, 0, 0, 0) {} - - RectT(const RectT&) = default; - RectT& operator=(const RectT&) = default; - - // Construct a subrect that resides in an image/plane/ImageBundle etc. - template - RectT Crop(const ImageT& image) const { - return Intersection(RectT(image)); - } - - // Construct a subrect that resides in the [0, ysize) x [0, xsize) region of - // the current rect. - RectT Crop(size_t area_xsize, size_t area_ysize) const { - return Intersection(RectT(0, 0, area_xsize, area_ysize)); - } - - JXL_MUST_USE_RESULT RectT Intersection(const RectT& other) const { - return RectT(std::max(x0_, other.x0_), std::max(y0_, other.y0_), xsize_, - ysize_, std::min(x1(), other.x1()), - std::min(y1(), other.y1())); - } - - JXL_MUST_USE_RESULT RectT Translate(int64_t x_offset, - int64_t y_offset) const { - return RectT(x0_ + x_offset, y0_ + y_offset, xsize_, ysize_); - } - - template