Skip to content

feat(compile): per-app feature gating for regex/Temporal/URL/normalize/segmenter (binary size)#5153

Merged
proggeramlug merged 1 commit into
mainfrom
feat/binary-size-feature-gating
Jun 14, 2026
Merged

feat(compile): per-app feature gating for regex/Temporal/URL/normalize/segmenter (binary size)#5153
proggeramlug merged 1 commit into
mainfrom
feat/binary-size-feature-gating

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

What

Gate five heavy runtime subsystems behind opt-in cargo features so a compiled binary links only what the program actually uses. Mirrors the existing wasm-host mechanism: the compiler detects usage in HIR and forwards perry-runtime/<feature> to the auto-optimize runtime build.

Feature Crates Size Auto-enabled when
regex-engine regex + fancy-regex ~1.2 MB regex literal / RegExp / .match/.matchAll/.search / glob API
temporal temporal_rs + tz/calendar deps ~580 KB Temporal.* (JS Date is a separate impl — unaffected)
url-engine url + idna (+ transitive percent_encoding) ~195 KB new URL / URLSearchParams / URLPattern / node:url
string-normalize unicode-normalization ~113 KB String.prototype.normalize
intl-segmenter unicode-segmentation ~73 KB Intl.Segmenter

Result

A console.log hello-world drops ~2.5 MB (all five gates off). Each engine auto-enables on use with identical behavior. No speed trade-off — this is pure dead-code elimination for paths a given program never reaches (no opt-level change).

