diff --git a/build.log b/build.log new file mode 100644 index 0000000000..525d84e940 --- /dev/null +++ b/build.log @@ -0,0 +1,682 @@ + Compiling proc-macro2 v1.0.106 + Compiling unicode-ident v1.0.24 + Compiling quote v1.0.45 + Compiling libc v0.2.189 + Compiling cfg-if v1.0.4 + Compiling version_check v0.9.5 + Compiling typenum v1.20.1 + Compiling subtle v2.6.1 + Compiling smallvec v1.15.1 + Compiling stable_deref_trait v1.2.1 + Compiling libm v0.2.16 + Compiling serde_core v1.0.229 + Compiling autocfg v1.5.1 + Compiling shlex v2.0.1 + Compiling find-msvc-tools v0.1.12 + Compiling either v1.16.0 + Compiling generic-array v0.14.7 + Compiling num-traits v0.2.19 + Compiling portable-atomic v1.13.1 + Compiling syn v2.0.117 + Compiling syn v3.0.3 + Compiling memchr v2.8.1 + Compiling getrandom v0.2.17 + Compiling jobserver v0.1.34 + Compiling cc v1.4.5 + Compiling rand_core v0.6.4 + Compiling serde v1.0.229 + Compiling critical-section v1.2.0 + Compiling pin-project-lite v0.2.17 + Compiling once_cell v1.21.4 + Compiling log v0.4.34 + Compiling writeable v0.6.4 + Compiling rand_core v0.10.1 + Compiling getrandom v0.4.2 + Compiling bytes v1.12.1 + Compiling litemap v0.8.2 + Compiling zerocopy v0.8.50 + Compiling cmov v0.5.4 + Compiling allocator-api2 v0.2.21 + Compiling const-oid v0.9.6 + Compiling ctutils v0.4.2 + Compiling scopeguard v1.2.0 + Compiling parking_lot_core v0.9.12 + Compiling lock_api v0.4.14 + Compiling siphasher v1.0.3 + Compiling utf8_iter v1.0.4 + Compiling bitflags v2.12.1 + Compiling icu_properties_data v2.2.0 + Compiling icu_normalizer_data v2.2.0 + Compiling percent-encoding v2.3.2 + Compiling getrandom v0.3.4 + Compiling parking_lot v0.12.5 + Compiling equivalent v1.0.2 + Compiling form_urlencoded v1.2.2 + Compiling anyhow v1.0.104 + Compiling num-integer v0.1.46 + Compiling tracing-core v0.1.36 + Compiling itoa v1.0.18 + Compiling cpufeatures v0.2.17 + Compiling foldhash v0.2.0 + Compiling base64ct v1.8.3 + Compiling crossbeam-utils v0.8.21 + Compiling new_debug_unreachable v1.0.6 + Compiling const-oid v0.10.2 + Compiling futures-core v0.3.34 + Compiling ahash v0.8.12 + Compiling hashbrown v0.17.1 + Compiling errno v0.3.14 + Compiling pkg-config v0.3.33 + Compiling signal-hook-registry v1.4.8 + Compiling socket2 v0.6.5 + Compiling mio v1.2.1 + Compiling futures-sink v0.3.34 + Compiling synstructure v0.13.2 + Compiling indexmap v2.14.2 + Compiling futures-io v0.3.34 + Compiling slab v0.4.12 + Compiling futures-task v0.3.34 + Compiling unicode-width v0.2.2 + Compiling rustc-hash v2.1.2 + Compiling thiserror v2.0.18 + Compiling rand v0.8.7 + Compiling swc_macros_common v1.0.1 + Compiling phf_shared v0.11.3 + Compiling object v0.37.3 + Compiling heck v0.5.0 + Compiling scoped-tls v1.0.1 + Compiling better_scoped_tls v1.0.1 + Compiling swc_visit v2.0.1 + Compiling siphasher v0.3.11 + Compiling cpufeatures v0.3.0 + Compiling zmij v1.0.21 + Compiling phf_generator v0.11.3 + Compiling unicode-id-start v1.4.0 + Compiling zeroize_derive v1.4.3 + Compiling zerofrom-derive v0.1.7 + Compiling yoke-derive v0.8.2 + Compiling serde_derive v1.0.229 + Compiling displaydoc v0.2.6 + Compiling zerovec-derive v0.11.6 + Compiling zerocopy-derive v0.8.50 + Compiling zeroize v1.8.2 + Compiling hybrid-array v0.4.12 + Compiling tracing-attributes v0.1.31 + Compiling zerofrom v0.1.8 + Compiling crypto-common v0.1.7 + Compiling block-buffer v0.10.4 + Compiling yoke v0.8.3 + Compiling tokio-macros v2.7.0 + Compiling digest v0.10.7 + Compiling zerovec v0.11.8 + Compiling zerotrie v0.2.5 + Compiling crypto-common v0.2.2 + Compiling tokio v1.53.1 + Compiling tracing v0.1.44 + Compiling tinystr v0.8.4 + Compiling potential_utf v0.1.5 + Compiling icu_locale_core v2.3.0 + Compiling icu_collections v2.2.0 + Compiling futures-macro v0.3.34 + Compiling block-padding v0.3.3 + Compiling inout v0.1.4 + Compiling cipher v0.4.4 + Compiling block-buffer v0.12.0 + Compiling thiserror-impl v2.0.18 + Compiling phf_macros v0.11.3 + Compiling futures-util v0.3.34 + Compiling digest v0.11.3 + Compiling sha2 v0.10.9 + Compiling icu_provider v2.3.1 + Compiling num-bigint v0.4.6 + Compiling icu_properties v2.2.0 + Compiling icu_normalizer v2.2.0 + Compiling triomphe v0.1.15 + Compiling phf v0.11.3 + Compiling bytes-str v0.2.7 + Compiling ast_node v5.0.0 + Compiling idna_adapter v1.2.2 + Compiling hashbrown v0.14.5 + Compiling idna v1.1.0 + Compiling swc_eq_ignore_macros v1.0.1 + Compiling url v2.5.8 + Compiling hstr v3.0.6 + Compiling from_variant v3.0.0 + Compiling hmac v0.12.1 + Compiling is-macro v0.3.7 + Compiling string_enum v1.0.2 + Compiling swc_atoms v9.0.3 + Compiling semver v1.0.28 + Compiling ar_archive_writer v0.5.1 + Compiling swc_common v18.0.1 + Compiling smartstring v1.0.1 + Compiling serde_json v1.0.151 + Compiling password-hash v0.5.0 + Compiling pem-rfc7468 v0.7.0 + Compiling static_assertions v1.1.0 + Compiling base64 v0.22.1 + Compiling psm v0.1.32 + Compiling der v0.7.10 + Compiling pbkdf2 v0.12.2 + Compiling salsa20 v0.10.2 + Compiling aes v0.8.4 + Compiling stacker v0.1.25 + Compiling seq-macro v0.3.6 + Compiling scrypt v0.11.0 + Compiling cbc v0.1.2 + Compiling cmake v0.1.58 + Compiling par-core v2.0.0 + Compiling num_cpus v1.17.0 + Compiling ryu-js v1.0.3 + Compiling dunce v1.0.5 + Compiling swc_ecma_ast v19.0.0 + Compiling spki v0.7.3 + Compiling fs_extra v1.3.0 + Compiling pkcs5 v0.7.1 + Compiling tokio-util v0.7.18 + Compiling aws-lc-sys v0.45.0 + Compiling phf_shared v0.13.1 + Compiling crossbeam-epoch v0.9.20 + Compiling rustversion v1.0.22 + Compiling fastrand v2.4.1 + Compiling pkcs8 v0.10.2 + Compiling phf_generator v0.13.1 + Compiling perry-diagnostics v0.5.1593 (/root/claude-fix-10476/crates/perry-diagnostics) + Compiling ppv-lite86 v0.2.21 + Compiling rustls-pki-types v1.14.1 + Compiling der_derive v0.8.0 + Compiling pem-rfc7468 v1.0.0 + Compiling flagset v0.4.7 + Compiling ring v0.17.14 + Compiling tinyvec_macros v0.1.1 + Compiling der v0.8.2 + Compiling aws-lc-rs v1.18.1 + Compiling icu_locale_fallback_data v2.3.0 + Compiling perry-dispatch v0.5.1593 (/root/claude-fix-10476/crates/perry-dispatch) + Compiling tinyvec v1.11.0 + Compiling rustc_version v0.4.1 + Compiling ipnet v2.12.0 + Compiling simd-adler32 v0.3.9 + Compiling arrayvec v0.7.6 + Compiling chacha20 v0.10.0 + Compiling futures-channel v0.3.32 + Compiling swc_ecma_visit v19.0.0 + Compiling swc_ecma_parser v29.0.2 + Compiling swc_ecma_parser v32.0.0 + Compiling spki v0.8.0 + Compiling http v1.4.1 + Compiling untrusted v0.9.0 + Compiling crunchy v0.2.4 + Compiling cpubits v0.1.1 + Compiling rand v0.10.2 + Compiling icu_locale_fallback v2.3.0 + Compiling block-padding v0.4.2 + Compiling signature v2.2.0 + Compiling zstd-sys v2.0.16+zstd.1.5.7 + Compiling tiny-keccak v2.0.2 + Compiling data-encoding v2.11.0 + Compiling rayon-core v1.13.0 + Compiling icu_calendar_data v2.3.0 + Compiling rustls v0.23.44 + Compiling inout v0.2.2 + Compiling swc_ecma_utils v25.0.0 + Compiling crossbeam-deque v0.8.6 + Compiling phf_codegen v0.13.1 + Compiling rand_core v0.9.5 + Compiling ff v0.13.1 + Compiling core_maths v0.1.1 + Compiling httparse v1.10.1 + Compiling swc_ecma_transforms_base v32.0.0 + Compiling glob v0.3.3 + Compiling adler2 v2.0.1 + Compiling crc32fast v1.5.0 + Compiling base16ct v0.2.0 + Compiling sec1 v0.7.3 + Compiling clang-sys v1.8.1 + Compiling perry-parser v0.5.1593 (/root/claude-fix-10476/crates/perry-parser) + Compiling calendrical_calculations v0.2.4 + Compiling group v0.13.0 + Compiling rand_chacha v0.9.0 + Compiling cipher v0.5.2 + Compiling unicode-normalization v0.1.25 + Compiling prefix-trie v0.8.4 + Compiling encoding_rs v0.8.41 + Compiling multiversion-macros v0.9.0 + Compiling combine v4.6.8 + Compiling hkdf v0.12.4 + Compiling perry-api-manifest v0.5.1593 (/root/claude-fix-10476/crates/perry-api-manifest) + Compiling sha2 v0.11.0 + Compiling crypto-bigint v0.5.5 + Compiling equator-macro v0.4.2 + Compiling concurrent-queue v2.5.0 + Compiling crossbeam-channel v0.5.15 + Compiling libmimalloc-sys v0.1.49 + Compiling zstd-safe v7.2.4 + Compiling icu_plurals_data v2.3.0 + Compiling bytemuck v1.25.0 + Compiling icu_time_data v2.3.0 + Compiling parking v2.2.1 + Compiling spin v0.9.8 + Compiling num-conv v0.2.2 + Compiling time-core v0.1.9 + Compiling perry-ui-model v0.5.1593 (/root/claude-fix-10476/crates/perry-ui-model) + Compiling icu_decimal_data v2.3.0 + Compiling lazy_static v1.5.0 + Compiling perry-hir v0.5.1593 (/root/claude-fix-10476/crates/perry-hir) + Compiling time-macros v0.2.32 + Compiling event-listener v5.4.1 + Compiling const-random-macro v0.1.16 + Compiling equator v0.4.2 + Compiling elliptic-curve v0.13.8 + Compiling multiversion v0.9.0 + Compiling hickory-proto v0.26.2 + Compiling icu_calendar v2.3.0 + Compiling rand v0.9.4 + Compiling http-body v1.0.1 + Compiling curve25519-dalek v4.1.3 + Compiling webpki-roots v1.0.9 + Compiling phf_macros v0.13.1 + Compiling hmac v0.13.0 + Compiling fixed_decimal v0.7.2 + Compiling hashbrown v0.16.1 + Compiling slotmap v1.1.1 + Compiling core_detect v1.0.0 + Compiling icu_datetime_data v2.3.0 + Compiling multiversion_no_op v1.0.0 + Compiling ryu v1.0.23 + Compiling iana-time-zone v0.1.65 + Compiling minimal-lexical v0.2.1 + Compiling deranged v0.5.8 + Compiling regex-syntax v0.8.11 + Compiling simdutf8 v0.1.5 + Compiling powerfmt v0.2.0 + Compiling icu_locale_data v2.2.0 + Compiling thiserror v1.0.69 + Compiling time v0.3.55 + Compiling regex-automata v0.4.18 + Compiling nom v7.1.3 + Compiling chrono v0.4.45 + Compiling phf v0.13.1 + Compiling tzif v0.4.1 + Compiling aligned-vec v0.6.4 + Compiling const-random v0.1.18 + Compiling rayon v1.12.0 + Compiling miniz_oxide v0.9.1 + Compiling string_cache_codegen v0.6.1 + Compiling rfc6979 v0.4.0 + Compiling derive-syn-parse v0.2.0 + Compiling thiserror-impl v1.0.69 + Compiling curve25519-dalek-derive v0.1.1 + Compiling macro_magic_core_macros v0.5.1 + Compiling miette-derive v7.6.0 + Compiling libloading v0.8.9 + Compiling inotify-sys v0.1.5 + Compiling filetime v0.2.29 + Compiling keccak v0.2.0 + Compiling ident_case v1.0.1 + Compiling jiff-tzdb v0.1.6 + Compiling resolv-conf v0.7.6 + Compiling atomic-waker v1.1.2 + Compiling bitflags v1.3.2 + Compiling precomputed-hash v0.1.1 + Compiling fnv v1.0.7 + Compiling cty v0.2.2 + Compiling regex-lite v0.1.9 + Compiling try-lock v0.2.5 + Compiling tower-service v0.3.3 + Compiling unicode-width v0.1.14 + Compiling unicode-segmentation v1.13.3 + Compiling option-ext v0.2.0 + Compiling bindgen v0.72.1 + Compiling same-file v1.0.6 + Compiling httpdate v1.0.3 + Compiling strsim v0.11.1 + Compiling zlib-rs v0.6.4 + Compiling sha1_smol v1.0.1 + Compiling winnow v1.0.3 + Compiling hex v0.4.3 + Compiling llvm-sys v221.0.1 + Compiling darling_core v0.23.0 + Compiling toml_parser v1.1.3+spec-1.1.0 + Compiling perry-runtime v0.5.1593 (/root/claude-fix-10476/crates/perry-runtime) + Compiling convert_case v0.10.0 + Compiling walkdir v2.5.0 + Compiling flate2 v1.1.10 + Compiling miette v7.6.0 + Compiling dirs-sys v0.5.0 + Compiling itertools v0.12.1 + Compiling want v0.3.1 + Compiling h2 v0.4.19 + Compiling inotify v0.9.6 + Compiling timezone_provider v0.2.6 + Compiling zstd v0.13.3 + Compiling macro_magic_core v0.5.1 + Compiling ecdsa v0.16.9 + Compiling web_atoms v0.2.4 + Compiling icu_time v2.3.0 + Compiling icu_decimal v2.3.0 + Compiling v_frame v0.3.9 + Compiling icu_plurals v2.3.0 + Compiling cexpr v0.6.0 + Compiling regex v1.13.1 + Compiling hashlink v0.11.1 + Compiling primeorder v0.13.6 + Compiling atomic v0.6.1 + Compiling pkcs8 v0.11.0 + Compiling rand_chacha v0.3.1 + Compiling num-rational v0.4.2 + Compiling sha1 v0.11.0 + Compiling icu_pattern v0.5.0 + Compiling md-5 v0.10.6 + Compiling universal-hash v0.5.1 + Compiling serde_spanned v1.1.1 + Compiling toml_datetime v1.1.1+spec-1.1.0 + Compiling ff v0.14.0 + Compiling mio v0.8.11 + Compiling nom v8.0.0 + Compiling hostname v0.4.2 + Compiling as-slice v0.2.1 + Compiling unicode-xid v0.2.6 + Compiling num-bigint-dig v0.8.6 + Compiling rustix v1.1.4 + Compiling radium v0.7.0 + Compiling unicase v2.9.0 + Compiling ixdtf v0.6.5 + Compiling xxhash-rust v0.8.18 + Compiling toml_writer v1.1.2+spec-1.1.0 + Compiling built v0.8.1 + Compiling opaque-debug v0.3.1 + Compiling base16ct v1.0.0 + Compiling shlex v1.3.0 + Compiling av-scenechange v0.14.1 + Compiling crc-catalog v2.5.0 + Compiling paste v1.0.15 + Compiling byteorder v1.5.0 + Compiling perex v0.1.7 + Compiling bytecount v0.6.9 + Compiling node-semver v2.2.0 + Compiling rav1e v0.8.1 + Compiling crc v3.4.0 + Compiling sec1 v0.8.1 + Compiling toml v1.1.5+spec-1.1.0 + Compiling temporal_rs v0.2.6 + Compiling mime_guess v2.0.5 + Compiling derive_more-impl v2.1.1 + Compiling aligned v0.4.3 + Compiling notify v6.1.1 + Compiling group v0.14.0 + Compiling icu_datetime v2.3.0 + Compiling uuid v1.26.0 + Compiling hyper v1.11.1 + Compiling icu_locale v2.2.0 + Compiling taffy v0.13.0 + Compiling macro_magic_macros v0.5.1 + Compiling mimalloc v0.1.52 + Compiling dirs v6.0.0 + Compiling darling_macro v0.23.0 + Compiling string_cache v0.9.0 + Compiling ed25519 v2.2.3 + Compiling crypto-bigint v0.7.5 + Compiling x509-cert v0.3.0 + Compiling tokio-stream v0.1.18 + Compiling async-trait v0.1.92 + Compiling profiling-procmacros v1.0.18 + Compiling arg_enum_proc_macro v0.3.4 + Compiling crossbeam-queue v0.3.12 + Compiling futures-intrusive v0.5.0 + Compiling sync_wrapper v1.0.2 + Compiling num-iter v0.1.45 + Compiling no_std_io2 v0.9.4 + Compiling unicode-bidi v0.3.18 + Compiling mime v0.3.17 + Compiling dtoa v1.0.11 + Compiling utf-8 v0.7.6 + Compiling pastey v0.1.1 + Compiling tower-layer v0.3.3 + Compiling rustc-demangle v0.1.28 + Compiling tap v1.0.1 + Compiling y4m v0.8.0 + Compiling linux-raw-sys v0.12.1 + Compiling sponge-cursor v0.1.0 + Compiling unicode-properties v0.1.4 + Compiling vcpkg v0.2.15 + Compiling unsafe-libyaml v0.2.11 + Compiling quick-error v2.0.1 + Compiling gimli v0.34.0 + Compiling dotenvy v0.15.7 + Compiling libsqlite3-sys v0.37.0 + Compiling stringprep v0.1.5 + Compiling wyz v0.5.1 + Compiling tower v0.5.3 + Compiling tendril v0.5.0 + Compiling elliptic-curve v0.14.0 + Compiling dtoa-short v0.3.5 + Compiling sqlx-core v0.9.0 + Compiling bitstream-io v4.10.0 + Compiling profiling v1.0.18 + Compiling ed25519-dalek v2.2.0 + Compiling darling v0.23.0 + Compiling hyper-util v0.1.20 + Compiling derive_more v2.1.1 + Compiling av1-grain v0.2.5 + Compiling maybe-rayon v0.1.1 + Compiling pbkdf2 v0.13.0 + Compiling aes v0.9.1 + Compiling miniz_oxide v0.8.9 + Compiling selectors v0.38.0 + Compiling rustc_version_runtime v0.3.0 + Compiling pkcs1 v0.7.5 + Compiling half v2.7.1 + Compiling md-5 v0.11.0 + Compiling signature v3.0.0 + Compiling sha1 v0.10.6 + Compiling num-derive v0.4.2 + Compiling cssparser-macros v0.7.0 + Compiling aho-corasick v1.1.5 + Compiling simd_helpers v0.1.0 + Compiling itertools v0.14.0 + Compiling funty v2.0.0 + Compiling noop_proc_macro v0.3.0 + Compiling alloc-no-stdlib v2.0.4 + Compiling weezl v0.1.12 + Compiling zune-core v0.5.1 + Compiling utf8parse v0.2.2 + Compiling imgref v1.12.3 + Compiling tagptr v0.2.0 + Compiling loop9 v0.1.5 + Compiling moka v0.12.15 + Compiling anstyle-parse v1.0.0 + Compiling zune-jpeg v0.5.15 + Compiling alloc-stdlib v0.2.2 + Compiling bitvec v1.0.1 + Compiling cssparser v0.37.0 + Compiling ed448 v0.5.0 + Compiling rsa v0.9.10 + Compiling markup5ever v0.39.0 + Compiling serde_with_macros v3.21.0 + Compiling hash2curve v0.14.0 + Compiling tower-http v0.6.11 + Compiling shake v0.1.0 + Compiling hickory-net v0.26.1 + Compiling macro_magic v0.5.1 + Compiling polyval v0.6.2 + Compiling p256 v0.13.2 + Compiling p384 v0.13.1 + Compiling simple_asn1 v0.6.4 + Compiling serde_urlencoded v0.7.1 + Compiling hkdf v0.13.0 + Compiling webpki-roots v0.26.11 + Compiling http-body-util v0.1.5 + Compiling event-listener-strategy v0.5.4 + Compiling avif-serialize v0.8.9 + Compiling zune-inflate v0.2.54 + Compiling fdeflate v0.3.7 + Compiling pem v3.0.6 + Compiling dashmap v6.2.1 + Compiling universal-hash v0.6.1 + Compiling aead v0.5.2 + Compiling typed-builder-macro v0.22.0 + Compiling raw-cpuid v11.6.0 + Compiling serde_bytes v0.11.19 + Compiling atoi v2.0.0 + Compiling servo_arc v0.4.3 + Compiling color_quant v1.1.0 + Compiling pxfm v0.1.29 + Compiling is_terminal_polyfill v1.70.2 + Compiling base64 v0.23.1 + Compiling bit_field v0.10.3 + Compiling byteorder-lite v0.1.0 + Compiling anstyle v1.0.14 + Compiling inkwell v0.9.0 + Compiling lebe v0.5.3 + Compiling rust_decimal v1.43.0 + Compiling colorchoice v1.0.5 + Compiling rgb v0.8.53 + Compiling whoami v2.1.2 + Compiling anstyle-query v1.1.5 + Compiling fax v0.2.7 + Compiling anstream v1.0.0 + Compiling tiff v0.11.3 + Compiling sqlx-postgres v0.9.0 + Compiling moxcms v0.8.1 + Compiling ravif v0.13.0 + Compiling quanta v0.12.6 + Compiling exr v1.74.0 + Compiling image-webp v0.2.4 + Compiling typed-builder v0.22.0 + Compiling gif v0.14.2 + Compiling bson v3.1.0 + Compiling polyval v0.7.3 + Compiling png v0.18.1 + Compiling jsonwebtoken v11.0.0 + Compiling async-lock v3.4.2 + Compiling hickory-resolver v0.26.1 + Compiling ghash v0.5.1 + Compiling mongodb-internal-macros v3.9.0 + Compiling ed448-goldilocks v0.14.0-pre.15 + Compiling serde_with v3.21.0 + Compiling winresource v0.1.31 + Compiling html5ever v0.39.0 + Compiling brotli-decompressor v5.0.1 + Compiling sqlx-mysql v0.9.0 + Compiling blowfish v0.10.0 + Compiling poly1305 v0.8.0 + Compiling backon v1.6.0 + Compiling sha3 v0.11.0 + Compiling qoi v0.4.1 +warning: function `relevant_box_roots` is never used + --> crates/perry-runtime/src/box.rs:1020:4 + | +1020 | fn relevant_box_roots() -> Vec { + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + + Compiling arc-swap v1.9.1 + Compiling email-encoding v0.4.1 + Compiling ctr v0.9.2 + Compiling chacha20 v0.9.1 + Compiling kem v0.3.0 + Compiling module-lattice v0.2.3 + Compiling blake2 v0.10.6 + Compiling inkwell_internals v0.14.0 + Compiling derive-where v1.6.1 + Compiling console v0.16.4 + Compiling getopts v0.2.24 + Compiling num-bigint v0.5.1 + Compiling spinning_top v0.3.0 + Compiling perry-codegen v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen) + Compiling web-time v1.1.0 + Compiling nonzero_ext v0.3.0 + Compiling futures-timer v3.0.4 + Compiling winnow v0.7.15 + Compiling arcstr v1.2.0 + Compiling fallible-streaming-iterator v0.1.9 + Compiling email_address v0.2.9 + Compiling quoted_printable v0.5.2 + Compiling keccak v0.1.6 + Compiling bumpalo v3.20.3 + Compiling leb128fmt v0.1.0 + Compiling fallible-iterator v0.3.0 + Compiling take_mut v0.2.2 + Compiling openssl-probe v0.2.1 + Compiling perry-native-registration v0.5.1593 (/root/claude-fix-10476/crates/perry-native-registration) + Compiling libbz2-rs-sys v0.2.5 + Compiling clap_lex v1.1.0 + Compiling ego-tree v0.11.0 + Compiling clap_builder v4.6.6 + Compiling scraper v0.27.0 + Compiling cron v0.17.0 + Compiling bzip2 v0.6.1 + Compiling perry-ffi v0.5.1593 (/root/claude-fix-10476/crates/perry-ffi) + Compiling rustls-native-certs v0.8.4 + Compiling rusqlite v0.39.0 + Compiling zopfli v0.8.3 + Compiling wasm-encoder v0.258.0 + Compiling sha3 v0.10.9 + Compiling redis v1.7.0 + Compiling governor v0.10.4 + Compiling argon2 v0.5.3 + Compiling ml-kem v0.3.2 + Compiling sqlx v0.9.0 + Compiling chacha20poly1305 v0.10.1 + Compiling aes-gcm v0.10.3 + Compiling brotli v8.0.4 + Compiling image v0.25.10 + Compiling bcrypt v0.19.3 + Compiling perry v0.5.1593 (/root/claude-fix-10476/crates/perry) + Compiling x448 v0.14.0-pre.12 + Compiling ghash v0.6.0 + Compiling aes-kw v0.3.1 + Compiling xattr v1.6.1 + Compiling tempfile v3.27.0 + Compiling sha3 v0.12.0 + Compiling p521 v0.13.3 + Compiling x25519-dalek v2.0.1 + Compiling perry-codegen-js v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-js) + Compiling nanoid v0.5.0 + Compiling lzma-rust2 v0.16.4 + Compiling cbc v0.2.1 + Compiling ecb v0.2.1 + Compiling rustls-pemfile v2.2.0 + Compiling perry-validation v0.5.1593 (/root/claude-fix-10476/crates/perry-validation) + Compiling sha3-utils v0.5.0 + Compiling clap_derive v4.6.4 + Compiling lru v0.18.4 + Compiling is-terminal v0.4.17 + Compiling deflate64 v0.1.12 + Compiling ppmd-rust v1.4.0 + Compiling rustls-webpki v0.103.15 + Compiling constant_time_eq v0.4.2 + Compiling termcolor v1.4.1 + Compiling unit-prefix v0.5.2 + Compiling typed-path v0.12.3 + Compiling humantime v2.3.0 + Compiling shell-words v1.1.1 + Compiling env_logger v0.10.2 + Compiling dialoguer v0.12.0 + Compiling indicatif v0.18.6 + Compiling zip v8.6.0 + Compiling clap v4.6.6 + Compiling perry-codegen-wasm v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-wasm) + Compiling tar v0.4.46 + Compiling perry-perex v0.5.1593 (/root/claude-fix-10476/crates/perry-perex) + Compiling tokio-rustls v0.26.5 + Compiling hyper-rustls v0.27.9 + Compiling tungstenite v0.29.0 + Compiling reqwest v0.12.28 + Compiling mongodb v3.9.0 + Compiling tokio-tungstenite v0.29.0 + Compiling lettre v0.11.23 + Compiling perry-codegen-glance v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-glance) + Compiling perry-codegen-arkts v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-arkts) + Compiling perry-codegen-wear-tiles v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-wear-tiles) + Compiling perry-transform v0.5.1593 (/root/claude-fix-10476/crates/perry-transform) + Compiling perry-codegen-swiftui v0.5.1593 (/root/claude-fix-10476/crates/perry-codegen-swiftui) + Compiling fslock v0.2.1 +warning: `perry-runtime` (lib) generated 1 warning + Compiling perry-updater v0.5.1593 (/root/claude-fix-10476/crates/perry-updater) + Compiling perry-runtime-static v0.5.1593 (/root/claude-fix-10476/crates/perry-runtime-static) + Compiling perry-stdlib v0.5.1593 (/root/claude-fix-10476/crates/perry-stdlib) + Compiling perry-stdlib-static v0.5.1593 (/root/claude-fix-10476/crates/perry-stdlib-static) + Finished `perry-dev` profile [optimized] target(s) in 3m 47s diff --git a/changelog.d/10591-builtin-named-user-methods.md b/changelog.d/10591-builtin-named-user-methods.md new file mode 100644 index 0000000000..8be23081da --- /dev/null +++ b/changelog.d/10591-builtin-named-user-methods.md @@ -0,0 +1,25 @@ +Fix a call whose method name matched a `Date`/`Number`/`Array` builtin +(`getTime`, `toFixed`, `toISOString`, `toSorted`, `endsWith`, ...) being +lowered straight to that builtin regardless of the receiver. A class, +function-constructor prototype, or object literal defining a same-named +method — dayjs's `toISOString`/`toJSON`, decimal.js/bignumber.js's +`toFixed`, a plain `Clock.getTime()` — had its own method silently skipped +in favor of the builtin, producing `NaN`, `"[object Object]"`, `Invalid +Date`, or an uncaught `RangeError`. A zero-arg call of a user method sharing +a name with a required-arg String builtin (`endsWith`/`includes`/ +`startsWith`) didn't even compile (#10476). + +Add `builtin_kind_guard.rs`: a receiver the compiler has proven to be a +Date/number/array keeps the direct builtin call; any other receiver is +evaluated once, rooted, and branches at runtime on its actual kind to +either the builtin or the universal method dispatcher, which still reaches +the builtin via the prototype chain for a real Date/number/array. + +Known cost: a receiver whose kind is not statically provable now pays a +real runtime dispatch check to call a builtin-named method. On a synthetic +probe this puts two `any`-typed shapes (`dayjs`-like `toISOString`/`toJSON`, +a `Money`-like `toFixed`) at roughly 3-4x Node's wall time — well outside +the usual 20%-of-Node floor. The prior fast numbers for those two shapes +were never valid: the old code crashed on one and silently computed the +wrong answer on the other, so the comparison this fix is measured against +is fix-vs-Node, not fix-vs-old-Perry. diff --git a/crates/perry-codegen/src/expr/mod.rs b/crates/perry-codegen/src/expr/mod.rs index fae77a1dba..348b5de456 100644 --- a/crates/perry-codegen/src/expr/mod.rs +++ b/crates/perry-codegen/src/expr/mod.rs @@ -3048,7 +3048,7 @@ mod math_simple; mod misc_methods; mod new_dynamic; mod objects_arrays_lit; -mod os_uri_dates; +pub(crate) mod os_uri_dates; pub(crate) mod property_get; pub(crate) mod property_set; pub(crate) mod proxy_reflect; diff --git a/crates/perry-codegen/src/lower_array_method.rs b/crates/perry-codegen/src/lower_array_method.rs index 3b66f4cf99..9516b7e0fa 100644 --- a/crates/perry-codegen/src/lower_array_method.rs +++ b/crates/perry-codegen/src/lower_array_method.rs @@ -363,67 +363,13 @@ pub(crate) fn lower_array_method( if args.is_empty() { bail!("perry-codegen: Array.copyWithin expects 1-3 args, got 0",); } - let target_d = arg_vals[0].clone(); - let start_d = if args.len() >= 2 { - arg_vals[1].clone() - } else { - double_literal(0.0) - }; - let (has_end_str, end_d) = if args.len() >= 3 { - ("1".to_string(), arg_vals[2].clone()) - } else { - ("0".to_string(), "0.0".to_string()) - }; - let blk = ctx.block(); - let recv_handle = unbox_to_i64(blk, recv_box); - let result = blk.call( - I64, - "js_array_copy_within", - &[ - (I64, &recv_handle), - (DOUBLE, &target_d), - (DOUBLE, &start_d), - (I32, &has_end_str), - (DOUBLE, &end_d), - ], - ); - Ok(nanbox_pointer_inline(blk, &result)) - } - "flat" => { - // ECMA-262 §23.1.3.10 `arr.flat(depth?)`. Default depth = 1. - // The depth-aware path routes to `js_array_flat_depth` (handles - // 0 = shallow copy, Infinity = full recursion); 0-arg keeps - // the legacy `js_array_flat` fast path. - if args.is_empty() { - let blk = ctx.block(); - let recv_handle = unbox_to_i64(blk, recv_box); - let result = blk.call(I64, "js_array_flat", &[(I64, &recv_handle)]); - Ok(nanbox_pointer_inline(blk, &result)) - } else { - let blk = ctx.block(); - let recv_handle = unbox_to_i64(blk, recv_box); - let result = blk.call( - I64, - "js_array_flat_depth", - &[(I64, &recv_handle), (DOUBLE, &arg_vals[0])], - ); - Ok(nanbox_pointer_inline(blk, &result)) - } - } - "flatMap" => { - // 0-arg → runtime TypeError (pad undefined), not compile-fail. - let cb_box = arg_or_undefined(arg_vals, 0); - let blk = ctx.block(); - let recv_handle = unbox_to_i64(blk, recv_box); - // #4091: throw TypeError for a non-callable callback before iterating. - let cb_handle = blk.call(I64, "js_validate_array_callback", &[(DOUBLE, &cb_box)]); - let result = blk.call( - I64, - "js_array_flatMap", - &[(I64, &recv_handle), (I64, &cb_handle)], - ); - Ok(nanbox_pointer_inline(blk, &result)) + Ok(emit_array_method_on_values( + ctx, property, recv_box, arg_vals, + )) } + "flat" | "flatMap" => Ok(emit_array_method_on_values( + ctx, property, recv_box, arg_vals, + )), // -------- Safety-net handlers for methods that normally arrive -------- // as HIR variants but may reach here as generic MethodCall when // the HIR lowering doesn't recognize the pattern. @@ -553,28 +499,8 @@ pub(crate) fn lower_array_method( args.len() ); } - // 0-arg → runtime TypeError (callback validation on undefined), - // not compile-fail. - let cb_box = arg_or_undefined(arg_vals, 0); - let (has_initial, initial_box) = if args.len() == 2 { - (1i32, arg_vals[1].clone()) - } else { - (0i32, "0.0".to_string()) - }; - let blk = ctx.block(); - let recv_handle = unbox_to_i64(blk, recv_box); - // #4091: throw TypeError for a non-callable callback before iterating. - let cb_handle = blk.call(I64, "js_validate_array_callback", &[(DOUBLE, &cb_box)]); - let has_init_str = format!("{}", has_initial); - Ok(blk.call( - DOUBLE, - "js_array_reduce_right", - &[ - (I64, &recv_handle), - (I64, &cb_handle), - (I32, &has_init_str), - (DOUBLE, &initial_box), - ], + Ok(emit_array_method_on_values( + ctx, property, recv_box, arg_vals, )) } "map" => { @@ -1051,14 +977,17 @@ pub(crate) fn lower_array_method( // the array, not the joined locale string). Route through the runtime // dispatch tower, which walks elements and calls each element's own // `toLocaleString(locales, options)`. - // - // #2803 defensive: `toReversed` / `toSorted` / `toSpliced` normally fold - // to dedicated `Expr::ArrayTo*` nodes upstream, but if that fold ever - // bails for an `any`-typed receiver they would otherwise hit the - // receiver-returning catch-all below. Dispatching them dynamically here - // keeps the immutable-copy semantics (the runtime arms added in #2803). - "next" | "return" | "throw" | "toLocaleString" | "toReversed" | "toSorted" - | "toSpliced" => emit_native_method_dispatch(ctx, recv_box, property, arg_vals), + "next" | "return" | "throw" | "toLocaleString" => { + emit_native_method_dispatch(ctx, recv_box, property, arg_vals) + } + // #2803 / #10476: `toReversed` / `toSorted` / `toSpliced` fold to + // `Expr::ArrayTo*` upstream only for a receiver HIR proves is an + // Array (a method NAME is no proof). One this pass proves — e.g. an + // `any`-annotated binding of an array literal — arrives here and + // keeps the same direct helpers rather than paying for dispatch. + "toReversed" | "toSorted" | "toSpliced" => Ok(emit_array_method_on_values( + ctx, property, recv_box, arg_vals, + )), // #3148: TypedArray.prototype.set(source, offset?). Copies elements // from an Array/TypedArray source into this typed array. The runtime // helper no-ops for non-typed-array receivers, so it is safe under the @@ -1119,6 +1048,154 @@ pub(crate) fn lower_array_method( }) } +/// Whether [`emit_array_method_on_values`] lowers `property` called with +/// `argc` arguments without a compile-time arity error. +pub(crate) fn is_array_method_on_values(property: &str, argc: usize) -> bool { + match property { + "flat" | "flatMap" | "toReversed" | "toSorted" | "toSpliced" => true, + "reduceRight" => argc <= 2, + "copyWithin" => argc >= 1, + _ => false, + } +} + +/// The dense-Array lowering of the ES2019+ flatten, right-fold and copy +/// methods on an already-evaluated receiver and arguments — the runtime calls +/// the `Expr::Array*` folds make. Shared by the arms above and by the +/// plain-array arm of the receiver-kind guard (#10476). The receiver may be +/// a typed array or Buffer here too; each helper re-dispatches on that before +/// reading an ArrayHeader. Surplus arguments were evaluated and are ignored. +/// +/// Only names [`is_array_method_on_values`] accepts reach this. +pub(crate) fn emit_array_method_on_values( + ctx: &mut FnCtx<'_>, + property: &str, + recv_box: &str, + arg_vals: &[String], +) -> String { + let blk = ctx.block(); + let recv_handle = unbox_to_i64(blk, recv_box); + let result = match property { + // ECMA-262 §23.1.3.10 `arr.flat(depth?)`. Default depth = 1. The + // depth-aware path routes to `js_array_flat_depth` (handles 0 = shallow + // copy, Infinity = full recursion); 0-arg keeps the `js_array_flat` fast + // path. + "flat" => match arg_vals.first() { + None => blk.call(I64, "js_array_flat", &[(I64, &recv_handle)]), + Some(depth) => blk.call( + I64, + "js_array_flat_depth", + &[(I64, &recv_handle), (DOUBLE, depth)], + ), + }, + "flatMap" => { + // 0-arg → runtime TypeError (pad undefined), not compile-fail. + let cb_box = arg_or_undefined(arg_vals, 0); + // #4091: throw TypeError for a non-callable callback before iterating. + let cb_handle = blk.call(I64, "js_validate_array_callback", &[(DOUBLE, &cb_box)]); + blk.call( + I64, + "js_array_flatMap", + &[(I64, &recv_handle), (I64, &cb_handle)], + ) + } + "reduceRight" => { + // 0-arg → runtime TypeError (callback validation on undefined), + // not compile-fail. + let cb_box = arg_or_undefined(arg_vals, 0); + let (has_initial, initial_box) = match arg_vals.get(1) { + Some(initial) => ("1", initial.clone()), + None => ("0", "0.0".to_string()), + }; + // #4091: throw TypeError for a non-callable callback before iterating. + let cb_handle = blk.call(I64, "js_validate_array_callback", &[(DOUBLE, &cb_box)]); + return blk.call( + DOUBLE, + "js_array_reduce_right", + &[ + (I64, &recv_handle), + (I64, &cb_handle), + (I32, has_initial), + (DOUBLE, &initial_box), + ], + ); + } + "copyWithin" => { + let target = arg_or_undefined(arg_vals, 0); + let start = arg_vals + .get(1) + .cloned() + .unwrap_or_else(|| double_literal(0.0)); + let (has_end, end) = match arg_vals.get(2) { + Some(end) => ("1", end.clone()), + None => ("0", "0.0".to_string()), + }; + blk.call( + I64, + "js_array_copy_within", + &[ + (I64, &recv_handle), + (DOUBLE, &target), + (DOUBLE, &start), + (I32, has_end), + (DOUBLE, &end), + ], + ) + } + "toSorted" => match arg_vals.first() { + None => blk.call(I64, "js_array_to_sorted_default", &[(I64, &recv_handle)]), + Some(comparator) => { + // #2796: the comparator must be a function or undefined. + let cmp = blk.call(I64, "js_validate_array_comparator", &[(DOUBLE, comparator)]); + blk.call( + I64, + "js_array_to_sorted_with_comparator", + &[(I64, &recv_handle), (I64, &cmp)], + ) + } + }, + "toSpliced" => { + // #2794: 0 args → shallow copy (start 0, deleteCount 0); 1 arg → + // delete through the end (deleteCount +Infinity, clamped by the + // runtime); 2+ → explicit count, the rest are inserted items. + let start = arg_vals + .first() + .cloned() + .unwrap_or_else(|| double_literal(0.0)); + let delete_count = match (arg_vals.len(), arg_vals.get(1)) { + (_, Some(count)) => count.clone(), + (0, None) => double_literal(0.0), + (_, None) => double_literal(f64::INFINITY), + }; + let items = arg_vals.get(2..).unwrap_or(&[]); + let (items_ptr, items_len) = if items.is_empty() { + ("null".to_string(), "0".to_string()) + } else { + let buf = ctx.func.alloca_entry_array(DOUBLE, items.len()); + let blk = ctx.block(); + for (i, item) in items.iter().enumerate() { + let slot = blk.gep(DOUBLE, &buf, &[(I64, &i.to_string())]); + blk.store(DOUBLE, item, &slot); + } + (buf, items.len().to_string()) + }; + ctx.block().call( + I64, + "js_array_to_spliced", + &[ + (I64, &recv_handle), + (DOUBLE, &start), + (DOUBLE, &delete_count), + (PTR, &items_ptr), + (I32, &items_len), + ], + ) + } + _ => blk.call(I64, "js_array_to_reversed", &[(I64, &recv_handle)]), + }; + nanbox_pointer_inline(ctx.block(), &result) +} + /// `js_native_call_method(recv, name, name_len, argv, argc)` over already-lowered /// argument values. /// diff --git a/crates/perry-codegen/src/lower_call/property_get.rs b/crates/perry-codegen/src/lower_call/property_get.rs index 7fc22aa15f..dda1cccd8d 100644 --- a/crates/perry-codegen/src/lower_call/property_get.rs +++ b/crates/perry-codegen/src/lower_call/property_get.rs @@ -21,6 +21,7 @@ use crate::type_analysis::{ }; use crate::types::{DOUBLE, I1, I64}; +mod builtin_kind_guard; mod dynamic_dispatch; mod fetch_chain; mod helpers; @@ -219,6 +220,19 @@ pub fn try_lower_property_get_method_call( return Ok(Some(value)); } + // #10476: the same guard for Date / Number method names. HIR folds a + // Date intrinsic only for a proven Date and number_string.rs claims only + // a proven number; any other receiver checks its runtime kind here. + if let Some(value) = builtin_kind_guard::try_lower_kind_guarded_builtin_method( + ctx, + object, + property, + args, + call_byte_offset, + )? { + return Ok(Some(value)); + } + // Class instance method call (interface/dynamic dispatch tower + // static-fallback / virtual-override tower). if let Some(value) = dynamic_dispatch::try_lower_instance_method_call( diff --git a/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard.rs b/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard.rs new file mode 100644 index 0000000000..759dc7f952 --- /dev/null +++ b/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard.rs @@ -0,0 +1,484 @@ +//! Receiver-kind guards for Date / Number / Array builtin method names (#10476). +//! +//! A method NAME is not proof of its receiver's kind. dayjs and moment own +//! `toISOString`/`getTime`, decimal.js and bignumber.js own `toFixed`, and any +//! class may define `setHours`, `toPrecision` or `toSorted`. Lowering such a +//! call straight to `js_date_*` / `js_number_to_*` / `js_array_*` ran the +//! builtin on the user's object (`"NaN"`, `Invalid time value`, +//! `[object Object]`, `[]`) and never called the user's method. +//! +//! The direct builtin call is kept for a receiver the compiler has proven to be +//! a Date / number. Any other receiver without a known class is evaluated once +//! with its arguments, then its runtime kind selects the builtin or the +//! universal method dispatcher, which finds an own or inherited user method and +//! still reaches the builtin through the prototype for a Date, a number or an +//! array. Known class receivers are left to the class dispatch tower. + +use anyhow::Result; +use perry_hir::Expr; + +use crate::expr::{lower_expr, nanbox_string_inline, unbox_to_i64, FnCtx}; +use crate::rooting::{any_operand_may_collect, open_rooted_group, Repr}; +use crate::type_analysis::{ + is_array_expr, is_native_module_dynamic_index, is_numeric_expr, is_string_expr, + receiver_class_name, +}; +use crate::types::{DOUBLE, I1, I32, I64, I8, PTR}; + +use super::is_date_receiver; + +/// A Date method with a direct runtime entry point. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum DateBuiltin { + /// `double fn(double time)`. Accepts a Date or its time value. + Getter(&'static str), + /// `StringHeader* fn(double time)`. Accepts a Date or its time value. + Formatter(&'static str), + /// `double js_value_to_locale_string(date)`, the proven-Date + /// `Expr::DateToLocaleString` entry point. It dispatches on the value's + /// tag, so it needs the Date itself, never its time value. + LocaleString, + /// `double js_date_apply_setter(date, is_utc, field, args, argc)`. + Setter { is_utc: bool, field: i32 }, +} + +/// The receiver kind a guarded builtin requires. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ReceiverKind { + Date, + /// `toLocaleString()`: any primitive, Date or Symbol (see + /// [`emit_receiver_kind_branch`]). + LocaleValue, + Number, + Array, +} + +/// The runtime entry point the HIR `Expr::Date*` arms lower each name to. +/// `toLocale*String` only has a fast path without locale/options arguments; +/// with arguments they belong to the generic dispatcher's Intl thunks. +fn date_builtin(property: &str, argc: usize) -> Option { + use crate::expr::os_uri_dates::{ + DATE_FIELD_DATE, DATE_FIELD_FULL_YEAR, DATE_FIELD_HOURS, DATE_FIELD_MILLISECONDS, + DATE_FIELD_MINUTES, DATE_FIELD_MONTH, DATE_FIELD_SECONDS, DATE_FIELD_TIME, + }; + let getter = DateBuiltin::Getter; + let formatter = DateBuiltin::Formatter; + let local = |field| DateBuiltin::Setter { + is_utc: false, + field, + }; + let utc = |field| DateBuiltin::Setter { + is_utc: true, + field, + }; + Some(match property { + "getTime" => getter("js_date_get_time"), + "getTimezoneOffset" => getter("js_date_get_timezone_offset"), + "getFullYear" => getter("js_date_get_full_year"), + "getMonth" => getter("js_date_get_month"), + "getDate" => getter("js_date_get_date"), + "getDay" => getter("js_date_get_day"), + "getHours" => getter("js_date_get_hours"), + "getMinutes" => getter("js_date_get_minutes"), + "getSeconds" => getter("js_date_get_seconds"), + "getMilliseconds" => getter("js_date_get_milliseconds"), + "getUTCFullYear" => getter("js_date_get_utc_full_year"), + "getUTCMonth" => getter("js_date_get_utc_month"), + "getUTCDate" => getter("js_date_get_utc_date"), + "getUTCDay" => getter("js_date_get_utc_day"), + "getUTCHours" => getter("js_date_get_utc_hours"), + "getUTCMinutes" => getter("js_date_get_utc_minutes"), + "getUTCSeconds" => getter("js_date_get_utc_seconds"), + "getUTCMilliseconds" => getter("js_date_get_utc_milliseconds"), + "toISOString" => formatter("js_date_to_iso_string_or_throw"), + "toDateString" => formatter("js_date_to_date_string"), + "toTimeString" => formatter("js_date_to_time_string"), + "toUTCString" | "toGMTString" => formatter("js_date_to_utc_string"), + "toLocaleDateString" if argc == 0 => formatter("js_date_to_locale_date_string"), + "toLocaleTimeString" if argc == 0 => formatter("js_date_to_locale_time_string"), + "toLocaleString" if argc == 0 => DateBuiltin::LocaleString, + "setFullYear" => local(DATE_FIELD_FULL_YEAR), + "setMonth" => local(DATE_FIELD_MONTH), + "setDate" => local(DATE_FIELD_DATE), + "setHours" => local(DATE_FIELD_HOURS), + "setMinutes" => local(DATE_FIELD_MINUTES), + "setSeconds" => local(DATE_FIELD_SECONDS), + "setMilliseconds" => local(DATE_FIELD_MILLISECONDS), + "setTime" => local(DATE_FIELD_TIME), + "setUTCFullYear" => utc(DATE_FIELD_FULL_YEAR), + "setUTCMonth" => utc(DATE_FIELD_MONTH), + "setUTCDate" => utc(DATE_FIELD_DATE), + "setUTCHours" => utc(DATE_FIELD_HOURS), + "setUTCMinutes" => utc(DATE_FIELD_MINUTES), + "setUTCSeconds" => utc(DATE_FIELD_SECONDS), + "setUTCMilliseconds" => utc(DATE_FIELD_MILLISECONDS), + _ => return None, + }) +} + +/// `StringHeader* fn(double number, double arg)` for each Number method. +fn number_builtin(property: &str) -> Option<&'static str> { + match property { + "toFixed" => Some("js_number_to_fixed"), + "toPrecision" => Some("js_number_to_precision"), + "toExponential" => Some("js_number_to_exponential"), + _ => None, + } +} + +/// An unproven receiver that neither the String/Array lowering nor the class +/// dispatch tower owns. Module objects keep their own member dispatch. +/// +/// When any compiled class defines `property`, the receiver may be one of its +/// instances — including a `class X extends Date` override, whose instance is a +/// Date at runtime and would pass the kind check — so the class dispatch tower +/// keeps the call; its fallback is the same universal method dispatch. +fn receiver_is_unproven(ctx: &FnCtx<'_>, object: &Expr, property: &str) -> bool { + !is_string_expr(ctx, object) + && !is_array_expr(ctx, object) + && receiver_class_name(ctx, object).is_none() + && !matches!(object, Expr::GlobalGet(_) | Expr::NativeModuleRef(_)) + && !is_native_module_dynamic_index(object) + && !ctx.methods.keys().any(|(_, method)| method == property) +} + +/// Lower a Date, Number or Array builtin-named method call whose receiver was +/// not claimed by a proven fast path. Returns `Ok(None)` for other names and +/// for receivers the class dispatch tower owns. +pub(super) fn try_lower_kind_guarded_builtin_method( + ctx: &mut FnCtx<'_>, + object: &Expr, + property: &str, + args: &[Expr], + call_byte_offset: u32, +) -> Result> { + if let Some(date) = date_builtin(property, args.len()) { + let unproven_kind = if date == DateBuiltin::LocaleString { + ReceiverKind::LocaleValue + } else { + ReceiverKind::Date + }; + let guard = if is_date_receiver(ctx, object) { + None + } else if receiver_is_unproven(ctx, object, property) { + Some(unproven_kind) + } else { + return Ok(None); + }; + return guarded_call( + ctx, + object, + property, + args, + call_byte_offset, + guard, + |ctx, recv, time, arg_vals| emit_date_builtin(ctx, date, recv, time, arg_vals), + ) + .map(Some); + } + if let Some(runtime_fn) = number_builtin(property) { + // A proven number keeps number_string.rs's direct lowering. + if receiver_is_unproven(ctx, object, property) && !is_numeric_expr(ctx, object) { + return guarded_call( + ctx, + object, + property, + args, + call_byte_offset, + Some(ReceiverKind::Number), + |ctx, recv, _, arg_vals| emit_number_builtin(ctx, runtime_fn, recv, arg_vals), + ) + .map(Some); + } + } + // A proven array keeps `lower_array_method`, and HIR folds most. + if crate::lower_array_method::is_array_method_on_values(property, args.len()) + && receiver_is_unproven(ctx, object, property) + { + return guarded_call( + ctx, + object, + property, + args, + call_byte_offset, + Some(ReceiverKind::Array), + |ctx, recv, _, arg_vals| { + crate::lower_array_method::emit_array_method_on_values( + ctx, property, recv, arg_vals, + ) + }, + ) + .map(Some); + } + Ok(None) +} + +fn emit_number_builtin( + ctx: &mut FnCtx<'_>, + runtime_fn: &'static str, + recv: &str, + arg_vals: &[String], +) -> String { + let arg = arg_vals.first().cloned().unwrap_or_else(|| { + crate::nanbox::double_literal(f64::from_bits(crate::nanbox::TAG_UNDEFINED)) + }); + let blk = ctx.block(); + let handle = blk.call(I64, runtime_fn, &[(DOUBLE, recv), (DOUBLE, &arg)]); + nanbox_string_inline(blk, &handle) +} + +/// `time` is the Date's time value when the kind check already read it. +/// Getters and formatters take it in place of the Date, so the runtime does +/// not classify the receiver a second time; `getTime` is that value. +fn emit_date_builtin( + ctx: &mut FnCtx<'_>, + date: DateBuiltin, + recv: &str, + time: Option<&str>, + arg_vals: &[String], +) -> String { + match date { + DateBuiltin::Getter("js_date_get_time") if time.is_some() => { + time.unwrap_or(recv).to_string() + } + DateBuiltin::Getter(runtime_fn) => { + let date = time.unwrap_or(recv); + ctx.block().call(DOUBLE, runtime_fn, &[(DOUBLE, date)]) + } + DateBuiltin::Formatter(runtime_fn) => { + let date = time.unwrap_or(recv); + let blk = ctx.block(); + let handle = blk.call(I64, runtime_fn, &[(DOUBLE, date)]); + nanbox_string_inline(blk, &handle) + } + DateBuiltin::LocaleString => { + ctx.block() + .call(DOUBLE, "js_value_to_locale_string", &[(DOUBLE, recv)]) + } + DateBuiltin::Setter { is_utc, field } => { + // Entry-block buffer: an alloca in a loop body is a stack + // adjustment that is never restored (#167). + let (args_ptr, argc) = if arg_vals.is_empty() { + ("null".to_string(), "0".to_string()) + } else { + let buf = ctx.func.alloca_entry_array(DOUBLE, arg_vals.len()); + let blk = ctx.block(); + for (i, value) in arg_vals.iter().enumerate() { + let slot = blk.gep(DOUBLE, &buf, &[(I64, &i.to_string())]); + blk.store(DOUBLE, value, &slot); + } + (buf, arg_vals.len().to_string()) + }; + let is_utc = if is_utc { "1" } else { "0" }; + ctx.block().call( + DOUBLE, + "js_date_apply_setter", + &[ + (DOUBLE, recv), + (I32, is_utc), + (I32, &field.to_string()), + (PTR, &args_ptr), + (I32, &argc), + ], + ) + } + } +} + +/// Evaluate the receiver, then the arguments, once; then either call the +/// builtin directly (`guard == None`, a proven receiver) or branch on the +/// receiver's runtime kind between the builtin and universal method dispatch. +/// +/// The receiver is rooted across argument evaluation, and both are re-read +/// below it. The group is released in the merge block, below both consuming +/// calls (`open_rooted_group`'s diamond case). +fn guarded_call( + ctx: &mut FnCtx<'_>, + object: &Expr, + property: &str, + args: &[Expr], + call_byte_offset: u32, + guard: Option, + builtin: impl FnOnce(&mut FnCtx<'_>, &str, Option<&str>, &[String]) -> String, +) -> Result { + let mut group = open_rooted_group(args.len() + 1); + let recv_box = lower_expr(ctx, object)?; + let recv_collects = any_operand_may_collect(ctx, args.iter()); + let rooted_recv = group.adopt_emitted(ctx, Repr::Boxed, &recv_box, recv_collects); + for (i, arg) in args.iter().enumerate() { + let collects = any_operand_may_collect(ctx, args[i + 1..].iter()); + group.lower(ctx, arg, collects)?; + } + let recv = group.reread_emitted(ctx, rooted_recv); + let arg_vals = group.reread_all(ctx)?; + + let Some(kind) = guard else { + let value = builtin(ctx, &recv, None, &arg_vals); + group.release(ctx); + return Ok(value); + }; + + let builtin_idx = ctx.new_block("kindguard.builtin"); + let generic_idx = ctx.new_block("kindguard.generic"); + let merge_idx = ctx.new_block("kindguard.merge"); + let builtin_label = ctx.block_label(builtin_idx); + let generic_label = ctx.block_label(generic_idx); + let merge_label = ctx.block_label(merge_idx); + let time = emit_receiver_kind_branch(ctx, kind, &recv, &builtin_label, &generic_label); + + ctx.current_block = builtin_idx; + let builtin_value = builtin(ctx, &recv, time.as_deref(), &arg_vals); + let builtin_end = ctx.block().label.clone(); + ctx.block().br(&merge_label); + + ctx.current_block = generic_idx; + let generic_value = super::super::console_promise::emit_native_method_str_dispatch( + ctx, + property, + call_byte_offset, + &recv, + &arg_vals, + ); + let generic_end = ctx.block().label.clone(); + ctx.block().br(&merge_label); + + ctx.current_block = merge_idx; + let value = ctx.block().phi( + DOUBLE, + &[ + (builtin_value.as_str(), builtin_end.as_str()), + (generic_value.as_str(), generic_end.as_str()), + ], + ); + group.release(ctx); + Ok(value) +} + +/// Branch to `builtin_label` when the NaN-boxed `recv` has the kind the +/// builtin requires, else to `generic_label`. No check allocates or runs user +/// code. A Date check returns the Date's time value. +fn emit_receiver_kind_branch( + ctx: &mut FnCtx<'_>, + kind: ReceiverKind, + recv: &str, + builtin_label: &str, + generic_label: &str, +) -> Option { + match kind { + // A Date is a `DateCell` pointer, and only the runtime can tell one + // from any other pointer safely (off-heap buffers carry no GC header). + // `js_date_get_time` returns a Date's time value — a Number, which can + // never equal the POINTER-tagged bits of the cell holding it — and + // every other value bit for bit, so one call both classifies the + // receiver and reads the time value the builtin arm needs. + ReceiverKind::Date => { + let blk = ctx.block(); + let time = blk.call(DOUBLE, "js_date_get_time", &[(DOUBLE, recv)]); + let time_bits = blk.bitcast_double_to_i64(&time); + let recv_bits = blk.bitcast_double_to_i64(recv); + let is_date = blk.icmp_ne(I64, &time_bits, &recv_bits); + blk.cond_br(&is_date, builtin_label, generic_label); + Some(time) + } + // `toLocaleString()` without arguments is `Object.prototype`'s on every + // primitive, a Date's own, and a Symbol's inherited one: + // `js_value_to_locale_string` — the proven path's entry point — formats + // all of them. A heap object may own the method (a user class, dayjs), + // and a nullish receiver must throw the property read's TypeError, so + // both take method dispatch. + ReceiverKind::LocaleValue => { + let heap_or_nullish_idx = ctx.new_block("kindguard.locale_heap_or_nullish"); + let heap_idx = ctx.new_block("kindguard.locale_heap"); + let symbol_idx = ctx.new_block("kindguard.locale_symbol"); + let heap_or_nullish_label = ctx.block_label(heap_or_nullish_idx); + let heap_label = ctx.block_label(heap_idx); + let symbol_label = ctx.block_label(symbol_idx); + + let blk = ctx.block(); + let bits = blk.bitcast_double_to_i64(recv); + let tag = blk.lshr(I64, &bits, "48"); + let is_pointer = blk.icmp_eq(I64, &tag, crate::nanbox::POINTER_TAG_TOP16_I64); + let is_undefined = blk.icmp_eq(I64, &bits, crate::nanbox::TAG_UNDEFINED_I64); + let is_null = blk.icmp_eq(I64, &bits, crate::nanbox::TAG_NULL_I64); + let is_nullish = blk.or(I1, &is_undefined, &is_null); + let not_primitive = blk.or(I1, &is_pointer, &is_nullish); + blk.cond_br(¬_primitive, &heap_or_nullish_label, builtin_label); + + ctx.current_block = heap_or_nullish_idx; + ctx.block().cond_br(&is_pointer, &heap_label, generic_label); + + ctx.current_block = heap_idx; + let blk = ctx.block(); + let time = blk.call(DOUBLE, "js_date_get_time", &[(DOUBLE, recv)]); + let time_bits = blk.bitcast_double_to_i64(&time); + let is_date = blk.icmp_ne(I64, &time_bits, &bits); + blk.cond_br(&is_date, builtin_label, &symbol_label); + + ctx.current_block = symbol_idx; + let blk = ctx.block(); + let is_symbol = blk.call(I32, "js_is_symbol", &[(DOUBLE, recv)]); + let is_symbol = blk.icmp_ne(I32, &is_symbol, "0"); + blk.cond_br(&is_symbol, builtin_label, generic_label); + None + } + // A number is any IEEE double outside Perry's positive tag band + // 0x7FF9..=0x7FFF (`JSValue::is_number`), or an INT32-tagged value. + ReceiverKind::Number => { + let blk = ctx.block(); + let bits = blk.bitcast_double_to_i64(recv); + let tag = blk.lshr(I64, &bits, "48"); + let band_offset = blk.sub(I64, &tag, crate::nanbox::SHORT_STRING_TAG_TOP16_I64); + let in_tag_band = blk.icmp_ult(I64, &band_offset, "7"); + let is_double = blk.xor(I1, &in_tag_band, "true"); + let is_int32 = blk.icmp_eq(I64, &tag, crate::nanbox::INT32_TAG_TOP16_I64); + let is_number = blk.or(I1, &is_double, &is_int32); + blk.cond_br(&is_number, builtin_label, generic_label); + None + } + // A plain array: a POINTER-tagged heap address above the small-handle + // band whose GcHeader is `GC_TYPE_ARRAY` and not forwarded — the header + // test `expr/array_pop.rs` inlines. Lazy JSON arrays, Array subclass + // instances, typed arrays and proxies take method dispatch. (A + // header-less Buffer whose preceding byte happens to match reaches + // helpers that dispatch on Buffer / typed-array receivers first, the + // same helpers the unguarded pre-#10476 fold called on every receiver.) + ReceiverKind::Array => { + const POINTER_TAG_TOP16: &str = "32765"; // 0x7FFD + const HANDLE_BAND_TOP: &str = "1048575"; // 0x0FFFFF + const HEAP_LIMIT: &str = "140737488355328"; // 2^47 + const GC_TYPE_ARRAY_I8: &str = "1"; + const GC_FLAG_FORWARDED_I8: &str = "-128"; // 0x80 as i8 + let header_idx = ctx.new_block("kindguard.array_header"); + let header_label = ctx.block_label(header_idx); + let blk = ctx.block(); + let bits = blk.bitcast_double_to_i64(recv); + let tag = blk.lshr(I64, &bits, "48"); + let is_pointer = blk.icmp_eq(I64, &tag, POINTER_TAG_TOP16); + let handle = unbox_to_i64(blk, recv); + let above_band = blk.icmp_ugt(I64, &handle, HANDLE_BAND_TOP); + let below_limit = blk.icmp_ult(I64, &handle, HEAP_LIMIT); + let in_heap = blk.and(I1, &above_band, &below_limit); + let candidate = blk.and(I1, &is_pointer, &in_heap); + blk.cond_br(&candidate, &header_label, generic_label); + + ctx.current_block = header_idx; + let blk = ctx.block(); + let type_addr = blk.sub(I64, &handle, "8"); + let type_ptr = blk.inttoptr(I64, &type_addr); + let gc_type = blk.load(I8, &type_ptr); + let is_array = blk.icmp_eq(I8, &gc_type, GC_TYPE_ARRAY_I8); + let flags_addr = blk.sub(I64, &handle, "7"); + let flags_ptr = blk.inttoptr(I64, &flags_addr); + let flags = blk.load(I8, &flags_ptr); + let forwarded = blk.and(I8, &flags, GC_FLAG_FORWARDED_I8); + let not_forwarded = blk.icmp_eq(I8, &forwarded, "0"); + let plain_array = blk.and(I1, &is_array, ¬_forwarded); + blk.cond_br(&plain_array, builtin_label, generic_label); + None + } + } +} + +#[cfg(test)] +#[path = "builtin_kind_guard_tests.rs"] +mod tests; diff --git a/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard_tests.rs b/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard_tests.rs new file mode 100644 index 0000000000..9825f68ec2 --- /dev/null +++ b/crates/perry-codegen/src/lower_call/property_get/builtin_kind_guard_tests.rs @@ -0,0 +1,283 @@ +//! #10476: IR census for builtin-named Date / Number / Array method calls. +//! +//! The defect was a lowering choice, so the assertions are on the emitted +//! call, not on a predicate: a `getTime` / `setUTCHours` / `toFixed` / +//! `toSorted` call on an unproven receiver must reach universal method dispatch +//! on the non-builtin arm (where a user method lives), and a proven receiver +//! must keep the direct builtin call with no guard. + +use perry_hir::types::Type; +use perry_hir::{Expr, Stmt}; + +use crate::compile_module; +use crate::temp_root_coverage::{entry_opts, module_with_init}; + +const DISPATCH: &str = "call double @js_typed_feedback_native_call_method_by_id("; +const GET_TIME: &str = "call double @js_date_get_time("; + +/// The `main` body for `init`, with `make()` importable as an `any`-returning +/// function so its result is an unproven receiver. +fn main_ir(name: &str, init: Vec) -> String { + let mut opts = entry_opts(); + opts.import_function_prefixes + .insert("make".to_string(), "kind_guard_ts".to_string()); + let bytes = compile_module(&module_with_init(name, init), opts) + .unwrap_or_else(|e| panic!("codegen failed for {name}: {e}")); + let ir = String::from_utf8(bytes).expect("LLVM IR should be UTF-8"); + crate::testing::root_slots::function_slice(&ir, "main").to_string() +} + +fn any_value() -> Expr { + Expr::Call { + callee: Box::new(Expr::ExternFuncRef { + name: "make".to_string(), + param_types: Vec::new(), + return_type: Type::Any, + }), + args: Vec::new(), + type_args: Vec::new(), + byte_offset: 0, + } +} + +fn method_call(object: Expr, property: &str, args: Vec) -> Stmt { + Stmt::Expr(Expr::Call { + callee: Box::new(Expr::PropertyGet { + byte_offset: 0, + object: Box::new(object), + property: property.to_string(), + }), + args, + type_args: Vec::new(), + byte_offset: 0, + }) +} + +/// The register a `= call double @callee(` line assigns. +fn call_result(ir: &str, callee: &str) -> String { + let marker = format!(" = call double @{callee}("); + let line = ir + .lines() + .find(|line| line.contains(&marker)) + .unwrap_or_else(|| panic!("no call to {callee}:\n{ir}")); + line.trim_start() + .split(" = ") + .next() + .expect("assignment") + .to_string() +} + +#[test] +fn unproven_date_getter_checks_the_receiver_kind() { + let ir = main_ir( + "kind_guard_get_time.ts", + vec![method_call(any_value(), "getTime", Vec::new())], + ); + assert_eq!( + ir.matches(GET_TIME).count(), + 1, + "one js_date_get_time call both classifies the receiver and is getTime's value:\n{ir}" + ); + assert!( + ir.contains("icmp ne i64"), + "a Date is recognized by its time value differing from the receiver bits:\n{ir}" + ); + assert!( + ir.contains(DISPATCH), + "a non-Date receiver must reach its own getTime through method dispatch:\n{ir}" + ); + assert_eq!( + ir.matches("= call double @perry_fn_kind_guard_ts__make(") + .count(), + 1, + "the receiver must be evaluated exactly once:\n{ir}" + ); +} + +#[test] +fn unproven_date_getter_reuses_the_time_value_from_the_check() { + let ir = main_ir( + "kind_guard_utc_full_year.ts", + vec![method_call(any_value(), "getUTCFullYear", Vec::new())], + ); + let time = call_result(&ir, "js_date_get_time"); + assert!( + ir.contains(&format!( + "call double @js_date_get_utc_full_year(double {time})" + )), + "the getter must read the time value the check produced, not re-classify \ + the receiver:\n{ir}" + ); + assert!( + ir.contains(DISPATCH), + "missing the method-dispatch arm:\n{ir}" + ); +} + +#[test] +fn unproven_date_setter_forwards_every_argument_on_both_arms() { + let ir = main_ir( + "kind_guard_set_utc_hours.ts", + vec![method_call( + any_value(), + "setUTCHours", + vec![Expr::Number(1.0), Expr::Number(2.0)], + )], + ); + assert!( + ir.contains(GET_TIME) && ir.contains("call double @js_date_apply_setter("), + "a Date receiver must keep the setter behind a runtime Date check:\n{ir}" + ); + assert!( + ir.contains(DISPATCH), + "missing the method-dispatch arm:\n{ir}" + ); +} + +#[test] +fn unproven_to_locale_string_formats_primitives_dates_and_symbols_directly() { + let ir = main_ir( + "kind_guard_to_locale_string.ts", + vec![method_call(any_value(), "toLocaleString", Vec::new())], + ); + assert!( + ir.contains("call double @js_value_to_locale_string(") + && ir.contains(GET_TIME) + && ir.contains("call i32 @js_is_symbol(") + && ir.contains(DISPATCH), + "primitives, Dates and Symbols format directly; heap objects dispatch:\n{ir}" + ); +} + +#[test] +fn unproven_number_method_uses_an_inline_tag_check() { + let ir = main_ir( + "kind_guard_to_fixed.ts", + vec![method_call(any_value(), "toFixed", vec![Expr::Number(2.0)])], + ); + assert!( + ir.contains("call i64 @js_number_to_fixed(") && ir.contains(DISPATCH), + "toFixed on an unproven receiver needs both the Number and dispatch arms:\n{ir}" + ); + assert!( + !ir.contains(GET_TIME), + "the Number check is an inline tag test, not a runtime call:\n{ir}" + ); +} + +#[test] +fn unproven_to_sorted_checks_for_a_plain_array_header() { + let ir = main_ir( + "kind_guard_to_sorted.ts", + vec![method_call(any_value(), "toSorted", vec![Expr::Undefined])], + ); + assert!( + ir.contains("call i64 @js_validate_array_comparator(") + && ir.contains("call i64 @js_array_to_sorted_with_comparator(") + && ir.contains(DISPATCH), + "toSorted on an unproven receiver needs both the Array and dispatch arms:\n{ir}" + ); + assert!( + ir.contains("load i8"), + "the Array check reads the GcHeader type inline:\n{ir}" + ); +} + +#[test] +fn unproven_flatten_and_splice_methods_keep_the_dense_helpers_behind_the_guard() { + let ir = main_ir( + "kind_guard_flatten.ts", + vec![ + method_call(any_value(), "flat", Vec::new()), + method_call(any_value(), "flatMap", vec![Expr::Undefined]), + method_call(any_value(), "toSpliced", vec![Expr::Number(1.0)]), + ], + ); + for helper in [ + "call i64 @js_array_flat(", + "call i64 @js_array_flatMap(", + "call i64 @js_array_to_spliced(", + ] { + assert!( + ir.contains(helper), + "missing the plain-array arm {helper}:\n{ir}" + ); + } + assert_eq!( + ir.matches(DISPATCH).count(), + 3, + "every call keeps a method-dispatch arm for a user method:\n{ir}" + ); +} + +#[test] +fn unproven_reduce_right_with_three_arguments_is_plain_dispatch() { + // Array.prototype.reduceRight's static lowering rejects three arguments, + // but a user method may accept them: no guard, no compile error. + let ir = main_ir( + "kind_guard_reduce_right_arity.ts", + vec![method_call( + any_value(), + "reduceRight", + vec![Expr::Undefined, Expr::Undefined, Expr::Undefined], + )], + ); + assert!( + ir.contains(DISPATCH) && !ir.contains("call double @js_array_reduce_right("), + "an out-of-arity reduceRight must stay a method call:\n{ir}" + ); +} + +#[test] +fn proven_receivers_keep_the_direct_builtin_call() { + let ir = main_ir( + "kind_guard_proven.ts", + vec![ + Stmt::Let { + id: 1, + name: "n".to_string(), + ty: Type::Number, + mutable: false, + init: Some(Expr::Number(1.25)), + }, + method_call(Expr::LocalGet(1), "toFixed", vec![Expr::Number(1.0)]), + method_call(Expr::DateNew(Vec::new()), "getTime", Vec::new()), + ], + ); + assert!( + ir.contains("call i64 @js_number_to_fixed(") + && ir.contains("call double @js_date_get_time("), + "proven receivers must use the builtin directly:\n{ir}" + ); + assert_eq!( + ir.matches(GET_TIME).count(), + 1, + "a proven Date calls the getter once, with no kind check:\n{ir}" + ); + assert!( + !ir.contains(DISPATCH), + "a proven receiver must not pay for method dispatch:\n{ir}" + ); +} + +#[test] +fn zero_argument_search_methods_compile_on_any_receiver_and_on_a_string() { + let ir = main_ir( + "kind_guard_zero_arg_search.ts", + vec![ + method_call(any_value(), "endsWith", Vec::new()), + method_call(any_value(), "includes", Vec::new()), + method_call( + Expr::String("xundefined".to_string()), + "startsWith", + Vec::new(), + ), + ], + ); + assert!( + ir.contains("call i32 @js_string_ends_with(") + && ir.contains("call i32 @js_string_starts_with(") + && ir.contains(DISPATCH), + "an omitted searchString is `undefined`, not a compile error:\n{ir}" + ); +} diff --git a/crates/perry-codegen/src/lower_call/property_get/number_string.rs b/crates/perry-codegen/src/lower_call/property_get/number_string.rs index 93335ef115..c3ce1ef55f 100644 --- a/crates/perry-codegen/src/lower_call/property_get/number_string.rs +++ b/crates/perry-codegen/src/lower_call/property_get/number_string.rs @@ -9,8 +9,8 @@ use perry_hir::Expr; use crate::expr::{lower_expr, nanbox_string_inline, FnCtx}; use crate::nanbox::double_literal; use crate::type_analysis::{ - is_array_expr, is_native_module_dynamic_index, is_string_expr, - is_url_search_params_subclass_expr, receiver_class_name, + is_array_expr, is_numeric_expr, is_string_expr, is_url_search_params_subclass_expr, + receiver_class_name, }; use crate::types::{DOUBLE, I32, I64}; @@ -25,13 +25,10 @@ pub(crate) fn try_lower_number_string_methods( args: &[Expr], ) -> Result> { // Number.prototype.toFixed(decimals) — call js_number_to_fixed. - // Receiver is any number-typed value; we don't gate on - // is_numeric_expr because tests often call it on Any locals. - if property == "toFixed" - && !is_string_expr(ctx, object) - && !is_array_expr(ctx, object) - && !is_native_module_dynamic_index(object) - { + // #10476: only for a proven number. decimal.js / bignumber.js and any + // class own `toFixed` / `toPrecision` / `toExponential`; an unproven + // receiver checks its runtime kind in `builtin_kind_guard.rs`. + if property == "toFixed" && is_numeric_expr(ctx, object) { let v = lower_expr(ctx, object)?; let dec = if let Some(arg) = args.first() { lower_expr(ctx, arg)? @@ -43,11 +40,7 @@ pub(crate) fn try_lower_number_string_methods( return Ok(Some(nanbox_string_inline(blk, &handle))); } // Number.prototype.toPrecision(digits) - if property == "toPrecision" - && !is_string_expr(ctx, object) - && !is_array_expr(ctx, object) - && !is_native_module_dynamic_index(object) - { + if property == "toPrecision" && is_numeric_expr(ctx, object) { let v = lower_expr(ctx, object)?; let prec = if let Some(arg) = args.first() { lower_expr(ctx, arg)? @@ -63,11 +56,7 @@ pub(crate) fn try_lower_number_string_methods( return Ok(Some(nanbox_string_inline(blk, &handle))); } // Number.prototype.toExponential(decimals) - if property == "toExponential" - && !is_string_expr(ctx, object) - && !is_array_expr(ctx, object) - && !is_native_module_dynamic_index(object) - { + if property == "toExponential" && is_numeric_expr(ctx, object) { let v = lower_expr(ctx, object)?; let dec = if let Some(arg) = args.first() { lower_expr(ctx, arg)? diff --git a/crates/perry-codegen/src/lower_string_method.rs b/crates/perry-codegen/src/lower_string_method.rs index 7925272030..9c143a1df9 100644 --- a/crates/perry-codegen/src/lower_string_method.rs +++ b/crates/perry-codegen/src/lower_string_method.rs @@ -1054,14 +1054,16 @@ fn lower_string_method_dispatch( "startsWith" | "endsWith" => { // Spec allows the 2-arg form: startsWith(searchString, position) // and endsWith(searchString, endPosition). Closes #315. - if args.is_empty() { - bail!( - "perry-codegen: String.{} expects 1 or 2 args, got {}", - property, - args.len() - ); - } - let other_box = lower_expr(ctx, &args[0])?; + // #10476: an omitted searchString is `undefined`, which ToString + // turns into "undefined" (`"xundefined".endsWith()` is true). A + // compile-time arity error here also rejected the string arm of + // the tag guard for a user `endsWith()` on an `any` receiver. + let other_box = if args.is_empty() { + ctx.block() + .bitcast_i64_to_double(crate::nanbox::TAG_UNDEFINED_I64) + } else { + lower_expr(ctx, &args[0])? + }; let pos_d = if args.len() >= 2 { Some(lower_expr(ctx, &args[1])?) } else { @@ -1117,13 +1119,13 @@ fn lower_string_method_dispatch( // honored (search starts there), matching the dynamic dispatch // path. Negative/NaN clamp to 0 and Infinity saturates past the // end inside js_string_index_of_from. - if args.is_empty() { - bail!( - "perry-codegen: String.includes expects 1 or 2 args, got {}", - args.len() - ); - } - let needle_box = lower_expr(ctx, &args[0])?; + // #10476: an omitted searchString is `undefined` (see above). + let needle_box = if args.is_empty() { + ctx.block() + .bitcast_i64_to_double(crate::nanbox::TAG_UNDEFINED_I64) + } else { + lower_expr(ctx, &args[0])? + }; // Preserve evaluation of the second argument for side effects and // use it as the start index when present. let pos_d = if args.len() >= 2 { diff --git a/crates/perry-codegen/src/temp_root_coverage/mod.rs b/crates/perry-codegen/src/temp_root_coverage/mod.rs index fb7d55edf1..504c712101 100644 --- a/crates/perry-codegen/src/temp_root_coverage/mod.rs +++ b/crates/perry-codegen/src/temp_root_coverage/mod.rs @@ -106,7 +106,7 @@ pub(crate) fn entry_opts() -> CompileOptions { } } -fn module_with_init(name: &str, init: Vec) -> Module { +pub(crate) fn module_with_init(name: &str, init: Vec) -> Module { Module { name: name.to_string(), imports: Vec::new(), diff --git a/crates/perry-hir/src/lower/expr_call/array_only_methods.rs b/crates/perry-hir/src/lower/expr_call/array_only_methods.rs index 32b179ea71..09e4ee8597 100644 --- a/crates/perry-hir/src/lower/expr_call/array_only_methods.rs +++ b/crates/perry-hir/src/lower/expr_call/array_only_methods.rs @@ -698,17 +698,24 @@ pub(super) fn try_array_only_methods( // with positive Array evidence. This mirrors the bare-local // gate in `local_array_methods.rs` instead of reviving the old // any-receiver fallback for property/call receivers. - let recv_is_proven_array = matches!(method_name, "entries" | "keys" | "values") - && { - let recv_ty = crate::lower_types::infer_type_from_expr(&member.obj, ctx); - matches!(recv_ty, Type::Array(_) | Type::Tuple(_)) - || matches!( - &recv_ty, - Type::Generic { base, .. } - if base == "Array" || base == "ReadonlyArray" - ) - || chain_roots_at_array(ctx, &member.obj) - }; + // + // #10476: `reduceRight` / `toReversed` / `toSorted` are the same + // case — a user class, prototype or object literal owns them + // (a vector's `toSorted()`), and the dense fold read that + // object as an empty ArrayHeader, so the user method never ran. + let recv_is_proven_array = matches!( + method_name, + "entries" | "keys" | "values" | "reduceRight" | "toReversed" | "toSorted" + ) && { + let recv_ty = crate::lower_types::infer_type_from_expr(&member.obj, ctx); + matches!(recv_ty, Type::Array(_) | Type::Tuple(_)) + || matches!( + &recv_ty, + Type::Generic { base, .. } + if base == "Array" || base == "ReadonlyArray" + ) + || chain_roots_at_array(ctx, &member.obj) + }; // thisArg routing: the dense `Expr::Array` fast paths // carry only the callback and silently drop a 2nd positional // `thisArg` argument, so `[x].every(cb, thisArg)` ran the @@ -1174,7 +1181,7 @@ pub(super) fn try_array_only_methods( array: Box::new(array_expr), })); } - "reduceRight" if !args.is_empty() => { + "reduceRight" if !args.is_empty() && recv_is_proven_array => { let array_expr = lower_expr(ctx, &member.obj)?; let mut args_iter = args.into_iter(); let callback = args_iter.next().unwrap(); @@ -1185,13 +1192,13 @@ pub(super) fn try_array_only_methods( initial, })); } - "toReversed" => { + "toReversed" if recv_is_proven_array => { let array_expr = lower_expr(ctx, &member.obj)?; return Ok(Ok(Expr::ArrayToReversed { array: Box::new(array_expr), })); } - "toSorted" => { + "toSorted" if recv_is_proven_array => { let array_expr = lower_expr(ctx, &member.obj)?; let comparator = args.into_iter().next().map(Box::new); return Ok(Ok(Expr::ArrayToSorted { diff --git a/crates/perry-hir/src/lower/expr_call/builtin_named_user_methods_tests.rs b/crates/perry-hir/src/lower/expr_call/builtin_named_user_methods_tests.rs new file mode 100644 index 0000000000..5d803c1df9 --- /dev/null +++ b/crates/perry-hir/src/lower/expr_call/builtin_named_user_methods_tests.rs @@ -0,0 +1,154 @@ +//! #10476: verdict tests for builtin-named method calls in HIR lowering. +//! +//! A Date / Array method NAME is not proof of the receiver's kind: dayjs's +//! `Dayjs.prototype.toISOString`, a class's `getTime()` or `toSorted()`, an +//! object literal's `setHours`. These assert which lowering a call got — the +//! builtin intrinsic only for a statically proven receiver, a generic method +//! call otherwise (codegen then checks the runtime kind). + +#![cfg(test)] + +use crate::Module; +use perry_diagnostics::SourceCache; + +fn lower(src: &str) -> String { + let src = src.to_string(); + let module: Module = std::thread::Builder::new() + .stack_size(32 * 1024 * 1024) + .spawn(move || { + let mut cache = SourceCache::new(); + let parsed = perry_parser::parse_typescript_with_cache( + &src, + "builtin_named_user_methods.ts", + &mut cache, + ) + .expect("parse should succeed"); + crate::lower_module(&parsed.module, "test", "builtin_named_user_methods.ts") + .expect("lower should succeed") + }) + .expect("spawn lower thread") + .join() + .expect("lower thread panicked"); + format!("{module:?}") +} + +#[test] +fn date_names_on_unproven_receivers_stay_method_calls() { + let hir = lower( + r#" + function Dayjs(this: any, ms: number) { this.$ms = ms; } + Dayjs.prototype.toISOString = function () { return "x"; }; + Dayjs.prototype.toJSON = function (this: any) { return this.toISOString(); }; + class Clock { getTime() { return 1; } setHours(h: number) { return h; } } + const c = new Clock(); + const lit = { getFullYear() { return 2; }, toLocaleString() { return "l"; } }; + function viaAny(x: any) { + return [x.getTime(), x.setUTCHours(1), x.toDateString(), x.toLocaleTimeString()]; + } + console.log(c.getTime(), c.setHours(3), lit.getFullYear(), lit.toLocaleString(), viaAny(c)); + "#, + ); + for intrinsic in [ + "DateToISOString", + "DateGetTime", + "DateSetHours", + "DateGetFullYear", + "DateToLocaleString", + "DateSetUtcHours", + "DateToDateString", + "DateToLocaleTimeString", + ] { + assert!( + !hir.contains(intrinsic), + "an unproven receiver was lowered to the Date intrinsic {intrinsic}: {hir}" + ); + } +} + +#[test] +fn proven_date_receivers_keep_the_date_intrinsics() { + let hir = lower( + r#" + const d = new Date(0); + function takesDate(p: Date) { return p.getUTCHours(); } + console.log( + d.getTime(), d.toISOString(), d.setHours(1), new Date(5).getFullYear(), + takesDate(d), d.toLocaleString(), (12345).toLocaleString(), + ); + "#, + ); + for intrinsic in [ + "DateGetTime", + "DateToISOString", + "DateSetHours", + "DateGetFullYear", + "DateGetUtcHours", + "DateToLocaleString", + ] { + assert!( + hir.contains(intrinsic), + "a proven Date receiver lost the {intrinsic} intrinsic: {hir}" + ); + } +} + +#[test] +fn array_copy_names_on_unproven_receivers_stay_method_calls() { + let hir = lower( + r#" + const add = (a: number, b: number) => a + b; + class Vec { toSorted() { return this; } toReversed() { return this; } reduceRight(f: any) { return f; } } + const v = new Vec(); + const lit: any = { flat() { return 1; }, flatMap(f: any) { return f; }, toSpliced(i: number) { return i; } }; + const proto: any = Object.create({ toSorted() { return 0; }, copyWithin(a: number, b: number) { return a + b; } }); + function viaAny(x: any) { return [x.toSorted(), x.toReversed(), x.reduceRight(add)]; } + console.log( + v.toSorted(), v.toReversed(), v.reduceRight(add), lit.flat(), lit.flatMap(add), + lit.toSpliced(1), proto.toSorted(), proto.copyWithin(0, 1), viaAny(v), + ); + "#, + ); + for intrinsic in [ + "ArrayToSorted", + "ArrayToReversed", + "ArrayReduceRight", + "ArrayFlat {", + "ArrayFlatMap", + "ArrayToSpliced", + "ArrayCopyWithin", + ] { + assert!( + !hir.contains(intrinsic), + "an unproven receiver was lowered to the Array intrinsic {intrinsic}: {hir}" + ); + } +} + +#[test] +fn proven_array_receivers_keep_the_array_intrinsics() { + let hir = lower( + r#" + const add = (a: number, b: number) => a + b; + const arr: number[] = [3, 1, 2]; + const nested = [1, [2]]; + console.log( + arr.toSorted(), arr.toReversed(), arr.reduceRight(add), nested.flat(), + arr.flatMap((x) => [x]), arr.toSpliced(1), [4, 5].toSorted(), + arr.map((x) => x).toReversed(), + ); + "#, + ); + for intrinsic in [ + "ArrayToSorted", + "ArrayToReversed", + "ArrayReduceRight", + "ArrayFlat {", + "ArrayFlatMap", + "ArrayToSpliced", + ] { + assert!( + hir.contains(intrinsic), + "a proven Array receiver lost the {intrinsic} intrinsic: {hir}" + ); + } +} diff --git a/crates/perry-hir/src/lower/expr_call/local_array_methods.rs b/crates/perry-hir/src/lower/expr_call/local_array_methods.rs index 99a7807ca3..529c2e8a1d 100644 --- a/crates/perry-hir/src/lower/expr_call/local_array_methods.rs +++ b/crates/perry-hir/src/lower/expr_call/local_array_methods.rs @@ -216,6 +216,17 @@ pub(super) fn try_local_array_methods( | "pop" | "shift" | "unshift" + // #10476: the ES2019+/ES2023 copy and flatten names are + // just as often user methods (a vector type's + // `toSorted`, a tree's `flat`). An unproven receiver + // reaches them through dynamic dispatch, which still + // runs the Array helper for a real array. + | "flat" + | "flatMap" + | "toReversed" + | "toSorted" + | "toSpliced" + | "copyWithin" ); let is_unknown_recv = matches!(type_info, None | Some(Type::Any) | Some(Type::Unknown)); diff --git a/crates/perry-hir/src/lower/expr_call/mod.rs b/crates/perry-hir/src/lower/expr_call/mod.rs index 8af6aff797..8c9108ad0b 100644 --- a/crates/perry-hir/src/lower/expr_call/mod.rs +++ b/crates/perry-hir/src/lower/expr_call/mod.rs @@ -55,6 +55,8 @@ fn typed_array_lacks_array_method(name: &str) -> bool { mod array_only_methods; #[cfg(test)] mod array_only_methods_tests; +#[cfg(test)] +mod builtin_named_user_methods_tests; mod crypto; mod globals; mod imported_array_methods; diff --git a/crates/perry-hir/src/lower/expr_call/url_date_instance.rs b/crates/perry-hir/src/lower/expr_call/url_date_instance.rs index fd842b7a68..3893cca7a9 100644 --- a/crates/perry-hir/src/lower/expr_call/url_date_instance.rs +++ b/crates/perry-hir/src/lower/expr_call/url_date_instance.rs @@ -132,36 +132,20 @@ pub(super) fn try_url_date_weakref_instance( } } - // Issue #650: gate the AMBIGUOUS Date instance method arms - // on a receiver-type check. Methods like `toJSON` / - // `toString` / `toLocaleString` / `valueOf` exist on every - // JS object — pre-fix the arms below fired unconditionally, - // so calling any of them on a URL / class instance / array - // got silently rewritten as a Date method, returning a Date - // string for the URL.toJSON() case the issue tracks. + // Issue #650 / #10476: a Date method NAME is not proof that the + // receiver is a Date. Userland objects own `getTime`, `setHours`, + // `toISOString`, … (dayjs, moment and every timer/clock type), so + // the Date intrinsics below require a statically proven Date + // receiver. Every other receiver stays a generic method call; codegen + // keeps a fast path for one that is a Date at runtime by checking the + // evaluated receiver's kind before taking it + // (`property_get/builtin_kind_guard.rs`). let recv_class = if let ast::MemberProp::Ident(_) = &member.prop { static_receiver_class(ctx, member.obj.as_ref()) } else { None }; - // #809: `Some("Object")` (object literal / `Object.create`) - // joins URL as a "definitely not a Date" receiver. - let receiver_may_be_date = !matches!( - recv_class, - Some("URL") - | Some("Object") - | Some("Buffer") - | Some("BlockList") - | Some("SocketAddress") - | Some("Uint8Array") - | Some("Uint8ClampedArray") - | Some("Array") - ); - // Most ambiguous Date methods retain the historical "unknown may be - // Date" behavior. Direct `.toJSON()` is different: userland classes - // commonly expose it as a plain method, and bracket/computed forms - // already dispatch generically, so only statically-known Date - // receivers should use the Date intrinsic. + let receiver_is_date = recv_class == Some("Date"); // Check for Date instance method calls (date.getTime(), etc.) if let ast::MemberProp::Ident(method_ident) = &member.prop { @@ -196,36 +180,18 @@ pub(super) fn try_url_date_weakref_instance( } cached_recv = Some(recv_expr); } - let ambiguous = matches!( - method_name, - "toJSON" - | "toString" - | "toLocaleString" - | "toDateString" - | "toTimeString" - | "toUTCString" - | "toGMTString" - | "toLocaleDateString" - | "toLocaleTimeString" - | "toISOString" - | "valueOf" - ); - let allow_date_method = if method_name == "toJSON" { - recv_class == Some("Date") - } else { - receiver_may_be_date - }; - if method_name == "setTime" - && is_node_test_mock_timers_receiver(ctx, member.obj.as_ref()) - { - // `node:test` exposes `mock.timers.setTime(ms)`. The broad - // Date setter fallback below also matches `.setTime(...)` on - // unknown receivers, so keep this known non-Date receiver on - // the generic method-call path. - } else if ambiguous && !allow_date_method { - // Receiver is statically a non-Date class (e.g. URL), or this - // is `.toJSON()` on an unknown/userland receiver. - // Skip the Date arms below — fall through to generic. + // `(12345).toLocaleString()` shares the misnamed + // `DateToLocaleString` node: its codegen arm routes a numeric + // receiver to the Number formatter and anything else through the + // runtime's tag dispatch. + let number_to_locale_string = method_name == "toLocaleString" + && matches!( + crate::lower_types::infer_type_from_expr(&member.obj, ctx), + Type::Number | Type::Int32 + ); + if !receiver_is_date && !number_to_locale_string { + // Not a proven Date (including `node:test`'s + // `mock.timers.setTime(ms)`): generic method call. } else { match method_name { "getTime" => { @@ -454,7 +420,7 @@ pub(super) fn try_url_date_weakref_instance( } _ => {} // Fall through to other handling } - } // close `else` of `if ambiguous && !allow_ambiguous_date` + } // close `else` of `if !receiver_is_date && !number_to_locale_string` } // Check for WeakRef.deref() / FinalizationRegistry.register() / .unregister() @@ -579,16 +545,3 @@ pub(super) fn try_url_date_weakref_instance( } Ok(Err(args)) } - -fn is_node_test_mock_timers_receiver(ctx: &LoweringContext, expr: &ast::Expr) -> bool { - let ast::Expr::Member(inner) = expr else { - return false; - }; - if !matches!(&inner.prop, ast::MemberProp::Ident(prop) if prop.sym.as_ref() == "timers") { - return false; - } - let ast::Expr::Ident(root) = inner.obj.as_ref() else { - return false; - }; - ctx.lookup_imported_func(root.sym.as_ref()) == Some("mock") -} diff --git a/npm-ci.log b/npm-ci.log new file mode 100644 index 0000000000..5948b8e64a --- /dev/null +++ b/npm-ci.log @@ -0,0 +1,7 @@ +npm warn EBADENGINE Unsupported engine { +npm warn EBADENGINE package: '@socketsecurity/sdk@4.1.4', +npm warn EBADENGINE required: { node: '>=24', npm: '>=12.0.1', pnpm: '>=11.0.5' }, +npm warn EBADENGINE current: { node: 'v26.8.1', npm: '11.19.0' } +npm warn EBADENGINE } + +added 32 packages in 575ms diff --git a/test-files/test_gap_10476_builtin_named_guarded_receivers.ts b/test-files/test_gap_10476_builtin_named_guarded_receivers.ts new file mode 100644 index 0000000000..c7278b05c3 --- /dev/null +++ b/test-files/test_gap_10476_builtin_named_guarded_receivers.ts @@ -0,0 +1,608 @@ +// #10476: a user method whose NAME matches a Date / Number / Array / String +// built-in must run on a receiver that is not that built-in. Pre-fix such a +// call was lowered to the built-in by name alone (NaN, "[object Object]", +// "Invalid time value", or a compile error for a zero-argument +// `endsWith`/`includes`/`startsWith`). No class here declares these names, so +// an unproven receiver takes the compiler's runtime receiver-kind check: every +// name is defined on a function-constructor prototype, a replaced prototype +// object, an object literal and an Object.create() prototype, and called +// directly, via `this` and via `any`, with real Date / number / array / string +// values as controls at the same call sites. +// Generated by a one-off script: one call site per (form, name, arity). + +const cmp = (a: number, b: number) => b - a; +const add = (acc: number, v: number) => acc + v; +const dup = (v: number) => [v, v]; + +function tag(form: string, name: string, args: any[]): string { + return form + "." + name + "/" + args.length; +} + +function show(v: any): string { + if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return String(v); + if (Array.isArray(v)) return "[" + v.join(",") + "]"; + return v === null ? "null" : typeof v; +} + +// Error messages for a missing method differ between engines; the name is +// what matters here. +function attempt(f: () => any): string { + try { + return show(f()); + } catch (e) { + return "THREW " + (e as Error).name; + } +} + +function attemptMessage(f: () => any): string { + try { + return show(f()); + } catch (e) { + return "THREW " + String(e); + } +} + +function row(label: string, ...fs: Array<() => any>): void { + console.log(label, fs.map(attempt).join(" ")); +} + +function viaAny(x: any, id: number): any { + switch (id) { + case 0: return x.getDate(); + case 1: return x.getDay(); + case 2: return x.getFullYear(); + case 3: return x.getHours(); + case 4: return x.getMilliseconds(); + case 5: return x.getMinutes(); + case 6: return x.getMonth(); + case 7: return x.getSeconds(); + case 8: return x.getTime(); + case 9: return x.getTimezoneOffset(); + case 10: return x.getUTCDate(); + case 11: return x.getUTCDay(); + case 12: return x.getUTCFullYear(); + case 13: return x.getUTCHours(); + case 14: return x.getUTCMilliseconds(); + case 15: return x.getUTCMinutes(); + case 16: return x.getUTCMonth(); + case 17: return x.getUTCSeconds(); + case 18: return x.setDate(1); + case 19: return x.setFullYear(1); + case 20: return x.setHours(1); + case 21: return x.setMilliseconds(1); + case 22: return x.setMinutes(1); + case 23: return x.setMonth(1); + case 24: return x.setSeconds(1); + case 25: return x.setTime(1); + case 26: return x.setUTCDate(1); + case 27: return x.setUTCFullYear(1); + case 28: return x.setUTCHours(1); + case 29: return x.setUTCMilliseconds(1); + case 30: return x.setUTCMinutes(1); + case 31: return x.setUTCMonth(1); + case 32: return x.setUTCSeconds(1); + case 33: return x.toISOString(); + case 34: return x.toDateString(); + case 35: return x.toTimeString(); + case 36: return x.toUTCString(); + case 37: return x.toGMTString(); + case 38: return x.toLocaleString(); + case 39: return x.toLocaleDateString(); + case 40: return x.toLocaleTimeString(); + case 41: return x.toFixed(2); + case 42: return x.toPrecision(2); + case 43: return x.toExponential(2); + case 44: return x.toFixed(); + case 45: return x.toSorted(); + case 46: return x.toSorted(cmp); + case 47: return x.toReversed(); + case 48: return x.reduceRight(add); + case 49: return x.flat(); + case 50: return x.flatMap(dup); + case 51: return x.toSpliced(1); + case 52: return x.toSpliced(0, 1); + case 53: return x.copyWithin(0, 1); + case 54: return x.endsWith(); + case 55: return x.includes(); + case 56: return x.startsWith(); + case 57: return x.endsWith('c'); + case 58: return x.includes('b'); + case 59: return x.startsWith('a'); + } +} + +function F(this: any) { this.v = 1; } +F.prototype.getDate = function (...a: any[]) { return tag("F", "getDate", a); }; +F.prototype.getDay = function (...a: any[]) { return tag("F", "getDay", a); }; +F.prototype.getFullYear = function (...a: any[]) { return tag("F", "getFullYear", a); }; +F.prototype.getHours = function (...a: any[]) { return tag("F", "getHours", a); }; +F.prototype.getMilliseconds = function (...a: any[]) { return tag("F", "getMilliseconds", a); }; +F.prototype.getMinutes = function (...a: any[]) { return tag("F", "getMinutes", a); }; +F.prototype.getMonth = function (...a: any[]) { return tag("F", "getMonth", a); }; +F.prototype.getSeconds = function (...a: any[]) { return tag("F", "getSeconds", a); }; +F.prototype.getTime = function (...a: any[]) { return tag("F", "getTime", a); }; +F.prototype.getTimezoneOffset = function (...a: any[]) { return tag("F", "getTimezoneOffset", a); }; +F.prototype.getUTCDate = function (...a: any[]) { return tag("F", "getUTCDate", a); }; +F.prototype.getUTCDay = function (...a: any[]) { return tag("F", "getUTCDay", a); }; +F.prototype.getUTCFullYear = function (...a: any[]) { return tag("F", "getUTCFullYear", a); }; +F.prototype.getUTCHours = function (...a: any[]) { return tag("F", "getUTCHours", a); }; +F.prototype.getUTCMilliseconds = function (...a: any[]) { return tag("F", "getUTCMilliseconds", a); }; +F.prototype.getUTCMinutes = function (...a: any[]) { return tag("F", "getUTCMinutes", a); }; +F.prototype.getUTCMonth = function (...a: any[]) { return tag("F", "getUTCMonth", a); }; +F.prototype.getUTCSeconds = function (...a: any[]) { return tag("F", "getUTCSeconds", a); }; +F.prototype.setDate = function (...a: any[]) { return tag("F", "setDate", a); }; +F.prototype.setFullYear = function (...a: any[]) { return tag("F", "setFullYear", a); }; +F.prototype.setHours = function (...a: any[]) { return tag("F", "setHours", a); }; +F.prototype.setMilliseconds = function (...a: any[]) { return tag("F", "setMilliseconds", a); }; +F.prototype.setMinutes = function (...a: any[]) { return tag("F", "setMinutes", a); }; +F.prototype.setMonth = function (...a: any[]) { return tag("F", "setMonth", a); }; +F.prototype.setSeconds = function (...a: any[]) { return tag("F", "setSeconds", a); }; +F.prototype.setTime = function (...a: any[]) { return tag("F", "setTime", a); }; +F.prototype.setUTCDate = function (...a: any[]) { return tag("F", "setUTCDate", a); }; +F.prototype.setUTCFullYear = function (...a: any[]) { return tag("F", "setUTCFullYear", a); }; +F.prototype.setUTCHours = function (...a: any[]) { return tag("F", "setUTCHours", a); }; +F.prototype.setUTCMilliseconds = function (...a: any[]) { return tag("F", "setUTCMilliseconds", a); }; +F.prototype.setUTCMinutes = function (...a: any[]) { return tag("F", "setUTCMinutes", a); }; +F.prototype.setUTCMonth = function (...a: any[]) { return tag("F", "setUTCMonth", a); }; +F.prototype.setUTCSeconds = function (...a: any[]) { return tag("F", "setUTCSeconds", a); }; +F.prototype.toISOString = function (...a: any[]) { return tag("F", "toISOString", a); }; +F.prototype.toDateString = function (...a: any[]) { return tag("F", "toDateString", a); }; +F.prototype.toTimeString = function (...a: any[]) { return tag("F", "toTimeString", a); }; +F.prototype.toUTCString = function (...a: any[]) { return tag("F", "toUTCString", a); }; +F.prototype.toGMTString = function (...a: any[]) { return tag("F", "toGMTString", a); }; +F.prototype.toLocaleString = function (...a: any[]) { return tag("F", "toLocaleString", a); }; +F.prototype.toLocaleDateString = function (...a: any[]) { return tag("F", "toLocaleDateString", a); }; +F.prototype.toLocaleTimeString = function (...a: any[]) { return tag("F", "toLocaleTimeString", a); }; +F.prototype.toFixed = function (...a: any[]) { return tag("F", "toFixed", a); }; +F.prototype.toPrecision = function (...a: any[]) { return tag("F", "toPrecision", a); }; +F.prototype.toExponential = function (...a: any[]) { return tag("F", "toExponential", a); }; +F.prototype.toSorted = function (...a: any[]) { return tag("F", "toSorted", a); }; +F.prototype.toReversed = function (...a: any[]) { return tag("F", "toReversed", a); }; +F.prototype.reduceRight = function (...a: any[]) { return tag("F", "reduceRight", a); }; +F.prototype.flat = function (...a: any[]) { return tag("F", "flat", a); }; +F.prototype.flatMap = function (...a: any[]) { return tag("F", "flatMap", a); }; +F.prototype.toSpliced = function (...a: any[]) { return tag("F", "toSpliced", a); }; +F.prototype.copyWithin = function (...a: any[]) { return tag("F", "copyWithin", a); }; +F.prototype.endsWith = function (...a: any[]) { return tag("F", "endsWith", a); }; +F.prototype.includes = function (...a: any[]) { return tag("F", "includes", a); }; +F.prototype.startsWith = function (...a: any[]) { return tag("F", "startsWith", a); }; +F.prototype.viaThis = function (this: any, id: number): any { + switch (id) { + case 0: return this.getDate(); + case 1: return this.getDay(); + case 2: return this.getFullYear(); + case 3: return this.getHours(); + case 4: return this.getMilliseconds(); + case 5: return this.getMinutes(); + case 6: return this.getMonth(); + case 7: return this.getSeconds(); + case 8: return this.getTime(); + case 9: return this.getTimezoneOffset(); + case 10: return this.getUTCDate(); + case 11: return this.getUTCDay(); + case 12: return this.getUTCFullYear(); + case 13: return this.getUTCHours(); + case 14: return this.getUTCMilliseconds(); + case 15: return this.getUTCMinutes(); + case 16: return this.getUTCMonth(); + case 17: return this.getUTCSeconds(); + case 18: return this.setDate(1); + case 19: return this.setFullYear(1); + case 20: return this.setHours(1); + case 21: return this.setMilliseconds(1); + case 22: return this.setMinutes(1); + case 23: return this.setMonth(1); + case 24: return this.setSeconds(1); + case 25: return this.setTime(1); + case 26: return this.setUTCDate(1); + case 27: return this.setUTCFullYear(1); + case 28: return this.setUTCHours(1); + case 29: return this.setUTCMilliseconds(1); + case 30: return this.setUTCMinutes(1); + case 31: return this.setUTCMonth(1); + case 32: return this.setUTCSeconds(1); + case 33: return this.toISOString(); + case 34: return this.toDateString(); + case 35: return this.toTimeString(); + case 36: return this.toUTCString(); + case 37: return this.toGMTString(); + case 38: return this.toLocaleString(); + case 39: return this.toLocaleDateString(); + case 40: return this.toLocaleTimeString(); + case 41: return this.toFixed(2); + case 42: return this.toPrecision(2); + case 43: return this.toExponential(2); + case 44: return this.toFixed(); + case 45: return this.toSorted(); + case 46: return this.toSorted(cmp); + case 47: return this.toReversed(); + case 48: return this.reduceRight(add); + case 49: return this.flat(); + case 50: return this.flatMap(dup); + case 51: return this.toSpliced(1); + case 52: return this.toSpliced(0, 1); + case 53: return this.copyWithin(0, 1); + case 54: return this.endsWith(); + case 55: return this.includes(); + case 56: return this.startsWith(); + case 57: return this.endsWith('c'); + case 58: return this.includes('b'); + case 59: return this.startsWith('a'); + } +}; + +function G(this: any) { this.v = 2; } +G.prototype = { + getDate: function (...a: any[]) { return tag("G", "getDate", a); }, + getDay: function (...a: any[]) { return tag("G", "getDay", a); }, + getFullYear: function (...a: any[]) { return tag("G", "getFullYear", a); }, + getHours: function (...a: any[]) { return tag("G", "getHours", a); }, + getMilliseconds: function (...a: any[]) { return tag("G", "getMilliseconds", a); }, + getMinutes: function (...a: any[]) { return tag("G", "getMinutes", a); }, + getMonth: function (...a: any[]) { return tag("G", "getMonth", a); }, + getSeconds: function (...a: any[]) { return tag("G", "getSeconds", a); }, + getTime: function (...a: any[]) { return tag("G", "getTime", a); }, + getTimezoneOffset: function (...a: any[]) { return tag("G", "getTimezoneOffset", a); }, + getUTCDate: function (...a: any[]) { return tag("G", "getUTCDate", a); }, + getUTCDay: function (...a: any[]) { return tag("G", "getUTCDay", a); }, + getUTCFullYear: function (...a: any[]) { return tag("G", "getUTCFullYear", a); }, + getUTCHours: function (...a: any[]) { return tag("G", "getUTCHours", a); }, + getUTCMilliseconds: function (...a: any[]) { return tag("G", "getUTCMilliseconds", a); }, + getUTCMinutes: function (...a: any[]) { return tag("G", "getUTCMinutes", a); }, + getUTCMonth: function (...a: any[]) { return tag("G", "getUTCMonth", a); }, + getUTCSeconds: function (...a: any[]) { return tag("G", "getUTCSeconds", a); }, + setDate: function (...a: any[]) { return tag("G", "setDate", a); }, + setFullYear: function (...a: any[]) { return tag("G", "setFullYear", a); }, + setHours: function (...a: any[]) { return tag("G", "setHours", a); }, + setMilliseconds: function (...a: any[]) { return tag("G", "setMilliseconds", a); }, + setMinutes: function (...a: any[]) { return tag("G", "setMinutes", a); }, + setMonth: function (...a: any[]) { return tag("G", "setMonth", a); }, + setSeconds: function (...a: any[]) { return tag("G", "setSeconds", a); }, + setTime: function (...a: any[]) { return tag("G", "setTime", a); }, + setUTCDate: function (...a: any[]) { return tag("G", "setUTCDate", a); }, + setUTCFullYear: function (...a: any[]) { return tag("G", "setUTCFullYear", a); }, + setUTCHours: function (...a: any[]) { return tag("G", "setUTCHours", a); }, + setUTCMilliseconds: function (...a: any[]) { return tag("G", "setUTCMilliseconds", a); }, + setUTCMinutes: function (...a: any[]) { return tag("G", "setUTCMinutes", a); }, + setUTCMonth: function (...a: any[]) { return tag("G", "setUTCMonth", a); }, + setUTCSeconds: function (...a: any[]) { return tag("G", "setUTCSeconds", a); }, + toISOString: function (...a: any[]) { return tag("G", "toISOString", a); }, + toDateString: function (...a: any[]) { return tag("G", "toDateString", a); }, + toTimeString: function (...a: any[]) { return tag("G", "toTimeString", a); }, + toUTCString: function (...a: any[]) { return tag("G", "toUTCString", a); }, + toGMTString: function (...a: any[]) { return tag("G", "toGMTString", a); }, + toLocaleString: function (...a: any[]) { return tag("G", "toLocaleString", a); }, + toLocaleDateString: function (...a: any[]) { return tag("G", "toLocaleDateString", a); }, + toLocaleTimeString: function (...a: any[]) { return tag("G", "toLocaleTimeString", a); }, + toFixed: function (...a: any[]) { return tag("G", "toFixed", a); }, + toPrecision: function (...a: any[]) { return tag("G", "toPrecision", a); }, + toExponential: function (...a: any[]) { return tag("G", "toExponential", a); }, + toSorted: function (...a: any[]) { return tag("G", "toSorted", a); }, + toReversed: function (...a: any[]) { return tag("G", "toReversed", a); }, + reduceRight: function (...a: any[]) { return tag("G", "reduceRight", a); }, + flat: function (...a: any[]) { return tag("G", "flat", a); }, + flatMap: function (...a: any[]) { return tag("G", "flatMap", a); }, + toSpliced: function (...a: any[]) { return tag("G", "toSpliced", a); }, + copyWithin: function (...a: any[]) { return tag("G", "copyWithin", a); }, + endsWith: function (...a: any[]) { return tag("G", "endsWith", a); }, + includes: function (...a: any[]) { return tag("G", "includes", a); }, + startsWith: function (...a: any[]) { return tag("G", "startsWith", a); }, +}; + +const lit = { + v: 3, + getDate(...a: any[]): any { return tag("lit", "getDate", a); }, + getDay(...a: any[]): any { return tag("lit", "getDay", a); }, + getFullYear(...a: any[]): any { return tag("lit", "getFullYear", a); }, + getHours(...a: any[]): any { return tag("lit", "getHours", a); }, + getMilliseconds(...a: any[]): any { return tag("lit", "getMilliseconds", a); }, + getMinutes(...a: any[]): any { return tag("lit", "getMinutes", a); }, + getMonth(...a: any[]): any { return tag("lit", "getMonth", a); }, + getSeconds(...a: any[]): any { return tag("lit", "getSeconds", a); }, + getTime(...a: any[]): any { return tag("lit", "getTime", a); }, + getTimezoneOffset(...a: any[]): any { return tag("lit", "getTimezoneOffset", a); }, + getUTCDate(...a: any[]): any { return tag("lit", "getUTCDate", a); }, + getUTCDay(...a: any[]): any { return tag("lit", "getUTCDay", a); }, + getUTCFullYear(...a: any[]): any { return tag("lit", "getUTCFullYear", a); }, + getUTCHours(...a: any[]): any { return tag("lit", "getUTCHours", a); }, + getUTCMilliseconds(...a: any[]): any { return tag("lit", "getUTCMilliseconds", a); }, + getUTCMinutes(...a: any[]): any { return tag("lit", "getUTCMinutes", a); }, + getUTCMonth(...a: any[]): any { return tag("lit", "getUTCMonth", a); }, + getUTCSeconds(...a: any[]): any { return tag("lit", "getUTCSeconds", a); }, + setDate(...a: any[]): any { return tag("lit", "setDate", a); }, + setFullYear(...a: any[]): any { return tag("lit", "setFullYear", a); }, + setHours(...a: any[]): any { return tag("lit", "setHours", a); }, + setMilliseconds(...a: any[]): any { return tag("lit", "setMilliseconds", a); }, + setMinutes(...a: any[]): any { return tag("lit", "setMinutes", a); }, + setMonth(...a: any[]): any { return tag("lit", "setMonth", a); }, + setSeconds(...a: any[]): any { return tag("lit", "setSeconds", a); }, + setTime(...a: any[]): any { return tag("lit", "setTime", a); }, + setUTCDate(...a: any[]): any { return tag("lit", "setUTCDate", a); }, + setUTCFullYear(...a: any[]): any { return tag("lit", "setUTCFullYear", a); }, + setUTCHours(...a: any[]): any { return tag("lit", "setUTCHours", a); }, + setUTCMilliseconds(...a: any[]): any { return tag("lit", "setUTCMilliseconds", a); }, + setUTCMinutes(...a: any[]): any { return tag("lit", "setUTCMinutes", a); }, + setUTCMonth(...a: any[]): any { return tag("lit", "setUTCMonth", a); }, + setUTCSeconds(...a: any[]): any { return tag("lit", "setUTCSeconds", a); }, + toISOString(...a: any[]): any { return tag("lit", "toISOString", a); }, + toDateString(...a: any[]): any { return tag("lit", "toDateString", a); }, + toTimeString(...a: any[]): any { return tag("lit", "toTimeString", a); }, + toUTCString(...a: any[]): any { return tag("lit", "toUTCString", a); }, + toGMTString(...a: any[]): any { return tag("lit", "toGMTString", a); }, + toLocaleString(...a: any[]): any { return tag("lit", "toLocaleString", a); }, + toLocaleDateString(...a: any[]): any { return tag("lit", "toLocaleDateString", a); }, + toLocaleTimeString(...a: any[]): any { return tag("lit", "toLocaleTimeString", a); }, + toFixed(...a: any[]): any { return tag("lit", "toFixed", a); }, + toPrecision(...a: any[]): any { return tag("lit", "toPrecision", a); }, + toExponential(...a: any[]): any { return tag("lit", "toExponential", a); }, + toSorted(...a: any[]): any { return tag("lit", "toSorted", a); }, + toReversed(...a: any[]): any { return tag("lit", "toReversed", a); }, + reduceRight(...a: any[]): any { return tag("lit", "reduceRight", a); }, + flat(...a: any[]): any { return tag("lit", "flat", a); }, + flatMap(...a: any[]): any { return tag("lit", "flatMap", a); }, + toSpliced(...a: any[]): any { return tag("lit", "toSpliced", a); }, + copyWithin(...a: any[]): any { return tag("lit", "copyWithin", a); }, + endsWith(...a: any[]): any { return tag("lit", "endsWith", a); }, + includes(...a: any[]): any { return tag("lit", "includes", a); }, + startsWith(...a: any[]): any { return tag("lit", "startsWith", a); }, +}; + +const protoLit: any = {}; +protoLit.getDate = function (...a: any[]) { return tag("oc", "getDate", a); }; +protoLit.getDay = function (...a: any[]) { return tag("oc", "getDay", a); }; +protoLit.getFullYear = function (...a: any[]) { return tag("oc", "getFullYear", a); }; +protoLit.getHours = function (...a: any[]) { return tag("oc", "getHours", a); }; +protoLit.getMilliseconds = function (...a: any[]) { return tag("oc", "getMilliseconds", a); }; +protoLit.getMinutes = function (...a: any[]) { return tag("oc", "getMinutes", a); }; +protoLit.getMonth = function (...a: any[]) { return tag("oc", "getMonth", a); }; +protoLit.getSeconds = function (...a: any[]) { return tag("oc", "getSeconds", a); }; +protoLit.getTime = function (...a: any[]) { return tag("oc", "getTime", a); }; +protoLit.getTimezoneOffset = function (...a: any[]) { return tag("oc", "getTimezoneOffset", a); }; +protoLit.getUTCDate = function (...a: any[]) { return tag("oc", "getUTCDate", a); }; +protoLit.getUTCDay = function (...a: any[]) { return tag("oc", "getUTCDay", a); }; +protoLit.getUTCFullYear = function (...a: any[]) { return tag("oc", "getUTCFullYear", a); }; +protoLit.getUTCHours = function (...a: any[]) { return tag("oc", "getUTCHours", a); }; +protoLit.getUTCMilliseconds = function (...a: any[]) { return tag("oc", "getUTCMilliseconds", a); }; +protoLit.getUTCMinutes = function (...a: any[]) { return tag("oc", "getUTCMinutes", a); }; +protoLit.getUTCMonth = function (...a: any[]) { return tag("oc", "getUTCMonth", a); }; +protoLit.getUTCSeconds = function (...a: any[]) { return tag("oc", "getUTCSeconds", a); }; +protoLit.setDate = function (...a: any[]) { return tag("oc", "setDate", a); }; +protoLit.setFullYear = function (...a: any[]) { return tag("oc", "setFullYear", a); }; +protoLit.setHours = function (...a: any[]) { return tag("oc", "setHours", a); }; +protoLit.setMilliseconds = function (...a: any[]) { return tag("oc", "setMilliseconds", a); }; +protoLit.setMinutes = function (...a: any[]) { return tag("oc", "setMinutes", a); }; +protoLit.setMonth = function (...a: any[]) { return tag("oc", "setMonth", a); }; +protoLit.setSeconds = function (...a: any[]) { return tag("oc", "setSeconds", a); }; +protoLit.setTime = function (...a: any[]) { return tag("oc", "setTime", a); }; +protoLit.setUTCDate = function (...a: any[]) { return tag("oc", "setUTCDate", a); }; +protoLit.setUTCFullYear = function (...a: any[]) { return tag("oc", "setUTCFullYear", a); }; +protoLit.setUTCHours = function (...a: any[]) { return tag("oc", "setUTCHours", a); }; +protoLit.setUTCMilliseconds = function (...a: any[]) { return tag("oc", "setUTCMilliseconds", a); }; +protoLit.setUTCMinutes = function (...a: any[]) { return tag("oc", "setUTCMinutes", a); }; +protoLit.setUTCMonth = function (...a: any[]) { return tag("oc", "setUTCMonth", a); }; +protoLit.setUTCSeconds = function (...a: any[]) { return tag("oc", "setUTCSeconds", a); }; +protoLit.toISOString = function (...a: any[]) { return tag("oc", "toISOString", a); }; +protoLit.toDateString = function (...a: any[]) { return tag("oc", "toDateString", a); }; +protoLit.toTimeString = function (...a: any[]) { return tag("oc", "toTimeString", a); }; +protoLit.toUTCString = function (...a: any[]) { return tag("oc", "toUTCString", a); }; +protoLit.toGMTString = function (...a: any[]) { return tag("oc", "toGMTString", a); }; +protoLit.toLocaleString = function (...a: any[]) { return tag("oc", "toLocaleString", a); }; +protoLit.toLocaleDateString = function (...a: any[]) { return tag("oc", "toLocaleDateString", a); }; +protoLit.toLocaleTimeString = function (...a: any[]) { return tag("oc", "toLocaleTimeString", a); }; +protoLit.toFixed = function (...a: any[]) { return tag("oc", "toFixed", a); }; +protoLit.toPrecision = function (...a: any[]) { return tag("oc", "toPrecision", a); }; +protoLit.toExponential = function (...a: any[]) { return tag("oc", "toExponential", a); }; +protoLit.toSorted = function (...a: any[]) { return tag("oc", "toSorted", a); }; +protoLit.toReversed = function (...a: any[]) { return tag("oc", "toReversed", a); }; +protoLit.reduceRight = function (...a: any[]) { return tag("oc", "reduceRight", a); }; +protoLit.flat = function (...a: any[]) { return tag("oc", "flat", a); }; +protoLit.flatMap = function (...a: any[]) { return tag("oc", "flatMap", a); }; +protoLit.toSpliced = function (...a: any[]) { return tag("oc", "toSpliced", a); }; +protoLit.copyWithin = function (...a: any[]) { return tag("oc", "copyWithin", a); }; +protoLit.endsWith = function (...a: any[]) { return tag("oc", "endsWith", a); }; +protoLit.includes = function (...a: any[]) { return tag("oc", "includes", a); }; +protoLit.startsWith = function (...a: any[]) { return tag("oc", "startsWith", a); }; +const oc = Object.create(protoLit); + +const f: any = new (F as any)(); +const g: any = new (G as any)(); + +row("getDate()", () => f.getDate(), () => f.viaThis(0), () => g.getDate(), () => lit.getDate(), () => oc.getDate(), () => viaAny(f, 0), () => viaAny(lit, 0), () => viaAny(oc, 0)); +row("getDay()", () => f.getDay(), () => f.viaThis(1), () => g.getDay(), () => lit.getDay(), () => oc.getDay(), () => viaAny(f, 1), () => viaAny(lit, 1), () => viaAny(oc, 1)); +row("getFullYear()", () => f.getFullYear(), () => f.viaThis(2), () => g.getFullYear(), () => lit.getFullYear(), () => oc.getFullYear(), () => viaAny(f, 2), () => viaAny(lit, 2), () => viaAny(oc, 2)); +row("getHours()", () => f.getHours(), () => f.viaThis(3), () => g.getHours(), () => lit.getHours(), () => oc.getHours(), () => viaAny(f, 3), () => viaAny(lit, 3), () => viaAny(oc, 3)); +row("getMilliseconds()", () => f.getMilliseconds(), () => f.viaThis(4), () => g.getMilliseconds(), () => lit.getMilliseconds(), () => oc.getMilliseconds(), () => viaAny(f, 4), () => viaAny(lit, 4), () => viaAny(oc, 4)); +row("getMinutes()", () => f.getMinutes(), () => f.viaThis(5), () => g.getMinutes(), () => lit.getMinutes(), () => oc.getMinutes(), () => viaAny(f, 5), () => viaAny(lit, 5), () => viaAny(oc, 5)); +row("getMonth()", () => f.getMonth(), () => f.viaThis(6), () => g.getMonth(), () => lit.getMonth(), () => oc.getMonth(), () => viaAny(f, 6), () => viaAny(lit, 6), () => viaAny(oc, 6)); +row("getSeconds()", () => f.getSeconds(), () => f.viaThis(7), () => g.getSeconds(), () => lit.getSeconds(), () => oc.getSeconds(), () => viaAny(f, 7), () => viaAny(lit, 7), () => viaAny(oc, 7)); +row("getTime()", () => f.getTime(), () => f.viaThis(8), () => g.getTime(), () => lit.getTime(), () => oc.getTime(), () => viaAny(f, 8), () => viaAny(lit, 8), () => viaAny(oc, 8)); +row("getTimezoneOffset()", () => f.getTimezoneOffset(), () => f.viaThis(9), () => g.getTimezoneOffset(), () => lit.getTimezoneOffset(), () => oc.getTimezoneOffset(), () => viaAny(f, 9), () => viaAny(lit, 9), () => viaAny(oc, 9)); +row("getUTCDate()", () => f.getUTCDate(), () => f.viaThis(10), () => g.getUTCDate(), () => lit.getUTCDate(), () => oc.getUTCDate(), () => viaAny(f, 10), () => viaAny(lit, 10), () => viaAny(oc, 10)); +row("getUTCDay()", () => f.getUTCDay(), () => f.viaThis(11), () => g.getUTCDay(), () => lit.getUTCDay(), () => oc.getUTCDay(), () => viaAny(f, 11), () => viaAny(lit, 11), () => viaAny(oc, 11)); +row("getUTCFullYear()", () => f.getUTCFullYear(), () => f.viaThis(12), () => g.getUTCFullYear(), () => lit.getUTCFullYear(), () => oc.getUTCFullYear(), () => viaAny(f, 12), () => viaAny(lit, 12), () => viaAny(oc, 12)); +row("getUTCHours()", () => f.getUTCHours(), () => f.viaThis(13), () => g.getUTCHours(), () => lit.getUTCHours(), () => oc.getUTCHours(), () => viaAny(f, 13), () => viaAny(lit, 13), () => viaAny(oc, 13)); +row("getUTCMilliseconds()", () => f.getUTCMilliseconds(), () => f.viaThis(14), () => g.getUTCMilliseconds(), () => lit.getUTCMilliseconds(), () => oc.getUTCMilliseconds(), () => viaAny(f, 14), () => viaAny(lit, 14), () => viaAny(oc, 14)); +row("getUTCMinutes()", () => f.getUTCMinutes(), () => f.viaThis(15), () => g.getUTCMinutes(), () => lit.getUTCMinutes(), () => oc.getUTCMinutes(), () => viaAny(f, 15), () => viaAny(lit, 15), () => viaAny(oc, 15)); +row("getUTCMonth()", () => f.getUTCMonth(), () => f.viaThis(16), () => g.getUTCMonth(), () => lit.getUTCMonth(), () => oc.getUTCMonth(), () => viaAny(f, 16), () => viaAny(lit, 16), () => viaAny(oc, 16)); +row("getUTCSeconds()", () => f.getUTCSeconds(), () => f.viaThis(17), () => g.getUTCSeconds(), () => lit.getUTCSeconds(), () => oc.getUTCSeconds(), () => viaAny(f, 17), () => viaAny(lit, 17), () => viaAny(oc, 17)); +row("setDate(1)", () => f.setDate(1), () => f.viaThis(18), () => g.setDate(1), () => lit.setDate(1), () => oc.setDate(1), () => viaAny(f, 18), () => viaAny(lit, 18), () => viaAny(oc, 18)); +row("setFullYear(1)", () => f.setFullYear(1), () => f.viaThis(19), () => g.setFullYear(1), () => lit.setFullYear(1), () => oc.setFullYear(1), () => viaAny(f, 19), () => viaAny(lit, 19), () => viaAny(oc, 19)); +row("setHours(1)", () => f.setHours(1), () => f.viaThis(20), () => g.setHours(1), () => lit.setHours(1), () => oc.setHours(1), () => viaAny(f, 20), () => viaAny(lit, 20), () => viaAny(oc, 20)); +row("setMilliseconds(1)", () => f.setMilliseconds(1), () => f.viaThis(21), () => g.setMilliseconds(1), () => lit.setMilliseconds(1), () => oc.setMilliseconds(1), () => viaAny(f, 21), () => viaAny(lit, 21), () => viaAny(oc, 21)); +row("setMinutes(1)", () => f.setMinutes(1), () => f.viaThis(22), () => g.setMinutes(1), () => lit.setMinutes(1), () => oc.setMinutes(1), () => viaAny(f, 22), () => viaAny(lit, 22), () => viaAny(oc, 22)); +row("setMonth(1)", () => f.setMonth(1), () => f.viaThis(23), () => g.setMonth(1), () => lit.setMonth(1), () => oc.setMonth(1), () => viaAny(f, 23), () => viaAny(lit, 23), () => viaAny(oc, 23)); +row("setSeconds(1)", () => f.setSeconds(1), () => f.viaThis(24), () => g.setSeconds(1), () => lit.setSeconds(1), () => oc.setSeconds(1), () => viaAny(f, 24), () => viaAny(lit, 24), () => viaAny(oc, 24)); +row("setTime(1)", () => f.setTime(1), () => f.viaThis(25), () => g.setTime(1), () => lit.setTime(1), () => oc.setTime(1), () => viaAny(f, 25), () => viaAny(lit, 25), () => viaAny(oc, 25)); +row("setUTCDate(1)", () => f.setUTCDate(1), () => f.viaThis(26), () => g.setUTCDate(1), () => lit.setUTCDate(1), () => oc.setUTCDate(1), () => viaAny(f, 26), () => viaAny(lit, 26), () => viaAny(oc, 26)); +row("setUTCFullYear(1)", () => f.setUTCFullYear(1), () => f.viaThis(27), () => g.setUTCFullYear(1), () => lit.setUTCFullYear(1), () => oc.setUTCFullYear(1), () => viaAny(f, 27), () => viaAny(lit, 27), () => viaAny(oc, 27)); +row("setUTCHours(1)", () => f.setUTCHours(1), () => f.viaThis(28), () => g.setUTCHours(1), () => lit.setUTCHours(1), () => oc.setUTCHours(1), () => viaAny(f, 28), () => viaAny(lit, 28), () => viaAny(oc, 28)); +row("setUTCMilliseconds(1)", () => f.setUTCMilliseconds(1), () => f.viaThis(29), () => g.setUTCMilliseconds(1), () => lit.setUTCMilliseconds(1), () => oc.setUTCMilliseconds(1), () => viaAny(f, 29), () => viaAny(lit, 29), () => viaAny(oc, 29)); +row("setUTCMinutes(1)", () => f.setUTCMinutes(1), () => f.viaThis(30), () => g.setUTCMinutes(1), () => lit.setUTCMinutes(1), () => oc.setUTCMinutes(1), () => viaAny(f, 30), () => viaAny(lit, 30), () => viaAny(oc, 30)); +row("setUTCMonth(1)", () => f.setUTCMonth(1), () => f.viaThis(31), () => g.setUTCMonth(1), () => lit.setUTCMonth(1), () => oc.setUTCMonth(1), () => viaAny(f, 31), () => viaAny(lit, 31), () => viaAny(oc, 31)); +row("setUTCSeconds(1)", () => f.setUTCSeconds(1), () => f.viaThis(32), () => g.setUTCSeconds(1), () => lit.setUTCSeconds(1), () => oc.setUTCSeconds(1), () => viaAny(f, 32), () => viaAny(lit, 32), () => viaAny(oc, 32)); +row("toISOString()", () => f.toISOString(), () => f.viaThis(33), () => g.toISOString(), () => lit.toISOString(), () => oc.toISOString(), () => viaAny(f, 33), () => viaAny(lit, 33), () => viaAny(oc, 33)); +row("toDateString()", () => f.toDateString(), () => f.viaThis(34), () => g.toDateString(), () => lit.toDateString(), () => oc.toDateString(), () => viaAny(f, 34), () => viaAny(lit, 34), () => viaAny(oc, 34)); +row("toTimeString()", () => f.toTimeString(), () => f.viaThis(35), () => g.toTimeString(), () => lit.toTimeString(), () => oc.toTimeString(), () => viaAny(f, 35), () => viaAny(lit, 35), () => viaAny(oc, 35)); +row("toUTCString()", () => f.toUTCString(), () => f.viaThis(36), () => g.toUTCString(), () => lit.toUTCString(), () => oc.toUTCString(), () => viaAny(f, 36), () => viaAny(lit, 36), () => viaAny(oc, 36)); +row("toGMTString()", () => f.toGMTString(), () => f.viaThis(37), () => g.toGMTString(), () => lit.toGMTString(), () => oc.toGMTString(), () => viaAny(f, 37), () => viaAny(lit, 37), () => viaAny(oc, 37)); +row("toLocaleString()", () => f.toLocaleString(), () => f.viaThis(38), () => g.toLocaleString(), () => lit.toLocaleString(), () => oc.toLocaleString(), () => viaAny(f, 38), () => viaAny(lit, 38), () => viaAny(oc, 38)); +row("toLocaleDateString()", () => f.toLocaleDateString(), () => f.viaThis(39), () => g.toLocaleDateString(), () => lit.toLocaleDateString(), () => oc.toLocaleDateString(), () => viaAny(f, 39), () => viaAny(lit, 39), () => viaAny(oc, 39)); +row("toLocaleTimeString()", () => f.toLocaleTimeString(), () => f.viaThis(40), () => g.toLocaleTimeString(), () => lit.toLocaleTimeString(), () => oc.toLocaleTimeString(), () => viaAny(f, 40), () => viaAny(lit, 40), () => viaAny(oc, 40)); +row("toFixed(2)", () => f.toFixed(2), () => f.viaThis(41), () => g.toFixed(2), () => lit.toFixed(2), () => oc.toFixed(2), () => viaAny(f, 41), () => viaAny(lit, 41), () => viaAny(oc, 41)); +row("toPrecision(2)", () => f.toPrecision(2), () => f.viaThis(42), () => g.toPrecision(2), () => lit.toPrecision(2), () => oc.toPrecision(2), () => viaAny(f, 42), () => viaAny(lit, 42), () => viaAny(oc, 42)); +row("toExponential(2)", () => f.toExponential(2), () => f.viaThis(43), () => g.toExponential(2), () => lit.toExponential(2), () => oc.toExponential(2), () => viaAny(f, 43), () => viaAny(lit, 43), () => viaAny(oc, 43)); +row("toFixed()", () => f.toFixed(), () => f.viaThis(44), () => g.toFixed(), () => lit.toFixed(), () => oc.toFixed(), () => viaAny(f, 44), () => viaAny(lit, 44), () => viaAny(oc, 44)); +row("toSorted()", () => f.toSorted(), () => f.viaThis(45), () => g.toSorted(), () => lit.toSorted(), () => oc.toSorted(), () => viaAny(f, 45), () => viaAny(lit, 45), () => viaAny(oc, 45)); +row("toSorted(cmp)", () => f.toSorted(cmp), () => f.viaThis(46), () => g.toSorted(cmp), () => lit.toSorted(cmp), () => oc.toSorted(cmp), () => viaAny(f, 46), () => viaAny(lit, 46), () => viaAny(oc, 46)); +row("toReversed()", () => f.toReversed(), () => f.viaThis(47), () => g.toReversed(), () => lit.toReversed(), () => oc.toReversed(), () => viaAny(f, 47), () => viaAny(lit, 47), () => viaAny(oc, 47)); +row("reduceRight(add)", () => f.reduceRight(add), () => f.viaThis(48), () => g.reduceRight(add), () => lit.reduceRight(add), () => oc.reduceRight(add), () => viaAny(f, 48), () => viaAny(lit, 48), () => viaAny(oc, 48)); +row("flat()", () => f.flat(), () => f.viaThis(49), () => g.flat(), () => lit.flat(), () => oc.flat(), () => viaAny(f, 49), () => viaAny(lit, 49), () => viaAny(oc, 49)); +row("flatMap(dup)", () => f.flatMap(dup), () => f.viaThis(50), () => g.flatMap(dup), () => lit.flatMap(dup), () => oc.flatMap(dup), () => viaAny(f, 50), () => viaAny(lit, 50), () => viaAny(oc, 50)); +row("toSpliced(1)", () => f.toSpliced(1), () => f.viaThis(51), () => g.toSpliced(1), () => lit.toSpliced(1), () => oc.toSpliced(1), () => viaAny(f, 51), () => viaAny(lit, 51), () => viaAny(oc, 51)); +row("toSpliced(0, 1)", () => f.toSpliced(0, 1), () => f.viaThis(52), () => g.toSpliced(0, 1), () => lit.toSpliced(0, 1), () => oc.toSpliced(0, 1), () => viaAny(f, 52), () => viaAny(lit, 52), () => viaAny(oc, 52)); +row("copyWithin(0, 1)", () => f.copyWithin(0, 1), () => f.viaThis(53), () => g.copyWithin(0, 1), () => lit.copyWithin(0, 1), () => oc.copyWithin(0, 1), () => viaAny(f, 53), () => viaAny(lit, 53), () => viaAny(oc, 53)); +row("endsWith()", () => f.endsWith(), () => f.viaThis(54), () => g.endsWith(), () => lit.endsWith(), () => oc.endsWith(), () => viaAny(f, 54), () => viaAny(lit, 54), () => viaAny(oc, 54)); +row("includes()", () => f.includes(), () => f.viaThis(55), () => g.includes(), () => lit.includes(), () => oc.includes(), () => viaAny(f, 55), () => viaAny(lit, 55), () => viaAny(oc, 55)); +row("startsWith()", () => f.startsWith(), () => f.viaThis(56), () => g.startsWith(), () => lit.startsWith(), () => oc.startsWith(), () => viaAny(f, 56), () => viaAny(lit, 56), () => viaAny(oc, 56)); +row("endsWith('c')", () => f.endsWith('c'), () => f.viaThis(57), () => g.endsWith('c'), () => lit.endsWith('c'), () => oc.endsWith('c'), () => viaAny(f, 57), () => viaAny(lit, 57), () => viaAny(oc, 57)); +row("includes('b')", () => f.includes('b'), () => f.viaThis(58), () => g.includes('b'), () => lit.includes('b'), () => oc.includes('b'), () => viaAny(f, 58), () => viaAny(lit, 58), () => viaAny(oc, 58)); +row("startsWith('a')", () => f.startsWith('a'), () => f.viaThis(59), () => g.startsWith('a'), () => lit.startsWith('a'), () => oc.startsWith('a'), () => viaAny(f, 59), () => viaAny(lit, 59), () => viaAny(oc, 59)); + +// dayjs / moment `toJSON` shape: a prototype method calls a sibling via `this`. +function Dayjs(this: any, ms: number) { this.$d = new Date(ms); } +Dayjs.prototype.isValid = function () { return true; }; +Dayjs.prototype.toISOString = function (this: any) { return "Dayjs(" + this.$d.toISOString() + ")"; }; +Dayjs.prototype.valueOf = function (this: any) { return this.$d.getTime(); }; +Dayjs.prototype.toJSON = function (this: any) { return this.isValid() ? this.toISOString() : null; }; +const dj: any = new (Dayjs as any)(7); +console.log("dayjs toJSON", dj.toJSON(), JSON.stringify({ at: dj }), +dj); + +// Controls: the real built-ins on typed receivers, `any` receivers and the +// shared `viaAny` call sites. Local-time results depend on the host zone, so +// local getters/setters are compared with UTC-derived values, not printed. +const d = new Date(Date.UTC(2024, 5, 15, 10, 20, 30, 456)); +const du: any = new Date(Date.UTC(2024, 5, 15, 10, 20, 30, 456)); +const shifted = new Date(d.getTime() - d.getTimezoneOffset() * 60000); +console.log("Date getDate", d.getDate() === shifted.getUTCDate(), du.getDate() === d.getDate(), viaAny(du, 0) === d.getDate()); +console.log("Date getDay", d.getDay() === shifted.getUTCDay(), du.getDay() === d.getDay(), viaAny(du, 1) === d.getDay()); +console.log("Date getFullYear", d.getFullYear() === shifted.getUTCFullYear(), du.getFullYear() === d.getFullYear(), viaAny(du, 2) === d.getFullYear()); +console.log("Date getHours", d.getHours() === shifted.getUTCHours(), du.getHours() === d.getHours(), viaAny(du, 3) === d.getHours()); +console.log("Date getMilliseconds", d.getMilliseconds() === shifted.getUTCMilliseconds(), du.getMilliseconds() === d.getMilliseconds(), viaAny(du, 4) === d.getMilliseconds()); +console.log("Date getMinutes", d.getMinutes() === shifted.getUTCMinutes(), du.getMinutes() === d.getMinutes(), viaAny(du, 5) === d.getMinutes()); +console.log("Date getMonth", d.getMonth() === shifted.getUTCMonth(), du.getMonth() === d.getMonth(), viaAny(du, 6) === d.getMonth()); +console.log("Date getSeconds", d.getSeconds() === shifted.getUTCSeconds(), du.getSeconds() === d.getSeconds(), viaAny(du, 7) === d.getSeconds()); +console.log("Date getTime", d.getTime(), du.getTime(), viaAny(du, 8)); +console.log("Date getTimezoneOffset", typeof d.getTimezoneOffset(), du.getTimezoneOffset() === d.getTimezoneOffset(), viaAny(du, 9) === d.getTimezoneOffset()); +console.log("Date getUTCDate", d.getUTCDate(), du.getUTCDate(), viaAny(du, 10)); +console.log("Date getUTCDay", d.getUTCDay(), du.getUTCDay(), viaAny(du, 11)); +console.log("Date getUTCFullYear", d.getUTCFullYear(), du.getUTCFullYear(), viaAny(du, 12)); +console.log("Date getUTCHours", d.getUTCHours(), du.getUTCHours(), viaAny(du, 13)); +console.log("Date getUTCMilliseconds", d.getUTCMilliseconds(), du.getUTCMilliseconds(), viaAny(du, 14)); +console.log("Date getUTCMinutes", d.getUTCMinutes(), du.getUTCMinutes(), viaAny(du, 15)); +console.log("Date getUTCMonth", d.getUTCMonth(), du.getUTCMonth(), viaAny(du, 16)); +console.log("Date getUTCSeconds", d.getUTCSeconds(), du.getUTCSeconds(), viaAny(du, 17)); +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setDate(1), tu.setDate(1), viaAny(tv, 18)]; + console.log("Date setDate", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getDate(), tu.getDate(), tv.getDate()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setFullYear(1), tu.setFullYear(1), viaAny(tv, 19)]; + console.log("Date setFullYear", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getFullYear(), tu.getFullYear(), tv.getFullYear()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setHours(1), tu.setHours(1), viaAny(tv, 20)]; + console.log("Date setHours", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getHours(), tu.getHours(), tv.getHours()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setMilliseconds(1), tu.setMilliseconds(1), viaAny(tv, 21)]; + console.log("Date setMilliseconds", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getMilliseconds(), tu.getMilliseconds(), tv.getMilliseconds()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setMinutes(1), tu.setMinutes(1), viaAny(tv, 22)]; + console.log("Date setMinutes", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getMinutes(), tu.getMinutes(), tv.getMinutes()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setMonth(1), tu.setMonth(1), viaAny(tv, 23)]; + console.log("Date setMonth", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getMonth(), tu.getMonth(), tv.getMonth()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + const r = [t.setSeconds(1), tu.setSeconds(1), viaAny(tv, 24)]; + console.log("Date setSeconds", typeof r[0], r[0] === t.getTime(), r[1] === tu.getTime(), r[2] === tv.getTime(), t.getSeconds(), tu.getSeconds(), tv.getSeconds()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setTime", t.setTime(1), t.getTime(), tu.setTime(1), tu.getTime(), viaAny(tv, 25), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCDate", t.setUTCDate(1), t.getTime(), tu.setUTCDate(1), tu.getTime(), viaAny(tv, 26), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCFullYear", t.setUTCFullYear(1), t.getTime(), tu.setUTCFullYear(1), tu.getTime(), viaAny(tv, 27), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCHours", t.setUTCHours(1), t.getTime(), tu.setUTCHours(1), tu.getTime(), viaAny(tv, 28), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCMilliseconds", t.setUTCMilliseconds(1), t.getTime(), tu.setUTCMilliseconds(1), tu.getTime(), viaAny(tv, 29), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCMinutes", t.setUTCMinutes(1), t.getTime(), tu.setUTCMinutes(1), tu.getTime(), viaAny(tv, 30), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCMonth", t.setUTCMonth(1), t.getTime(), tu.setUTCMonth(1), tu.getTime(), viaAny(tv, 31), tv.getTime()); } +{ const t = new Date(d.getTime()); const tu: any = new Date(d.getTime()); const tv = new Date(d.getTime()); + console.log("Date setUTCSeconds", t.setUTCSeconds(1), t.getTime(), tu.setUTCSeconds(1), tu.getTime(), viaAny(tv, 32), tv.getTime()); } +console.log("Date toISOString", d.toISOString(), du.toISOString(), viaAny(du, 33)); +console.log("Date toUTCString", d.toUTCString(), du.toUTCString(), viaAny(du, 36)); +console.log("Date toGMTString", d.toGMTString(), du.toGMTString(), viaAny(du, 37)); +console.log("Date toDateString", typeof d.toDateString(), du.toDateString() === d.toDateString(), viaAny(du, 34) === d.toDateString()); +console.log("Date toTimeString", typeof d.toTimeString(), du.toTimeString() === d.toTimeString(), viaAny(du, 35) === d.toTimeString()); +console.log("Date toLocaleString", typeof d.toLocaleString(), du.toLocaleString() === d.toLocaleString(), viaAny(du, 38) === d.toLocaleString()); +console.log("Date toLocaleDateString", typeof d.toLocaleDateString(), du.toLocaleDateString() === d.toLocaleDateString(), viaAny(du, 39) === d.toLocaleDateString()); +console.log("Date toLocaleTimeString", typeof d.toLocaleTimeString(), du.toLocaleTimeString() === d.toLocaleTimeString(), viaAny(du, 40) === d.toLocaleTimeString()); +// Zero-argument toLocaleString on primitives and symbols is Object.prototype's. +console.log("primitive toLocaleString", viaAny(true, 38), viaAny("abc", 38), viaAny(Symbol("s"), 38)); +const bad: any = new Date(NaN); +console.log("Invalid Date toISOString", attemptMessage(() => bad.toISOString()), attemptMessage(() => viaAny(bad, 33))); +const num = 3.14159; +const numu: any = 1234.5678; +console.log("number toFixed(2)", num.toFixed(2), numu.toFixed(2), viaAny(num, 41), viaAny(42, 41), viaAny(-0.5, 41)); +console.log("number toFixed()", num.toFixed(), numu.toFixed(), viaAny(num, 44), viaAny(42, 44), viaAny(-0.5, 44)); +console.log("number toPrecision(2)", num.toPrecision(2), numu.toPrecision(2), viaAny(num, 42), viaAny(42, 42), viaAny(-0.5, 42)); +console.log("number toExponential(2)", num.toExponential(2), numu.toExponential(2), viaAny(num, 43), viaAny(42, 43), viaAny(-0.5, 43)); +const arr = [3, 1, 2]; +const arru: any = [5, 4, 6]; +const nested = [1, [2, [3]]]; +const nestedu: any = [4, [5, [6]]]; +console.log("array toSorted()", show(arr.toSorted()), show(arru.toSorted()), show(viaAny(arr, 45)), show(arr)); +console.log("array toSorted(cmp)", show(arr.toSorted(cmp)), show(arru.toSorted(cmp)), show(viaAny(arr, 46)), show(arr)); +console.log("array toReversed()", show(arr.toReversed()), show(arru.toReversed()), show(viaAny(arr, 47)), show(arr)); +console.log("array reduceRight(add)", show(arr.reduceRight(add)), show(arru.reduceRight(add)), show(viaAny(arr, 48)), show(arr)); +console.log("array flat()", show(nested.flat()), show(nestedu.flat()), show(viaAny(nested, 49))); +console.log("array flatMap(dup)", show(arr.flatMap(dup)), show(arru.flatMap(dup)), show(viaAny(arr, 50)), show(arr)); +console.log("array toSpliced(1)", show(arr.toSpliced(1)), show(arru.toSpliced(1)), show(viaAny(arr, 51)), show(arr)); +console.log("array toSpliced(0, 1)", show(arr.toSpliced(0, 1)), show(arru.toSpliced(0, 1)), show(viaAny(arr, 52)), show(arr)); +console.log("array copyWithin(0, 1)", show([3, 1, 2].copyWithin(0, 1)), show(arru.slice().copyWithin(0, 1)), show(viaAny([7, 8, 9], 53))); +const str = "abc"; +const stru: any = "xundefined"; +console.log("string endsWith()", str.endsWith(), stru.endsWith(), viaAny(str, 54), viaAny(stru, 54)); +console.log("string endsWith('c')", str.endsWith('c'), stru.endsWith('c'), viaAny(str, 57), viaAny(stru, 57)); +console.log("string includes()", str.includes(), stru.includes(), viaAny(str, 55), viaAny(stru, 55)); +console.log("string includes('b')", str.includes('b'), stru.includes('b'), viaAny(str, 58), viaAny(stru, 58)); +console.log("string startsWith()", str.startsWith(), stru.startsWith(), viaAny(str, 56), viaAny(stru, 56)); +console.log("string startsWith('a')", str.startsWith('a'), stru.startsWith('a'), viaAny(str, 59), viaAny(stru, 59)); + +// One call site per name, the matching built-in and user objects in turn. +for (const m of [new Date(Date.UTC(2020, 0, 1)), f, g, lit, oc] as any[]) row("mixed getTime", () => viaAny(m, 8)); +for (const m of [new Date(0), f, g, lit, oc] as any[]) row("mixed toISOString", () => viaAny(m, 33)); +for (const m of [new Date(0), f, g, lit, oc] as any[]) row("mixed setUTCHours", () => viaAny(m, 28)); +for (const m of [12.345, f, g, lit, oc] as any[]) row("mixed toFixed", () => viaAny(m, 41)); +for (const m of [-7, f, g, lit, oc] as any[]) row("mixed toPrecision", () => viaAny(m, 42)); +for (const m of [[2, 1], f, g, lit, oc] as any[]) row("mixed toSorted", () => viaAny(m, 45)); +for (const m of [[[1], 2], f, g, lit, oc] as any[]) row("mixed flat", () => viaAny(m, 49)); +for (const m of ["abc", f, g, lit, oc] as any[]) row("mixed endsWith", () => viaAny(m, 54)); + +// A builtin-named user method may return anything; its result must not be +// treated as the builtin's string / number result. +const odd: any = { + toFixed(dp: number) { return 5 + dp; }, + toPrecision() { return { p: 1 }; }, + toExponential() { return null; }, + getTime() { return "t"; }, + toISOString() { return 42; }, +}; +console.log("odd", "a" + odd.toFixed(2), odd.toFixed(2) + 1, typeof odd.toFixed(2), JSON.stringify(odd.toPrecision(3))); +console.log("odd", "c" + odd.toExponential(1), odd.getTime() + 1, odd.toISOString() + 1, `${odd.toFixed(4)}!`); +let oddAcc = ""; +for (let i = 0; i < 3; i++) oddAcc += odd.toFixed(i); +console.log("odd loop", oddAcc); + +// Nullish receivers throw instead of reaching a builtin. +const nothing: any = null; +console.log("nullish", attemptMessage(() => nothing.getTime()), attemptMessage(() => nothing.toFixed(1))); diff --git a/test-files/test_gap_10476_builtin_named_user_methods.ts b/test-files/test_gap_10476_builtin_named_user_methods.ts new file mode 100644 index 0000000000..07b37e80b2 --- /dev/null +++ b/test-files/test_gap_10476_builtin_named_user_methods.ts @@ -0,0 +1,329 @@ +// #10476: a user method whose NAME matches a Date / Number / Array / String +// built-in must run on a receiver that is not that built-in. Pre-fix such a +// call was lowered to the built-in by name alone (NaN, "[object Object]", +// "Invalid time value", or a compile error for a zero-argument +// `endsWith`/`includes`/`startsWith`). This file covers class receivers: a +// class declares every name, directly and via `this`, typed and through `any`, +// plus `extends Date` subclasses with and without overrides. The sibling +// test_gap_10476_builtin_named_guarded_receivers.ts covers prototypes, object +// literals and `any` receivers in a module with no such class. +// Generated by a one-off script: one call site per (form, name, arity). + +const cmp = (a: number, b: number) => b - a; +const add = (acc: number, v: number) => acc + v; +const dup = (v: number) => [v, v]; + +function tag(form: string, name: string, args: any[]): string { + return form + "." + name + "/" + args.length; +} + +function show(v: any): string { + if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return String(v); + if (Array.isArray(v)) return "[" + v.join(",") + "]"; + return v === null ? "null" : typeof v; +} + +// Error messages for a missing method differ between engines; the name is +// what matters here. +function attempt(f: () => any): string { + try { + return show(f()); + } catch (e) { + return "THREW " + (e as Error).name; + } +} + +function attemptMessage(f: () => any): string { + try { + return show(f()); + } catch (e) { + return "THREW " + String(e); + } +} + +function row(label: string, ...fs: Array<() => any>): void { + console.log(label, fs.map(attempt).join(" ")); +} + +function viaAny(x: any, id: number): any { + switch (id) { + case 0: return x.getDate(); + case 1: return x.getDay(); + case 2: return x.getFullYear(); + case 3: return x.getHours(); + case 4: return x.getMilliseconds(); + case 5: return x.getMinutes(); + case 6: return x.getMonth(); + case 7: return x.getSeconds(); + case 8: return x.getTime(); + case 9: return x.getTimezoneOffset(); + case 10: return x.getUTCDate(); + case 11: return x.getUTCDay(); + case 12: return x.getUTCFullYear(); + case 13: return x.getUTCHours(); + case 14: return x.getUTCMilliseconds(); + case 15: return x.getUTCMinutes(); + case 16: return x.getUTCMonth(); + case 17: return x.getUTCSeconds(); + case 18: return x.setDate(1); + case 19: return x.setFullYear(1); + case 20: return x.setHours(1); + case 21: return x.setMilliseconds(1); + case 22: return x.setMinutes(1); + case 23: return x.setMonth(1); + case 24: return x.setSeconds(1); + case 25: return x.setTime(1); + case 26: return x.setUTCDate(1); + case 27: return x.setUTCFullYear(1); + case 28: return x.setUTCHours(1); + case 29: return x.setUTCMilliseconds(1); + case 30: return x.setUTCMinutes(1); + case 31: return x.setUTCMonth(1); + case 32: return x.setUTCSeconds(1); + case 33: return x.toISOString(); + case 34: return x.toDateString(); + case 35: return x.toTimeString(); + case 36: return x.toUTCString(); + case 37: return x.toGMTString(); + case 38: return x.toLocaleString(); + case 39: return x.toLocaleDateString(); + case 40: return x.toLocaleTimeString(); + case 41: return x.toFixed(2); + case 42: return x.toPrecision(2); + case 43: return x.toExponential(2); + case 44: return x.toFixed(); + case 45: return x.toSorted(); + case 46: return x.toSorted(cmp); + case 47: return x.toReversed(); + case 48: return x.reduceRight(add); + case 49: return x.flat(); + case 50: return x.flatMap(dup); + case 51: return x.toSpliced(1); + case 52: return x.toSpliced(0, 1); + case 53: return x.copyWithin(0, 1); + case 54: return x.endsWith(); + case 55: return x.includes(); + case 56: return x.startsWith(); + case 57: return x.endsWith('c'); + case 58: return x.includes('b'); + case 59: return x.startsWith('a'); + } +} + +class K { + getDate(...a: any[]): any { return tag("K", "getDate", a); } + getDay(...a: any[]): any { return tag("K", "getDay", a); } + getFullYear(...a: any[]): any { return tag("K", "getFullYear", a); } + getHours(...a: any[]): any { return tag("K", "getHours", a); } + getMilliseconds(...a: any[]): any { return tag("K", "getMilliseconds", a); } + getMinutes(...a: any[]): any { return tag("K", "getMinutes", a); } + getMonth(...a: any[]): any { return tag("K", "getMonth", a); } + getSeconds(...a: any[]): any { return tag("K", "getSeconds", a); } + getTime(...a: any[]): any { return tag("K", "getTime", a); } + getTimezoneOffset(...a: any[]): any { return tag("K", "getTimezoneOffset", a); } + getUTCDate(...a: any[]): any { return tag("K", "getUTCDate", a); } + getUTCDay(...a: any[]): any { return tag("K", "getUTCDay", a); } + getUTCFullYear(...a: any[]): any { return tag("K", "getUTCFullYear", a); } + getUTCHours(...a: any[]): any { return tag("K", "getUTCHours", a); } + getUTCMilliseconds(...a: any[]): any { return tag("K", "getUTCMilliseconds", a); } + getUTCMinutes(...a: any[]): any { return tag("K", "getUTCMinutes", a); } + getUTCMonth(...a: any[]): any { return tag("K", "getUTCMonth", a); } + getUTCSeconds(...a: any[]): any { return tag("K", "getUTCSeconds", a); } + setDate(...a: any[]): any { return tag("K", "setDate", a); } + setFullYear(...a: any[]): any { return tag("K", "setFullYear", a); } + setHours(...a: any[]): any { return tag("K", "setHours", a); } + setMilliseconds(...a: any[]): any { return tag("K", "setMilliseconds", a); } + setMinutes(...a: any[]): any { return tag("K", "setMinutes", a); } + setMonth(...a: any[]): any { return tag("K", "setMonth", a); } + setSeconds(...a: any[]): any { return tag("K", "setSeconds", a); } + setTime(...a: any[]): any { return tag("K", "setTime", a); } + setUTCDate(...a: any[]): any { return tag("K", "setUTCDate", a); } + setUTCFullYear(...a: any[]): any { return tag("K", "setUTCFullYear", a); } + setUTCHours(...a: any[]): any { return tag("K", "setUTCHours", a); } + setUTCMilliseconds(...a: any[]): any { return tag("K", "setUTCMilliseconds", a); } + setUTCMinutes(...a: any[]): any { return tag("K", "setUTCMinutes", a); } + setUTCMonth(...a: any[]): any { return tag("K", "setUTCMonth", a); } + setUTCSeconds(...a: any[]): any { return tag("K", "setUTCSeconds", a); } + toISOString(...a: any[]): any { return tag("K", "toISOString", a); } + toDateString(...a: any[]): any { return tag("K", "toDateString", a); } + toTimeString(...a: any[]): any { return tag("K", "toTimeString", a); } + toUTCString(...a: any[]): any { return tag("K", "toUTCString", a); } + toGMTString(...a: any[]): any { return tag("K", "toGMTString", a); } + toLocaleString(...a: any[]): any { return tag("K", "toLocaleString", a); } + toLocaleDateString(...a: any[]): any { return tag("K", "toLocaleDateString", a); } + toLocaleTimeString(...a: any[]): any { return tag("K", "toLocaleTimeString", a); } + toFixed(...a: any[]): any { return tag("K", "toFixed", a); } + toPrecision(...a: any[]): any { return tag("K", "toPrecision", a); } + toExponential(...a: any[]): any { return tag("K", "toExponential", a); } + toSorted(...a: any[]): any { return tag("K", "toSorted", a); } + toReversed(...a: any[]): any { return tag("K", "toReversed", a); } + reduceRight(...a: any[]): any { return tag("K", "reduceRight", a); } + flat(...a: any[]): any { return tag("K", "flat", a); } + flatMap(...a: any[]): any { return tag("K", "flatMap", a); } + toSpliced(...a: any[]): any { return tag("K", "toSpliced", a); } + copyWithin(...a: any[]): any { return tag("K", "copyWithin", a); } + endsWith(...a: any[]): any { return tag("K", "endsWith", a); } + includes(...a: any[]): any { return tag("K", "includes", a); } + startsWith(...a: any[]): any { return tag("K", "startsWith", a); } + viaThis(id: number): any { + switch (id) { + case 0: return this.getDate(); + case 1: return this.getDay(); + case 2: return this.getFullYear(); + case 3: return this.getHours(); + case 4: return this.getMilliseconds(); + case 5: return this.getMinutes(); + case 6: return this.getMonth(); + case 7: return this.getSeconds(); + case 8: return this.getTime(); + case 9: return this.getTimezoneOffset(); + case 10: return this.getUTCDate(); + case 11: return this.getUTCDay(); + case 12: return this.getUTCFullYear(); + case 13: return this.getUTCHours(); + case 14: return this.getUTCMilliseconds(); + case 15: return this.getUTCMinutes(); + case 16: return this.getUTCMonth(); + case 17: return this.getUTCSeconds(); + case 18: return this.setDate(1); + case 19: return this.setFullYear(1); + case 20: return this.setHours(1); + case 21: return this.setMilliseconds(1); + case 22: return this.setMinutes(1); + case 23: return this.setMonth(1); + case 24: return this.setSeconds(1); + case 25: return this.setTime(1); + case 26: return this.setUTCDate(1); + case 27: return this.setUTCFullYear(1); + case 28: return this.setUTCHours(1); + case 29: return this.setUTCMilliseconds(1); + case 30: return this.setUTCMinutes(1); + case 31: return this.setUTCMonth(1); + case 32: return this.setUTCSeconds(1); + case 33: return this.toISOString(); + case 34: return this.toDateString(); + case 35: return this.toTimeString(); + case 36: return this.toUTCString(); + case 37: return this.toGMTString(); + case 38: return this.toLocaleString(); + case 39: return this.toLocaleDateString(); + case 40: return this.toLocaleTimeString(); + case 41: return this.toFixed(2); + case 42: return this.toPrecision(2); + case 43: return this.toExponential(2); + case 44: return this.toFixed(); + case 45: return this.toSorted(); + case 46: return this.toSorted(cmp); + case 47: return this.toReversed(); + case 48: return this.reduceRight(add); + case 49: return this.flat(); + case 50: return this.flatMap(dup); + case 51: return this.toSpliced(1); + case 52: return this.toSpliced(0, 1); + case 53: return this.copyWithin(0, 1); + case 54: return this.endsWith(); + case 55: return this.includes(); + case 56: return this.startsWith(); + case 57: return this.endsWith('c'); + case 58: return this.includes('b'); + case 59: return this.startsWith('a'); + } + } +} + +class Sub extends K {} + +const k = new K(); +const sub = new Sub(); +const kAny: any = new K(); + +row("getDate()", () => k.getDate(), () => k.viaThis(0), () => sub.getDate(), () => kAny.getDate(), () => viaAny(k, 0)); +row("getDay()", () => k.getDay(), () => k.viaThis(1), () => sub.getDay(), () => kAny.getDay(), () => viaAny(k, 1)); +row("getFullYear()", () => k.getFullYear(), () => k.viaThis(2), () => sub.getFullYear(), () => kAny.getFullYear(), () => viaAny(k, 2)); +row("getHours()", () => k.getHours(), () => k.viaThis(3), () => sub.getHours(), () => kAny.getHours(), () => viaAny(k, 3)); +row("getMilliseconds()", () => k.getMilliseconds(), () => k.viaThis(4), () => sub.getMilliseconds(), () => kAny.getMilliseconds(), () => viaAny(k, 4)); +row("getMinutes()", () => k.getMinutes(), () => k.viaThis(5), () => sub.getMinutes(), () => kAny.getMinutes(), () => viaAny(k, 5)); +row("getMonth()", () => k.getMonth(), () => k.viaThis(6), () => sub.getMonth(), () => kAny.getMonth(), () => viaAny(k, 6)); +row("getSeconds()", () => k.getSeconds(), () => k.viaThis(7), () => sub.getSeconds(), () => kAny.getSeconds(), () => viaAny(k, 7)); +row("getTime()", () => k.getTime(), () => k.viaThis(8), () => sub.getTime(), () => kAny.getTime(), () => viaAny(k, 8)); +row("getTimezoneOffset()", () => k.getTimezoneOffset(), () => k.viaThis(9), () => sub.getTimezoneOffset(), () => kAny.getTimezoneOffset(), () => viaAny(k, 9)); +row("getUTCDate()", () => k.getUTCDate(), () => k.viaThis(10), () => sub.getUTCDate(), () => kAny.getUTCDate(), () => viaAny(k, 10)); +row("getUTCDay()", () => k.getUTCDay(), () => k.viaThis(11), () => sub.getUTCDay(), () => kAny.getUTCDay(), () => viaAny(k, 11)); +row("getUTCFullYear()", () => k.getUTCFullYear(), () => k.viaThis(12), () => sub.getUTCFullYear(), () => kAny.getUTCFullYear(), () => viaAny(k, 12)); +row("getUTCHours()", () => k.getUTCHours(), () => k.viaThis(13), () => sub.getUTCHours(), () => kAny.getUTCHours(), () => viaAny(k, 13)); +row("getUTCMilliseconds()", () => k.getUTCMilliseconds(), () => k.viaThis(14), () => sub.getUTCMilliseconds(), () => kAny.getUTCMilliseconds(), () => viaAny(k, 14)); +row("getUTCMinutes()", () => k.getUTCMinutes(), () => k.viaThis(15), () => sub.getUTCMinutes(), () => kAny.getUTCMinutes(), () => viaAny(k, 15)); +row("getUTCMonth()", () => k.getUTCMonth(), () => k.viaThis(16), () => sub.getUTCMonth(), () => kAny.getUTCMonth(), () => viaAny(k, 16)); +row("getUTCSeconds()", () => k.getUTCSeconds(), () => k.viaThis(17), () => sub.getUTCSeconds(), () => kAny.getUTCSeconds(), () => viaAny(k, 17)); +row("setDate(1)", () => k.setDate(1), () => k.viaThis(18), () => sub.setDate(1), () => kAny.setDate(1), () => viaAny(k, 18)); +row("setFullYear(1)", () => k.setFullYear(1), () => k.viaThis(19), () => sub.setFullYear(1), () => kAny.setFullYear(1), () => viaAny(k, 19)); +row("setHours(1)", () => k.setHours(1), () => k.viaThis(20), () => sub.setHours(1), () => kAny.setHours(1), () => viaAny(k, 20)); +row("setMilliseconds(1)", () => k.setMilliseconds(1), () => k.viaThis(21), () => sub.setMilliseconds(1), () => kAny.setMilliseconds(1), () => viaAny(k, 21)); +row("setMinutes(1)", () => k.setMinutes(1), () => k.viaThis(22), () => sub.setMinutes(1), () => kAny.setMinutes(1), () => viaAny(k, 22)); +row("setMonth(1)", () => k.setMonth(1), () => k.viaThis(23), () => sub.setMonth(1), () => kAny.setMonth(1), () => viaAny(k, 23)); +row("setSeconds(1)", () => k.setSeconds(1), () => k.viaThis(24), () => sub.setSeconds(1), () => kAny.setSeconds(1), () => viaAny(k, 24)); +row("setTime(1)", () => k.setTime(1), () => k.viaThis(25), () => sub.setTime(1), () => kAny.setTime(1), () => viaAny(k, 25)); +row("setUTCDate(1)", () => k.setUTCDate(1), () => k.viaThis(26), () => sub.setUTCDate(1), () => kAny.setUTCDate(1), () => viaAny(k, 26)); +row("setUTCFullYear(1)", () => k.setUTCFullYear(1), () => k.viaThis(27), () => sub.setUTCFullYear(1), () => kAny.setUTCFullYear(1), () => viaAny(k, 27)); +row("setUTCHours(1)", () => k.setUTCHours(1), () => k.viaThis(28), () => sub.setUTCHours(1), () => kAny.setUTCHours(1), () => viaAny(k, 28)); +row("setUTCMilliseconds(1)", () => k.setUTCMilliseconds(1), () => k.viaThis(29), () => sub.setUTCMilliseconds(1), () => kAny.setUTCMilliseconds(1), () => viaAny(k, 29)); +row("setUTCMinutes(1)", () => k.setUTCMinutes(1), () => k.viaThis(30), () => sub.setUTCMinutes(1), () => kAny.setUTCMinutes(1), () => viaAny(k, 30)); +row("setUTCMonth(1)", () => k.setUTCMonth(1), () => k.viaThis(31), () => sub.setUTCMonth(1), () => kAny.setUTCMonth(1), () => viaAny(k, 31)); +row("setUTCSeconds(1)", () => k.setUTCSeconds(1), () => k.viaThis(32), () => sub.setUTCSeconds(1), () => kAny.setUTCSeconds(1), () => viaAny(k, 32)); +row("toISOString()", () => k.toISOString(), () => k.viaThis(33), () => sub.toISOString(), () => kAny.toISOString(), () => viaAny(k, 33)); +row("toDateString()", () => k.toDateString(), () => k.viaThis(34), () => sub.toDateString(), () => kAny.toDateString(), () => viaAny(k, 34)); +row("toTimeString()", () => k.toTimeString(), () => k.viaThis(35), () => sub.toTimeString(), () => kAny.toTimeString(), () => viaAny(k, 35)); +row("toUTCString()", () => k.toUTCString(), () => k.viaThis(36), () => sub.toUTCString(), () => kAny.toUTCString(), () => viaAny(k, 36)); +row("toGMTString()", () => k.toGMTString(), () => k.viaThis(37), () => sub.toGMTString(), () => kAny.toGMTString(), () => viaAny(k, 37)); +row("toLocaleString()", () => k.toLocaleString(), () => k.viaThis(38), () => sub.toLocaleString(), () => kAny.toLocaleString(), () => viaAny(k, 38)); +row("toLocaleDateString()", () => k.toLocaleDateString(), () => k.viaThis(39), () => sub.toLocaleDateString(), () => kAny.toLocaleDateString(), () => viaAny(k, 39)); +row("toLocaleTimeString()", () => k.toLocaleTimeString(), () => k.viaThis(40), () => sub.toLocaleTimeString(), () => kAny.toLocaleTimeString(), () => viaAny(k, 40)); +row("toFixed(2)", () => k.toFixed(2), () => k.viaThis(41), () => sub.toFixed(2), () => kAny.toFixed(2), () => viaAny(k, 41)); +row("toPrecision(2)", () => k.toPrecision(2), () => k.viaThis(42), () => sub.toPrecision(2), () => kAny.toPrecision(2), () => viaAny(k, 42)); +row("toExponential(2)", () => k.toExponential(2), () => k.viaThis(43), () => sub.toExponential(2), () => kAny.toExponential(2), () => viaAny(k, 43)); +row("toFixed()", () => k.toFixed(), () => k.viaThis(44), () => sub.toFixed(), () => kAny.toFixed(), () => viaAny(k, 44)); +row("toSorted()", () => k.toSorted(), () => k.viaThis(45), () => sub.toSorted(), () => kAny.toSorted(), () => viaAny(k, 45)); +row("toSorted(cmp)", () => k.toSorted(cmp), () => k.viaThis(46), () => sub.toSorted(cmp), () => kAny.toSorted(cmp), () => viaAny(k, 46)); +row("toReversed()", () => k.toReversed(), () => k.viaThis(47), () => sub.toReversed(), () => kAny.toReversed(), () => viaAny(k, 47)); +row("reduceRight(add)", () => k.reduceRight(add), () => k.viaThis(48), () => sub.reduceRight(add), () => kAny.reduceRight(add), () => viaAny(k, 48)); +row("flat()", () => k.flat(), () => k.viaThis(49), () => sub.flat(), () => kAny.flat(), () => viaAny(k, 49)); +row("flatMap(dup)", () => k.flatMap(dup), () => k.viaThis(50), () => sub.flatMap(dup), () => kAny.flatMap(dup), () => viaAny(k, 50)); +row("toSpliced(1)", () => k.toSpliced(1), () => k.viaThis(51), () => sub.toSpliced(1), () => kAny.toSpliced(1), () => viaAny(k, 51)); +row("toSpliced(0, 1)", () => k.toSpliced(0, 1), () => k.viaThis(52), () => sub.toSpliced(0, 1), () => kAny.toSpliced(0, 1), () => viaAny(k, 52)); +row("copyWithin(0, 1)", () => k.copyWithin(0, 1), () => k.viaThis(53), () => sub.copyWithin(0, 1), () => kAny.copyWithin(0, 1), () => viaAny(k, 53)); +row("endsWith()", () => k.endsWith(), () => k.viaThis(54), () => sub.endsWith(), () => kAny.endsWith(), () => viaAny(k, 54)); +row("includes()", () => k.includes(), () => k.viaThis(55), () => sub.includes(), () => kAny.includes(), () => viaAny(k, 55)); +row("startsWith()", () => k.startsWith(), () => k.viaThis(56), () => sub.startsWith(), () => kAny.startsWith(), () => viaAny(k, 56)); +row("endsWith('c')", () => k.endsWith('c'), () => k.viaThis(57), () => sub.endsWith('c'), () => kAny.endsWith('c'), () => viaAny(k, 57)); +row("includes('b')", () => k.includes('b'), () => k.viaThis(58), () => sub.includes('b'), () => kAny.includes('b'), () => viaAny(k, 58)); +row("startsWith('a')", () => k.startsWith('a'), () => k.viaThis(59), () => sub.startsWith('a'), () => kAny.startsWith('a'), () => viaAny(k, 59)); + +// A Date subclass is a Date at runtime; its own overrides must still win. +class OverDate extends Date { + getTime(): any { return "OverDate.getTime"; } + toISOString(): any { return "OverDate.toISOString"; } + setUTCHours(h: number): any { return "OverDate.setUTCHours/" + h; } + toFixed(d: number): any { return "OverDate.toFixed/" + d; } +} +class PlainDate extends Date {} +const od = new OverDate(86400000); +const odAny: any = new OverDate(86400000); +const pd = new PlainDate(86400000); +const pdAny: any = new PlainDate(86400000); +row("OverDate", () => od.getTime(), () => od.toISOString(), () => od.setUTCHours(3), () => od.getUTCDate(), () => od.toFixed(1)); +row("OverDate any", () => odAny.getTime(), () => odAny.toISOString(), () => odAny.setUTCHours(3), () => odAny.getUTCDate()); +row("PlainDate", () => pd.getTime(), () => pd.toISOString(), () => pd.getUTCDate(), () => pdAny.getTime(), () => pdAny.toISOString(), () => viaAny(pdAny, 33)); + +// Real built-ins through `any` in a module whose classes declare the names. +const du: any = new Date(Date.UTC(2024, 5, 15, 10, 20, 30, 456)); +const nu: any = 1234.5678; +const au: any = [5, 4, 6]; +const su: any = "xundefined"; +row("builtins any", () => du.getTime(), () => du.toISOString(), () => du.getUTCHours(), () => nu.toFixed(2), () => nu.toPrecision(3), () => au.toSorted(), () => au.toReversed(), () => au.reduceRight(add), () => su.endsWith(), () => su.includes("und")); +{ const t: any = new Date(0); row("builtins any setter", () => t.setUTCHours(5), () => t.toISOString(), () => viaAny(t, 30)); } +// One call site per name, the matching built-in and class instances in turn. +for (const m of [new Date(0), k, sub] as any[]) row("mixed getTime", () => viaAny(m, 8)); +for (const m of [pd, k, sub] as any[]) row("mixed toISOString", () => viaAny(m, 33)); +for (const m of [7, k, sub] as any[]) row("mixed toFixed", () => viaAny(m, 41)); +for (const m of [[3, 1, 2], k, sub] as any[]) row("mixed toSorted", () => viaAny(m, 45)); +for (const m of ["xundefined", k, sub] as any[]) row("mixed endsWith", () => viaAny(m, 54));