fix(compile): robust fetch detection for minified bundles + drop runtime fetch stubs when web-fetch is linked#5679
Merged
proggeramlug merged 1 commit intoJun 25, 2026
Conversation
…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).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughFallback fetch detection now scans lowered HIR when ChangesFetch feature flow
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A minified bundle that uses
fetch/Headers/Requestsegfaults at startup: the real fetch implementation isn't linked, only the no-op runtime stub, which returns garbage the caller dereferences →EXC_BAD_ACCESSinjs_object_get_class_id.Two root causes:
uses_fetchdetection is shape-fragile. perry-stdlib's real fetch (web-fetch) is linked only whenuses_fetchis set, which happens at ~30 shape-specific sites scattered through HIR lowering (expr_new.rs,destructuring/*, …). A minified bundle'snew Headers()/new Request()/fetch(...)can reach codegen asExpr::New { class_name: "Headers" }/ aFetch*HIR variant — codegen correctly dispatches those tojs_headers_new/js_request_new/js_fetch_with_options— without having hit anyuses_fetchset-site. Souses_fetchstays false,web-fetchis stripped, and only the stub remains.The runtime fetch stubs are never compiled out. The
js_fetch_*/js_headers_new/js_request_newstubs inperry-runtimeare gated#[cfg(not(feature = "external-fetch-symbols"))], butexternal-fetch-symbolswas enabled nowhere in the build. So wheneverweb-fetchis 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
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) → setuses_fetch. This mirrors the existing EventEmitter / URL token-scan fallbacks in the same file. Over-matching only over-linksweb-fetch(a size cost); the rule is zero false negatives.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 oflibperry_runtime.a, leaving only perry-stdlib's real symbols — no dup-symbol gamble.Both are required together: fix 1 links
web-fetchbut 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_fetchis detected, the stub warnings are gone, and the realweb-fetchsymbols are linked.Summary by CodeRabbit