fix(fetch): call linked stdlib fetch constructors directly under external-fetch-symbols#5687
Conversation
…rnal-fetch-symbols The runtime's fetch-constructor wrappers (`call_global_headers_new`, `call_global_request_new`, and the Blob/File/Response siblings in `global_fetch.rs`) route through the `GLOBAL_FETCH_*` AtomicPtr registry that perry-stdlib populates from `js_stdlib_init_dispatch`. When a wrapper observes its registry pointer null it warns (`js_headers_new is a no-op stub ...`) and returns `undefined`. In an `external-fetch-symbols` build, perry-stdlib's `web-fetch` implementations are statically linked — the same invariant `call_fetch_with_options` already relies on to call `js_fetch_with_options` directly, and the same one the codegen fast path uses when it emits a direct `js_headers_new` for a literal `new Headers()`. Only the registry-backed construction path (a reflective `new Headers()` / `new Request()` resolved through the class registry, e.g. an aliased `const K = Headers; new K()`) still went through the AtomicPtr. In a large auto-optimized binary with bundled runtime copies that wrapper could read the pointer null even though init had already stored it, yielding a no-op fetch (a hang on the first request) while the equivalent direct codegen path worked. Extend the existing `external-fetch-symbols` direct-call pattern to the nine fetch-constructor wrappers so the registry path matches the codegen path: under the feature they call the linked `js_*` externs directly; without it they keep today's registry-load + stub-warn behavior for runtime-only builds. No behavior change for the default (stub) build.
|
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 (1)
📝 WalkthroughWalkthroughThe runtime now splits fetch-related global constructors between registry-backed lookups and direct ChangesExternal fetch symbol constructors
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 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 |
|
Verified end-to-end against a large minified CLI bundle whose startup constructs
Also confirmed |
Problem
The runtime's fetch-constructor wrappers in
crates/perry-runtime/src/object/global_fetch.rs—call_global_headers_new,call_global_request_new, and theBlob/File/Responsesiblings — call into perry-stdlib indirectly through theGLOBAL_FETCH_*AtomicPtrregistry, whichjs_stdlib_init_dispatch(js_register_global_fetch_constructors) populates at program entry. When a wrapper loads its registry pointer and sees null, it warnsand returns
undefined, so the construction no-ops.These wrappers are only reached by the registry-backed construction path — a reflective/aliased
new Headers()/new Request()resolved through the class registry (object/class_registry/construct.rs), e.g.const K = Headers; new K(). A literalnew Headers()takes the codegen fast path, which emits a directjs_headers_newcall and is unaffected.In a large, auto-optimized binary that bundles its own runtime copies, the registry pointer could be observed null even though init had already stored it — so the reflective path no-opped (and a subsequent
fetch()hung) while the equivalent direct codegen path worked on the very same symbol.Fix
call_fetch_with_optionsalready handles exactly this: under theexternal-fetch-symbolsfeature it calls the linkedjs_fetch_with_optionsextern directly instead of going through the registry, because in that build perry-stdlib'sweb-fetchimplementations are statically linked. This PR extends that established pattern to the nine fetch-constructor wrappers:#[cfg(feature = "external-fetch-symbols")]→ call the linkedjs_blob_new/js_file_new/js_headers_new/js_headers_init_from_value/js_request_new/js_response_new/js_response_static_json/js_response_static_redirect/js_response_static_errorexterns directly.#[cfg(not(feature = "external-fetch-symbols"))]→ unchanged: registry load + stub-warn, for runtime-only builds where these externs are not linked and a runtime stub provides the symbol.This makes the registry construction path consistent with both the codegen fast path and
call_fetch_with_options, and sidesteps the registry-visibility hazard entirely.Why it's link-safe
external-fetch-symbolsis enabled exactly when the program uses fetch, which links perry-stdlib withweb-fetch.web-fetchprovides all nine symbols (src/fetch/plussrc/fetch_blob.rs), so they are guaranteed resolvable whenever the feature is on — the same invariantcall_fetch_with_optionsalready depends on.Testing
cargo check -p perry-runtime(default / stub build) — clean.cargo check -p perry-runtime --features external-fetch-symbols(direct-extern build) — clean.not(external-fetch-symbols)variants are byte-for-byte the previous code.Summary by CodeRabbit
New Features
fetch-related web objects in builds that use statically linked web APIs.Bug Fixes
fetchconstructors from failing silently by returning safe empty values and showing a clearer warning.