Verified per-engine (clean build off this base): hello/string-ops/Date 5.5 MB · regex 6.9 MB (12/34, a#b#c#, …) · Temporal 6.3 MB (2020) · URL 5.7 MB (example.com 1) · normalize 5.6 MB (1). Date and AbortController (which share modules with gated code) work with the engines off.

How

  • Detection (collect_modules): ctx.uses_* flags set by grepping the HIR Debug form for the relevant tokens; forwarded + baked into the auto-optimize cache key (optimized_libs).
  • Runtime: each engine keeps its identity/display layer always compiled (value formatting / JSON / instanceof on non-engine values still work); the engine code + the engine branches inside always-linked dispatchers are #[cfg]-gated with the existing non-engine path as the fallback.
  • Feature-unification fix: the workspace perry-runtime dep is default-features = false so dependency edges (perry-stdlib, ext crates, the perry binary) don't force the heavy default features back on during the auto-optimize --no-default-features build. Plain cargo build / cargo test --workspace and the shipped prebuilt still build perry-runtime as a selected package, so its own default applies and they keep every engine (test coverage + out-of-tree correctness preserved).

All five features are in default, so behavior is unchanged for any build that isn't the per-app auto-optimize path.

Summary by CodeRabbit

Release Notes

  • Chores
    • Builds now auto-detect whether you use RegExp/regex, Temporal, WHATWG URL APIs, String.prototype.normalize, and Intl.Segmenter, and include only the needed runtime capabilities.
    • Optimized build caching now incorporates these capability selections to prevent reusing incompatible build outputs.
    • When a capability isn’t included, related functionality is omitted or safely falls back (e.g., regex/Temporal-specific handling during formatting, cloning, glob/path matching, and normalization/segmentation), producing smaller outputs.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ef96cd80-a533-4d88-afb1-092ac65289ac

📥 Commits

Reviewing files that changed from the base of the PR and between 3241f2a and fc2bb4c.

📒 Files selected for processing (36)
  • Cargo.toml
  • crates/perry-runtime/Cargo.toml
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/builtins/formatting.rs
  • crates/perry-runtime/src/builtins/globals.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry-runtime/src/fs/dir_glob_watch.rs
  • crates/perry-runtime/src/intl.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/object/assert.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • crates/perry-runtime/src/object/field_get_set.rs
  • crates/perry-runtime/src/object/global_this.rs
  • crates/perry-runtime/src/object/iterator_prototypes.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/object/object_ops.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/regex.rs
  • crates/perry-runtime/src/regex/match_all.rs
  • crates/perry-runtime/src/regex/replace_fn.rs
  • crates/perry-runtime/src/string/compare.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry-runtime/src/string/split.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry-runtime/src/temporal/mod.rs
  • crates/perry-runtime/src/url/mod.rs
  • crates/perry-runtime/src/url/node_compat.rs
  • crates/perry-runtime/src/url/url_class.rs
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/src/value/to_string.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/src/commands/compile/optimized_libs.rs
  • crates/perry/src/commands/compile/types.rs
  • scripts/check_file_size.sh
✅ Files skipped from review due to trivial changes (1)
  • crates/perry-runtime/src/object/mod.rs
🚧 Files skipped from review as they are similar to previous changes (32)
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/src/object/object_ops.rs
  • crates/perry-runtime/src/builtins/globals.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • crates/perry-runtime/src/string/compare.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/string/split.rs
  • crates/perry-runtime/src/object/field_get_set.rs
  • crates/perry-runtime/src/object/iterator_prototypes.rs
  • crates/perry-runtime/src/builtins/formatting.rs
  • crates/perry-runtime/src/object/assert.rs
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry/src/commands/compile/optimized_libs.rs
  • crates/perry-runtime/src/url/url_class.rs
  • Cargo.toml
  • crates/perry-runtime/src/url/mod.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/url/node_compat.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry-runtime/src/value/to_string.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry/src/commands/compile/types.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/object/global_this.rs
  • crates/perry-runtime/src/regex/replace_fn.rs
  • crates/perry-runtime/src/intl.rs
  • crates/perry-runtime/src/temporal/mod.rs
  • crates/perry-runtime/src/regex.rs
  • crates/perry-runtime/src/fs/dir_glob_watch.rs

📝 Walkthrough

Walkthrough

Introduces five Cargo feature flags (regex-engine, temporal, url-engine, string-normalize, intl-segmenter) in perry-runtime, marking the corresponding dependencies optional and wrapping all subsystem code with #[cfg(feature = "...")] attributes. The compiler gains HIR-based capability detection that sets flags on CompilationContext, which the auto-optimize build then uses to select only the required features and invalidate the build cache when they change.

Changes

Optional runtime subsystem feature gating

Layer / File(s) Summary
Cargo feature and dependency declarations
Cargo.toml, crates/perry-runtime/Cargo.toml
Root workspace disables perry-runtime default features. perry-runtime expands its default feature list to explicitly include each subsystem feature and marks regex, fancy-regex, temporal_rs, unicode-normalization, unicode-segmentation, idna, and url as optional = true.
CompilationContext capability flags, HIR detection, and build wiring
crates/perry/src/commands/compile/types.rs, crates/perry/src/commands/compile/collect_modules.rs, crates/perry/src/commands/compile/optimized_libs.rs
CompilationContext gains five uses_* boolean fields initialized to false. collect_module_finish scans the HIR Debug output for API-specific tokens to set those flags. build_optimized_libs hashes the flags into the cache key and conditionally passes --features to cargo.
Temporal subsystem feature gating
crates/perry-runtime/src/temporal/mod.rs, crates/perry-runtime/src/object/global_this.rs, crates/perry-runtime/src/object/mod.rs, crates/perry-runtime/src/object/native_call_method.rs, crates/perry-runtime/src/builtins/formatting.rs, crates/perry-runtime/src/builtins/arithmetic.rs, crates/perry-runtime/src/date.rs, crates/perry-runtime/src/json/stringify.rs, crates/perry-runtime/src/symbol.rs, crates/perry-runtime/src/value/..., crates/perry-runtime/src/object/field_get_set.rs, crates/perry-runtime/src/object/object_ops.rs
temporal/mod.rs introduces an uninhabited TemporalValue fallback and no-op GC hooks. All Temporal constructor/prototype thunks, namespace installation, and every dispatch callsite across the runtime are wrapped with #[cfg(feature = "temporal")]; temporal_ctor_kind gains a #[cfg(not(...))] stub returning None.
regex.rs, match_all.rs, and replace_fn.rs feature gating
crates/perry-runtime/src/regex.rs, crates/perry-runtime/src/regex/match_all.rs, crates/perry-runtime/src/regex/replace_fn.rs
regex.rs introduces CompiledRegex (unit type when off), updates RegExpHeader.regex_ptr, gates thread-local caches and every FFI entrypoint. match_all.rs moves REGEXP_STRING_ITERATOR_CLASS_ID to the parent module (ungated) and imports from super. replace_fn.rs gates js_string_replace_regex_dyn and js_string_replace_all_regex_dyn and regex-specific dispatch branches.
Regex-engine feature gating in object/string/path/fs dispatch
crates/perry-runtime/src/object/assert.rs, crates/perry-runtime/src/object/class_registry.rs, crates/perry-runtime/src/object/regex_proto_thunks.rs, crates/perry-runtime/src/object/native_call_method.rs, crates/perry-runtime/src/object/iterator_prototypes.rs, crates/perry-runtime/src/builtins/globals.rs, crates/perry-runtime/src/string/mod.rs, crates/perry-runtime/src/string/split.rs, crates/perry-runtime/src/path.rs, crates/perry-runtime/src/fs/dir_glob_watch.rs
RegExp constructor dispatch, exec/test proto thunks, iterator dispatch, structured clone, string match/search/replace/split regex paths, and spec_regex_split are all wrapped with #[cfg(feature = "regex-engine")]. Glob-to-regex helpers, path-matching APIs, and run_fs_glob_result are gated; when the engine is absent, matching functions return no match and glob operations return empty results.
url-engine, string-normalize, and intl-segmenter feature gating
crates/perry-runtime/src/url/mod.rs, crates/perry-runtime/src/url/node_compat.rs, crates/perry-runtime/src/url/url_class.rs, crates/perry-runtime/src/string/compare.rs, crates/perry-runtime/src/intl.rs
WHATWG host canonicalization and legacy URL joining are gated behind url-engine; IDNA domain conversions are gated appropriately. Unicode normalization computation in js_string_normalize is gated behind string-normalize (form validation is preserved). build_segments is gated behind intl-segmenter with a char-by-char fallback when off.
CI allowlist exemption
scripts/check_file_size.sh
Adds crates/perry-runtime/src/regex.rs to the file-size allowlist, exempting it from LOC-threshold violations.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 Hoppity-hop through the feature gate,
Regex and Temporal wait their fate,
With cfg flags we trim the weight,
Only what's needed ships in the crate!
Lighter builds arrive — isn't that great?

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: implementing per-app feature gating for five runtime subsystems to reduce binary size, with specific mention of the subsystems and the end goal.
Description check ✅ Passed The description provides comprehensive coverage of the changes including a summary section, detailed changes across multiple files, implementation approach, and verification results. All required template sections are present and meaningfully filled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/binary-size-feature-gating

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/perry-runtime/src/object/global_this.rs (1)

5005-5014: ⚠️ Potential issue | 🟠 Major

Verify that Temporal is conditionally excluded from globalThis when the temporal feature is disabled.

When feature = "temporal" is disabled, "Temporal" remains in the GLOBAL_THIS_BUILTIN_NAMESPACES array (line 178 of crates/perry-runtime/src/object/global_this_tables.rs). The namespace-installation loop at line 4968 still processes it, but the cfg-gated match arm at lines 5005–5009 is compiled out, leaving the default _ => {} handler. This installs an empty namespace object on globalThis.Temporal instead of omitting it entirely.

Either:

  • Move the cfg guard to GLOBAL_THIS_BUILTIN_NAMESPACES itself (conditionally include "Temporal" only when enabled), or
  • Ensure the match arm handles the feature-disabled case explicitly (e.g., #[cfg(not(feature = "temporal"))] "Temporal" => skip_or_error)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/global_this.rs` around lines 5005 - 5014, The
`"Temporal"` entry in the `GLOBAL_THIS_BUILTIN_NAMESPACES` array is processed
unconditionally during namespace installation, but the corresponding match arm
for `"Temporal"` (which calls `install_temporal_namespace`) is gated behind
`#[cfg(feature = "temporal")]`. When the temporal feature is disabled, this
match arm is compiled out, causing the default case `_ => {}` to install an
empty namespace object instead of excluding it. To fix this, either
conditionally include `"Temporal"` in the `GLOBAL_THIS_BUILTIN_NAMESPACES` array
itself by wrapping its declaration with `#[cfg(feature = "temporal")]`, or add
an explicit feature-gated match arm for the `"Temporal"` case that handles both
the enabled case (with `install_temporal_namespace`) and the disabled case (with
an appropriate skip or error handler).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Cargo.toml`:
- Around line 286-296: Increment the patch version in the [workspace.package]
section of Cargo.toml from 0.5.1168 to 0.5.1169 to reflect the change made to
the perry-runtime dependency configuration, as required by the repository rule
for all modifications landing on main.

In `@crates/perry-runtime/src/intl.rs`:
- Around line 640-651: The fallback implementation in the intl-segmenter feature
gate is passing None for the word_like parameter to make_segment_record on every
call, causing the isWordLike property to be missing from segment records when
word granularity is used. Instead of always passing None, check if the
granularity is "word" and if so, determine whether the segment is word-like
(typically by checking if it contains word characters) and pass that computed
value to make_segment_record; for other granularities, continue passing None to
maintain consistency with the feature-enabled path.

In `@crates/perry/src/commands/compile/collect_modules.rs`:
- Around line 1870-1911: The current implementation in the Temporal, URL, and
Segmenter detectors uses overly broad substring matching on the HIR debug
output, which can trigger false positives and unnecessarily enable heavy runtime
features. Replace the generic substring checks (such as "Temporal", "Url"/"URL",
and "Segmenter") with more targeted detection logic that matches
feature-specific HIR tokens instead. Consider either identifying unique HIR
token patterns specific to each feature or implementing a targeted walk through
the HIR structure (hir_module.init and hir_module.functions) rather than relying
on substring matches of the entire debug format string.

---

Outside diff comments:
In `@crates/perry-runtime/src/object/global_this.rs`:
- Around line 5005-5014: The `"Temporal"` entry in the
`GLOBAL_THIS_BUILTIN_NAMESPACES` array is processed unconditionally during
namespace installation, but the corresponding match arm for `"Temporal"` (which
calls `install_temporal_namespace`) is gated behind `#[cfg(feature =
"temporal")]`. When the temporal feature is disabled, this match arm is compiled
out, causing the default case `_ => {}` to install an empty namespace object
instead of excluding it. To fix this, either conditionally include `"Temporal"`
in the `GLOBAL_THIS_BUILTIN_NAMESPACES` array itself by wrapping its declaration
with `#[cfg(feature = "temporal")]`, or add an explicit feature-gated match arm
for the `"Temporal"` case that handles both the enabled case (with
`install_temporal_namespace`) and the disabled case (with an appropriate skip or
error handler).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7398d1a8-bb88-4997-9c66-ef712a612cdd

