From 3a1c5efb900705b48fee8206cb635d772bb01f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 13 Jun 2026 15:40:47 +0200 Subject: [PATCH] fix(perry-ffi): unbreak `cargo test -p perry-ffi` (undefined js_register_ffi_handle_exists_probe) #5083 added a `js_register_ffi_handle_exists_probe(...)` call inside `register_handle` (handle.rs) for the runtime's handle-vs-timer disambiguation probe. That symbol is defined in perry-runtime, but perry-ffi's `runtime-link` feature is off by default and CI runs `cargo test -p perry-ffi` per-package in isolation (no --workspace feature unification). The handle-registry tests (bare `#[cfg(test)]`) call `register_handle`, so the test binary retained a reference to an unlinked symbol and failed at `cc` with `undefined reference`. Gating the handle tests on `runtime-link` (the pattern other perry-ffi test modules use) would drop them from CI entirely (the per-package loop never enables the feature). Instead add a test-only no-op stub gated `#[cfg(all(test, not(feature = "runtime-link")))]` so the registry tests keep running and the binary links; the real extern declaration is gated out of that exact config to avoid an E0428 name clash. The stub can never collide with perry-runtime's real definition (it exists only in perry-ffi's own test binary with runtime-link off). Verified: `cargo test -p perry-ffi` links + passes (13); `cargo test -p perry-ffi --features runtime-link` uses the real symbol + passes (35). --- crates/perry-ffi/src/handle.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/perry-ffi/src/handle.rs b/crates/perry-ffi/src/handle.rs index 062f24d3d3..2220cba3ba 100644 --- a/crates/perry-ffi/src/handle.rs +++ b/crates/perry-ffi/src/handle.rs @@ -105,13 +105,39 @@ extern "C" { scanner_id: usize, scanner: PerryFfiNamedMutableRootScanner, ); - /// perry-runtime hook: register a probe the runtime's generic method - /// dispatcher consults to tell a `register_handle` id apart from a Node - /// timer id (both occupy the pointer-tagged small-integer band). Resolved - /// at final link (perry-runtime is always linked into the binary). +} + +// perry-runtime hook: register a probe the runtime's generic method dispatcher +// consults to tell a `register_handle` id apart from a Node timer id (both +// occupy the pointer-tagged small-integer band). Defined in perry-runtime and +// resolved at the final link of any real Perry binary. +// +// The declaration is gated OUT of perry-ffi's own unit-test binary when +// `runtime-link` is off, where a no-op stub stands in instead (see below) — +// otherwise the always-present `extern` item and the stub would clash (E0428). +#[cfg(not(all(test, not(feature = "runtime-link"))))] +extern "C" { fn js_register_ffi_handle_exists_probe(probe: extern "C" fn(handle: i64) -> bool); } +// perry-ffi's own unit-test binary does not link perry-runtime: `runtime-link` +// is off by default and CI runs `cargo test -p perry-ffi` per-package in +// isolation (no `--workspace` feature unification, see `.github/workflows/ +// test.yml`). The handle-registry tests below exercise `register_handle`, +// which calls `js_register_ffi_handle_exists_probe` to wire up the runtime's +// handle-vs-timer disambiguation probe (#5083). Give that test binary a no-op +// definition so it links and the registry tests keep running. Gated on +// `not(feature = "runtime-link")` so it never collides with perry-runtime's +// real definition — which is present whenever runtime-link is on, or at a +// wrapper's final link against libperry_runtime.a, neither of which is a +// perry-ffi `test` build. +#[cfg(all(test, not(feature = "runtime-link")))] +#[no_mangle] +unsafe extern "C" fn js_register_ffi_handle_exists_probe( + _probe: extern "C" fn(handle: i64) -> bool, +) { +} + /// Probe handed to perry-runtime: is `handle` a live entry in this registry? /// Used to disambiguate a `POINTER_TAG | id` value that names both a live /// handle and a live timer (e.g. HTTP/2 server handle 1 vs `setTimeout` id 1),