diff --git a/changelog.d/7009-intl-tolocale-gate.md b/changelog.d/7009-intl-tolocale-gate.md new file mode 100644 index 0000000000..f52644905c --- /dev/null +++ b/changelog.d/7009-intl-tolocale-gate.md @@ -0,0 +1 @@ +**Binary size:** the `Date.prototype.toLocale{,Date,Time}String` implementation and the `class X extends Intl.` super probe in the fetch-globals construct path now sit behind the existing `intl-namespace` feature. These were the last two always-live references into the Intl formatting web: the Date prototype is installed in every binary, so its locale thunks pinned ~99 KB of Intl machinery even in programs with no locale API. The compiler's `intl-namespace` detection already covers every `toLocale*` token, so any program that can reach these keeps them; with the feature off the Date thunks defer to the non-locale formatter (unreachable in practice) and the Intl subclass probe can never match. Hello world: −99 KB (4,344,464 → 4,245,216 bytes). diff --git a/crates/perry-runtime/src/object/date_proto_thunks.rs b/crates/perry-runtime/src/object/date_proto_thunks.rs index 503e1419c5..c58a56c389 100644 --- a/crates/perry-runtime/src/object/date_proto_thunks.rs +++ b/crates/perry-runtime/src/object/date_proto_thunks.rs @@ -500,6 +500,19 @@ date_setter_thunk!(date_set_utc_milliseconds, 1, 6); /// both. A `Date` is a specific INSTANT, so we shift its epoch into the resolved /// zone's wall clock (the `timeZone` option, else the host zone) before /// formatting it as the now-zone-agnostic date-time. +/// Locale-aware `Date.prototype.toLocale*String`. Behind `intl-namespace` +/// (which the compiler enables on any `toLocale*` token, among others): with +/// the feature off no program in this binary can call these thunks, so the +/// fallback simply defers to the non-locale formatter instead of statically +/// pinning the Intl formatting web from the always-installed Date prototype. +#[cfg(not(feature = "intl-namespace"))] +fn date_to_locale_opts_impl(_rest: f64, _ctx: crate::intl::TemporalLocaleCtx) -> f64 { + let this = require_date_this(); + let s = crate::date::js_date_to_locale_string(this); + crate::value::js_nanbox_string(s as i64) +} + +#[cfg(feature = "intl-namespace")] fn date_to_locale_opts_impl(rest: f64, ctx: crate::intl::TemporalLocaleCtx) -> f64 { let this = require_date_this(); let epoch_ms = crate::date::date_cell_timestamp(this); diff --git a/crates/perry-runtime/src/object/global_this/fetch_globals.rs b/crates/perry-runtime/src/object/global_this/fetch_globals.rs index 3ebbe2c3fd..5da05351a0 100644 --- a/crates/perry-runtime/src/object/global_this/fetch_globals.rs +++ b/crates/perry-runtime/src/object/global_this/fetch_globals.rs @@ -589,6 +589,10 @@ pub unsafe extern "C" fn js_fetch_or_value_super( // and construct it with `new.target` set, re-homing the instance's brand + // methods onto `this`. `parent_val` can arrive stale for an aliased heritage // (`const L = Intl.Locale; class X extends L`); recover the decl-time parent. + // Behind `intl-namespace`: with the feature off no Intl constructor value + // exists, so this probe can never match — and skipping it keeps this + // always-live construct path from pinning the Intl web. + #[cfg(feature = "intl-namespace")] { let intl_parent = if crate::intl::is_intl_constructor_value(parent_val) { parent_val