📥 Commits

Reviewing files that changed from the base of the PR and between f493cd3 and 6643a66.

📒 Files selected for processing (35)
  • Cargo.toml
  • crates/perry-runtime/Cargo.toml
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/builtins/formatting.rs
  • crates/perry-runtime/src/builtins/globals.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry-runtime/src/fs/dir_glob_watch.rs
  • crates/perry-runtime/src/intl.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/object/assert.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • crates/perry-runtime/src/object/field_get_set.rs
  • crates/perry-runtime/src/object/global_this.rs
  • crates/perry-runtime/src/object/iterator_prototypes.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/object/object_ops.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/regex.rs
  • crates/perry-runtime/src/regex/match_all.rs
  • crates/perry-runtime/src/regex/replace_fn.rs
  • crates/perry-runtime/src/string/compare.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry-runtime/src/string/split.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry-runtime/src/temporal/mod.rs
  • crates/perry-runtime/src/url/mod.rs
  • crates/perry-runtime/src/url/node_compat.rs
  • crates/perry-runtime/src/url/url_class.rs
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/src/value/to_string.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/src/commands/compile/optimized_libs.rs
  • crates/perry/src/commands/compile/types.rs

Comment thread Cargo.toml
Comment thread crates/perry-runtime/src/intl.rs
Comment thread crates/perry/src/commands/compile/collect_modules.rs
@proggeramlug proggeramlug force-pushed the feat/binary-size-feature-gating branch 3 times, most recently from 5944d40 to 3241f2a Compare June 14, 2026 19:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/perry-runtime/src/regex.rs (1)

