Skip to content

fix(compile): robust fetch detection for minified bundles + drop runtime fetch stubs when web-fetch is linked#5679

Merged
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix-fetch-detection-minified
Jun 25, 2026
Merged

fix(compile): robust fetch detection for minified bundles + drop runtime fetch stubs when web-fetch is linked#5679
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix-fetch-detection-minified

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Problem

A minified bundle that uses fetch / Headers / Request segfaults at startup: the real fetch implementation isn't linked, only the no-op runtime stub, which returns garbage the caller dereferences → EXC_BAD_ACCESS in js_object_get_class_id.

Two root causes:

  1. uses_fetch detection is shape-fragile. perry-stdlib's real fetch (web-fetch) is linked only when uses_fetch is set, which happens at ~30 shape-specific sites scattered through HIR lowering (expr_new.rs, destructuring/*, …). A minified bundle's new Headers() / new Request() / fetch(...) can reach codegen as Expr::New { class_name: "Headers" } / a Fetch* HIR variant — codegen correctly dispatches those to js_headers_new / js_request_new / js_fetch_with_optionswithout having hit any uses_fetch set-site. So uses_fetch stays false, web-fetch is stripped, and only the stub remains.

  2. The runtime fetch stubs are never compiled out. The js_fetch_* / js_headers_new / js_request_new stubs in perry-runtime are gated #[cfg(not(feature = "external-fetch-symbols"))], but external-fetch-symbols was enabled nowhere in the build. So whenever web-fetch is linked, both the stub (perry-runtime) and the real (perry-stdlib) symbols exist, and on the fresh build path the stub has won the link.

Fix

  1. Robust fetch detection (feature_detect.rs): a post-lowering token scan of the final HIR for the fetch web-platform constructors (class_name: "Headers"|"Request"|"Response"|"FormData"|"Blob"|"File") and the dedicated fetch call variants (FetchWithOptions / FetchGetWithAuth / FetchPostWithAuth) → set uses_fetch. This mirrors the existing EventEmitter / URL token-scan fallbacks in the same file. Over-matching only over-links web-fetch (a size cost); the rule is zero false negatives.

  2. Drop the stub when the real is linked (freshness.rs, auto_optimized_cross_features): if ctx.uses_fetch { push("perry-runtime/external-fetch-symbols") }, mirroring the existing per-feature lines (regex-engine, url-engine, …). This compiles the stubs out of libperry_runtime.a, leaving only perry-stdlib's real symbols — no dup-symbol gamble.

Both are required together: fix 1 links web-fetch but leaves the dup; fix 2 never fires without fix 1.

Verification

A large minified CLI bundle that uses fetch/Headers/Request (7× new Headers(), 9× fetch() previously linked only the stub and printed [perry] warning: js_headers_new is a no-op stub … (runtime-only build). With both fixes, uses_fetch is detected, the stub warnings are gone, and the real web-fetch symbols are linked.

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection of fetch-related code so the correct web-fetch runtime support is kept enabled in more cases.
    • Prevented fresh builds from incorrectly selecting placeholder fetch symbols, reducing missing-runtime issues and link problems.

…ime fetch stubs when web-fetch is linked

A minified bundle's new Headers()/new Request()/fetch() can reach codegen as
Expr::New{class_name:"Headers"} / a Fetch* variant without hitting any of the
~30 shape-specific ctx.uses_fetch set-sites, so web-fetch is stripped and only the
no-op stub remains -> it returns garbage the caller derefs -> SIGSEGV. Add a
post-lowering token scan (mirrors the EventEmitter/URL fallbacks) to set uses_fetch.

Also enable perry-runtime/external-fetch-symbols when uses_fetch so the runtime
fetch stubs (gated cfg(not(external-fetch-symbols))) are compiled out and only
perry-stdlib's real symbols remain (was wired nowhere -> dup-symbol, stub won).
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

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: ab701108-71ca-4c25-a5e4-f9979f537dba

📥 Commits

Reviewing files that changed from the base of the PR and between 15c91e1 and 22b9edb.

📒 Files selected for processing (2)
  • crates/perry/src/commands/compile/collect_modules/feature_detect.rs
  • crates/perry/src/commands/compile/optimized_libs/freshness.rs

📝 Walkthrough

Walkthrough

Fallback fetch detection now scans lowered HIR when uses_fetch was not set during lowering, and the fresh-build cross-feature list now includes perry-runtime/external-fetch-symbols when fetch usage is detected.

Changes

Fetch feature flow

Layer / File(s) Summary
Fallback fetch detection
crates/perry/src/commands/compile/collect_modules/feature_detect.rs
detect_optional_feature_usage serializes the module HIR and scans for fetch-related constructors and Fetch* variants when ctx.uses_fetch is still false, then sets ctx.needs_stdlib and ctx.uses_fetch on a match.
Fresh-build fetch gating
crates/perry/src/commands/compile/optimized_libs/freshness.rs
auto_optimized_cross_features appends perry-runtime/external-fetch-symbols to cross_features when ctx.uses_fetch is true.

Sequence Diagram(s)

sequenceDiagram
  participant detect_optional_feature_usage
  participant ctx
  participant auto_optimized_cross_features
  participant cross_features

  detect_optional_feature_usage->>ctx: set uses_fetch and needs_stdlib after HIR scan
  ctx->>auto_optimized_cross_features: uses_fetch = true
  auto_optimized_cross_features->>cross_features: append perry-runtime/external-fetch-symbols
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • PerryTS/perry#5172: Extends the same optional-feature detection path in collect_modules/feature_detect.rs.
  • PerryTS/perry#5189: Also adjusts fetch-related feature selection so web-fetch stays available when fetch usage is detected.
  • PerryTS/perry#5606: Touches fetch-runtime registrations that depend on the same uses_fetch/feature-gating path.

Poem

A bunny sniffed the HIR so fine,
and found fetch clues in every line.
Now symbols hop where they belong,
and runtime stays swift, bright, and strong.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the PR’s main fix: improved fetch detection and excluding runtime fetch stubs when web-fetch is linked.
Description check ✅ Passed The description covers the problem, fix, and verification, but it does not follow the template headings and omits an explicit related issue and test-plan checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests

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

@proggeramlug proggeramlug merged commit dabecec into PerryTS:main Jun 25, 2026
15 checks passed
proggeramlug added a commit that referenced this pull request Jun 25, 2026
…H_DIAG (follow-up #5679) (#5684)

#5679's token-scan fallback covered hir_module.init + functions only; a minified
bundle can construct new Headers()/new Request() inside a class-method body
(hir_module.classes), so uses_fetch resolved false and the no-op fetch stub linked.
Scan classes too. Adds opt-in PERRY_FETCH_DIAG logging hir.uses_fetch -> ctx.uses_fetch.

Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
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