421-440: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Store an owned pattern header here too.

js_regexp_new now materializes flags_ptr, but pattern_ptr still points at the caller's StringHeader. The fancy-regex path later dereferences (*re).pattern_ptr in lookup_fancy_regex, so a RegExp built from a temporary pattern can still read freed memory on the next exec/search/match/replace/split. Either allocate a stable pattern_ptr here as well, or switch the fancy-cache lookup to REGEX_SOURCE_TABLE.

💡 Suggested fix
-    let canonical_flags_ptr = js_string_from_str(flags_str);
+    let owned_pattern_ptr = js_string_from_str(pattern_str);
+    let canonical_flags_ptr = js_string_from_str(flags_str);
@@
-        (*ptr).pattern_ptr = pattern;
+        (*ptr).pattern_ptr = owned_pattern_ptr;
         (*ptr).flags_ptr = canonical_flags_ptr;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/regex.rs` around lines 421 - 440, The issue is that
in the `js_regexp_new` function, the `pattern_ptr` is stored directly from the
caller's temporary input without materializing a stable, owned copy (unlike
`canonical_flags_ptr` which is properly materialized). This causes
use-after-free when `lookup_fancy_regex` later dereferences `(*re).pattern_ptr`.
Materialize a stable pattern header by calling `js_string_from_str` on the
pattern string (similar to how `canonical_flags_ptr` is created from
`flags_str`), and then store that stable pointer in `(*ptr).pattern_ptr` instead
of storing the caller's temporary pattern directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/perry-runtime/src/regex.rs`:
- Around line 421-440: The issue is that in the `js_regexp_new` function, the
`pattern_ptr` is stored directly from the caller's temporary input without
materializing a stable, owned copy (unlike `canonical_flags_ptr` which is
properly materialized). This causes use-after-free when `lookup_fancy_regex`
later dereferences `(*re).pattern_ptr`. Materialize a stable pattern header by
calling `js_string_from_str` on the pattern string (similar to how
`canonical_flags_ptr` is created from `flags_str`), and then store that stable
pointer in `(*ptr).pattern_ptr` instead of storing the caller's temporary
pattern directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c967a29-43c4-4715-9c5c-e0c41e053c0d

📥 Commits

Reviewing files that changed from the base of the PR and between e1dc8db and 5944d40.

📒 Files selected for processing (36)
  • Cargo.toml
  • crates/perry-runtime/Cargo.toml
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/builtins/formatting.rs
  • crates/perry-runtime/src/builtins/globals.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry-runtime/src/fs/dir_glob_watch.rs
  • crates/perry-runtime/src/intl.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/object/assert.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • crates/perry-runtime/src/object/field_get_set.rs
  • crates/perry-runtime/src/object/global_this.rs
  • crates/perry-runtime/src/object/iterator_prototypes.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/object/object_ops.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/regex.rs
  • crates/perry-runtime/src/regex/match_all.rs
  • crates/perry-runtime/src/regex/replace_fn.rs
  • crates/perry-runtime/src/string/compare.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry-runtime/src/string/split.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry-runtime/src/temporal/mod.rs
  • crates/perry-runtime/src/url/mod.rs
  • crates/perry-runtime/src/url/node_compat.rs
  • crates/perry-runtime/src/url/url_class.rs
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/src/value/to_string.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/src/commands/compile/optimized_libs.rs
  • crates/perry/src/commands/compile/types.rs
  • scripts/check_file_size.sh
✅ Files skipped from review due to trivial changes (1)
  • crates/perry-runtime/src/symbol.rs
🚧 Files skipped from review as they are similar to previous changes (33)
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/object/iterator_prototypes.rs
  • crates/perry-runtime/src/regex/match_all.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry-runtime/src/string/split.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/url/mod.rs
  • crates/perry-runtime/src/object/object_ops.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • Cargo.toml
  • crates/perry-runtime/src/object/field_get_set.rs
  • crates/perry-runtime/src/builtins/globals.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/string/compare.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry/src/commands/compile/optimized_libs.rs
  • crates/perry-runtime/src/builtins/formatting.rs
  • crates/perry-runtime/src/object/assert.rs
  • crates/perry-runtime/src/value/to_string.rs
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/Cargo.toml
  • crates/perry-runtime/src/url/node_compat.rs
  • crates/perry-runtime/src/temporal/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/url/url_class.rs
  • crates/perry/src/commands/compile/types.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/intl.rs
  • crates/perry-runtime/src/regex/replace_fn.rs
  • crates/perry-runtime/src/object/global_this.rs
  • crates/perry-runtime/src/fs/dir_glob_watch.rs

…e/segmenter

Gate five heavy runtime subsystems behind opt-in cargo features so a compiled
binary links only what the program actually uses. Mirrors the existing
`wasm-host` mechanism: the compiler detects usage in HIR and forwards
`perry-runtime/<feature>` to the auto-optimize runtime build.

  regex-engine     regex + fancy-regex                       ~1.2 MB
  temporal         temporal_rs + tz/calendar deps            ~580 KB  (JS Date is a separate impl)
  url-engine       url + idna + transitive percent_encoding  ~195 KB
  string-normalize unicode-normalization                     ~113 KB
  intl-segmenter   unicode-segmentation                      ~73 KB

A `console.log` hello-world drops ~2.5 MB (all five gates off); each engine
auto-enables on use with identical behavior. No speed trade-off — this is pure
dead-code elimination for code paths a given program never reaches.

Detection (collect_modules) sets ctx.uses_* flags by grepping the HIR Debug
form for the relevant tokens (regex literal / RegExp / .match/.matchAll/.search
/ glob; Temporal; URL/URLSearchParams/URLPattern; .normalize; Intl.Segmenter),
forwarded + baked into the auto-optimize cache key in optimized_libs.

Each engine keeps its identity/display layer always compiled (so value
formatting / JSON / instanceof on non-engine values still work); the engine
code and the engine branches inside always-linked dispatchers are #[cfg]-gated
with the existing non-engine path as the fallback.

The workspace perry-runtime dependency is set to default-features=false so
dependency edges (perry-stdlib, ext crates, the perry binary) don't force the
heavy default features back on during the auto-optimize --no-default-features
build (cargo unifies features additively). Plain `cargo build` /
`cargo test --workspace` and the shipped prebuilt still build perry-runtime as
a *selected* package, so its own `default` applies and they keep every engine.
@proggeramlug proggeramlug force-pushed the feat/binary-size-feature-gating branch from 3241f2a to fc2bb4c Compare June 14, 2026 19:54
@proggeramlug proggeramlug merged commit c7fbadf into main Jun 14, 2026
15 checks passed
@proggeramlug proggeramlug deleted the feat/binary-size-feature-gating branch June 14, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant