Skip to content

feat(compile): auto-provision native ext bindings + wasm host - #7118

Open
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:fix/auto-provision-ext-bindings
Open

feat(compile): auto-provision native ext bindings + wasm host#7118
jdalton wants to merge 1 commit into
PerryTS:mainfrom
jdalton:fix/auto-provision-ext-bindings

Conversation

@jdalton

@jdalton jdalton commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Problem

A bare perry compile failed to fully provision the native artifacts a program actually needs. Two measured symptoms (Socket Firewall, perry v0.5.1265):

  1. Link gap. firewall lists iovalkey in perry.compilePackages, so it AOT-compiles — but codegen still lowers its client usage to the native js_ioredis_new FFI. That symbol was in neither the exact ext_registry table nor any import set, so the well-known flip never fired and perry-ext-ioredis was never linked: Undefined symbols: _js_ioredis_new. This bites any well-known-backed package that also lives in compilePackages (or whose ext FFI is codegen-emitted without the module being in the well-known iteration set).
  2. Missing prebuild. The auto-optimize path never built the CPU-only ext staticlibs (e.g. perry-ext-node-forge) nor perry-wasm-host, so a human had to cargo build --release -p … them first, and a WebAssembly.* program died with libperry_wasm_host.a not found.

Root cause

Native-artifact provisioning was driven off the well-known iteration set (ctx.native_module_imports + PERRY_FORCE_WELL_KNOWN), not off what the program actually references. compilePackages members are AOT-compiled, so they never enter native_module_imports — yet their codegen still emits the ext binding's js_<binding>_* FFI. The exact-symbol ext_registry table only routed symbols someone remembered to add (http/net/ws/events/mysql2/streams/crypto); whole js_<binding>_* families (ioredis, undici, node-forge) fell through. Separately, only the shared-tokio ext crates were rebuilt in the auto-optimize cargo invocation; CPU-only ext crates and perry-wasm-host were built by nothing.

Fix

Drive linking + building off the emitted-FFI set (the ground truth) plus the existing well-known routing.

  • Link (crates/perry-codegen/src/ext_registry.rs) — add a prefix net (EXT_PREFIX_REGISTRY) that routes any emitted js_<binding>_* symbol to its well-known binding key (js_ioredis_*ioredis, js_undici_*undici, js_node_forge_*node-forge), checked after the exact table + crypto net. The driver's existing WellKnown(key)native_module_imports → routing machinery does the rest — no per-symbol maintenance, no import required. The per-module capture records the matched prefix as a stable object-cache replay marker (same shape as the js_crypto_ marker).
  • Build (CPU-only ext, optimized_libs/driver.rs) — when a routed CPU-only binding's staticlib is missing, auto-build it from workspace source via the existing build_missing_prebuilt_ext_lib leaf-build (the shared-tokio ones are already rebuilt in the auto-optimize invocation). No-op when the archive is already present.
  • Build (wasm host, library_search.rs + run_pipeline.rs) — add build_wasm_host_library, wired to the existing ctx.needs_wasm_runtime/--enable-wasm-runtime signal: when a WebAssembly.* program's libperry_wasm_host.a is missing, build perry-wasm-host from workspace source before the symbol-stub scan + link so both resolve it. No-op when it exists or the program doesn't use wasm.

Acceptance (bare perry compile, no PERRY_FORCE_WELL_KNOWN, no manual prebuild)

All three sfw entrypoints link and run; _js_ioredis_new is gone; ioredis/http/net auto-build:

entrypoint result size
src/sfw-enterprise/cli.ts links + runs (--version ok) 57.5 MB
src/sfw-free/cli.ts links + runs (--version ok) 40.3 MB
src/sfw-registry/cli.ts links (separate runtime bug tracked elsewhere) 34.8 MB

Wasm host auto-provision verified end-to-end on a minimal new WebAssembly.Module(...) program: with libperry_wasm_host.a absent, the compile auto-builds it (Using wasmi WebAssembly host runtime), links, and runs — the not found error is gone.

Tests

  • perry-codegen: prefix routing for the ioredis/undici/node-forge families, no-over-match, and prefix-marker cache-replay.
  • perry: ext binding keys resolve to their perry-ext-* crates, the tokio vs CPU-only build-routing split, and a fake-cargo perry-wasm-host auto-build.
  • cargo test -p perry --bin perry green (841 passed); cargo build --release -p perry succeeds; cargo fmt clean; changelog fragment added.

Summary by CodeRabbit

  • New Features
    • perry compile automatically detects, builds, and links required native extension bindings.
    • ioredis, undici, and node-forge bindings work without manual prebuilds or special placement.
    • Missing WebAssembly host support is built automatically when needed.
    • Cross-compilation setup is improved for Android and HarmonyOS targets.
  • Bug Fixes
    • Improved binding resolution and routing for compiled applications.
    • Existing builds and non-WebAssembly workflows remain unaffected.
  • Documentation
    • Added API documentation for the node-forge module and tls.convertALPNProtocols.

@coderabbitai

coderabbitai Bot commented Jul 31, 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: 0adfdbee-3ca4-44a5-839b-c5520fa38b1c

📥 Commits

Reviewing files that changed from the base of the PR and between 05d3a38 and 410ffd3.

📒 Files selected for processing (15)
  • changelog.d/0000-auto-provision-ext-bindings.md
  • crates/perry-codegen/src/ext_registry.rs
  • crates/perry-runtime/src/os_network.rs
  • crates/perry/src/commands/compile.rs
  • crates/perry/src/commands/compile/library_search.rs
  • crates/perry/src/commands/compile/optimized_libs/driver.rs
  • crates/perry/src/commands/compile/optimized_libs/no_auto.rs
  • crates/perry/src/commands/compile/optimized_libs/tests.rs
  • crates/perry/src/commands/compile/resolve.rs
  • crates/perry/src/commands/compile/run_pipeline.rs
  • crates/perry/src/commands/compile/well_known.rs
  • docs/api/perry.d.ts
  • docs/src/api/reference.md
  • test-parity/gap_snapshot.json
  • workspace-architecture.json
💤 Files with no reviewable changes (1)
  • test-parity/gap_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (14)
  • crates/perry/src/commands/compile/optimized_libs/tests.rs
  • crates/perry/src/commands/compile.rs
  • crates/perry-runtime/src/os_network.rs
  • crates/perry/src/commands/compile/optimized_libs/driver.rs
  • changelog.d/0000-auto-provision-ext-bindings.md
  • docs/api/perry.d.ts
  • workspace-architecture.json
  • crates/perry/src/commands/compile/resolve.rs
  • crates/perry/src/commands/compile/library_search.rs
  • crates/perry/src/commands/compile/optimized_libs/no_auto.rs
  • crates/perry-codegen/src/ext_registry.rs
  • crates/perry/src/commands/compile/well_known.rs
  • crates/perry/src/commands/compile/run_pipeline.rs
  • docs/src/api/reference.md

📝 Walkthrough

Walkthrough

perry compile now routes supported extension FFI symbols to well-known bindings and automatically builds missing CPU-only extension libraries and perry-wasm-host archives when required.

Changes

Automatic binding and Wasm provisioning

Layer / File(s) Summary
Extension FFI prefix routing
crates/perry-codegen/src/ext_registry.rs, crates/perry/src/commands/compile/optimized_libs/tests.rs
Supported ioredis, undici, and node-forge symbols map to well-known binding providers. Prefixes persist through module-capture replay. Tests cover routing and build classification.
CPU-only binding library provisioning
crates/perry/src/commands/compile/optimized_libs/driver.rs, crates/perry/src/commands/compile/optimized_libs/no_auto.rs
Missing CPU-only libraries trigger workspace builds before the perry-stdlib fallback. Android and HarmonyOS builds receive target-specific environments.
Wasm host archive provisioning
crates/perry/src/commands/compile/library_search.rs, crates/perry/src/commands/compile/run_pipeline.rs, crates/perry/src/commands/compile.rs, crates/perry/src/commands/compile/optimized_libs/tests.rs, changelog.d/0000-auto-provision-ext-bindings.md
The compile pipeline builds missing perry-wasm-host archives with Cargo and resolves them from configured or standard target directories.
Compile metadata and workspace support
crates/perry-runtime/src/os_network.rs, crates/perry/src/commands/compile/resolve.rs, crates/perry/src/commands/compile/well_known.rs, workspace-architecture.json, test-parity/gap_snapshot.json
Platform-specific compilation guards unused network code. Tooling metadata receives documentation and dead-code allowances. Workspace metadata records added members and binding crates. The zlib parity gap entry is removed.
Node-forge and TLS API manifests
docs/api/perry.d.ts, docs/src/api/reference.md
The API declarations and reference add the node-forge module and tls.convertALPNProtocols. Manifest counts are updated.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FFI as record_ffi_call
  participant Registry as EXT_PREFIX_REGISTRY
  participant Compile as perry compile
  participant Resolver as optimized library resolver
  participant Builder as build_wasm_host_library
  participant Cargo
  FFI->>Registry: Match extension symbol prefix
  Registry-->>FFI: Return well-known binding provider
  Compile->>Resolver: Resolve required static library
  Resolver->>Builder: Request missing Wasm host archive
  Builder->>Cargo: Build release archive for resolved target
  Cargo-->>Builder: Produce static archive
  Builder-->>Resolver: Return resolved archive path
Loading

Possibly related PRs

  • PerryTS/perry#6717: Both PRs modify compile-time library discovery in compile/library_search.rs.
  • PerryTS/perry#7033: This PR directly enables automatic discovery and compilation of the perry-ext-node-forge binding.
  • PerryTS/perry#7094: This PR adds the tls.convertALPNProtocols API declaration documented here.

Suggested labels: tooling

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes automatic provisioning of native extension bindings and the WebAssembly host.
Description check ✅ Passed The description clearly explains the problem, root cause, implementation, acceptance results, and tests, providing sufficient detail for review.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 1

🤖 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 `@crates/perry/src/commands/compile/optimized_libs/driver.rs`:
- Around line 207-214: Update the missing-library auto-build call in the
optimized compilation flow to apply the target-specific Android or HarmonyOS
cross-compilation environment before invoking build_missing_prebuilt_ext_lib.
Reuse the same cross-env setup or target-specific invocation path used by
build_optimized_libs, while preserving existing behavior for other targets.
🪄 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: 898d2929-50d0-47ce-9118-6197d7efe94a

📥 Commits

Reviewing files that changed from the base of the PR and between 76582ad and fbd0bb4.

📒 Files selected for processing (7)
  • changelog.d/0000-auto-provision-ext-bindings.md
  • crates/perry-codegen/src/ext_registry.rs
  • crates/perry/src/commands/compile.rs
  • crates/perry/src/commands/compile/library_search.rs
  • crates/perry/src/commands/compile/optimized_libs/driver.rs
  • crates/perry/src/commands/compile/optimized_libs/tests.rs
  • crates/perry/src/commands/compile/run_pipeline.rs

Comment thread crates/perry/src/commands/compile/optimized_libs/driver.rs

@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: 1

🤖 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 `@crates/perry/src/commands/compile/optimized_libs/no_auto.rs`:
- Around line 179-212: Update the HarmonyOS branch in the auto-build flow to
require both a usable clang compiler and the corresponding sysroot before
applying harmonyos_cross_env. Validate the candidate returned by
find_harmonyos_sdk, and when either requirement is missing, return None so the
existing fallback message/path is used instead of configuring an incomplete
cross environment.
🪄 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: b3ba8319-22c9-4cf2-b3ab-916013f9b16e

📥 Commits

Reviewing files that changed from the base of the PR and between fbd0bb4 and d93aa69.

📒 Files selected for processing (5)
  • crates/perry-runtime/src/os_network.rs
  • crates/perry/src/commands/compile/optimized_libs/no_auto.rs
  • crates/perry/src/commands/compile/resolve.rs
  • crates/perry/src/commands/compile/well_known.rs
  • workspace-architecture.json

Comment thread crates/perry/src/commands/compile/optimized_libs/no_auto.rs
Drive native-artifact provisioning off what a program actually references
instead of the well-known iteration set alone.

Link: codegen's ext_registry gains a prefix net so any emitted
js_<binding>_* FFI (js_ioredis_*, js_undici_*, js_node_forge_*) flips its
well-known wrapper onto the link line off provenance alone. An
AOT-compiled iovalkey/undici/node-forge (a compilePackages member, never
in any import set) now links its perry-ext-* staticlib instead of failing
with Undefined symbols: _js_ioredis_new.

Build: routed CPU-only ext staticlibs are auto-built from workspace
source when missing (reusing build_missing_prebuilt_ext_lib), and
perry-wasm-host is auto-built when the program uses WebAssembly.* so
libperry_wasm_host.a-not-found can't happen on a normal compile.

Tests: ext_registry prefix routing + replay markers, ext binding key
resolution + build-routing split, and a fake-cargo wasm-host auto-build.

Review + CI follow-ups folded in:

- Android/HarmonyOS cross-env: build_missing_prebuilt_ext_lib now applies
  the OHOS SDK / Android NDK compile env (mirroring build_optimized_libs)
  before the auto-build, so a C-dependent CPU-only wrapper's cross build
  no longer fails in build.rs before it can fall back (CodeRabbit).
- workspace-architecture baseline refreshed to record the audit facts the
  ext crates imply: perry-ext-node-forge classification, the intentional
  perry-ext-node-forge/perry-ext-undici -> perry-runtime edges (PerryTS#6303/PerryTS#6314),
  perry-container-compose default membership, and member/closure counts.
- Cleared pre-existing -D warnings dead-code that had been red on main
  since PerryTS#7080/PerryTS#7028/PerryTS#7031: gate perry-runtime's format_mac_address to its
  real callers (Windows/macOS/BSD/test; Linux reads the MAC preformatted
  from /sys), and allow(dead_code) on resolve_exports and the well_known
  provenance fields (read by the review gate + tests, not the link path).
- Regenerated docs/src/api/reference.md + docs/api/perry.d.ts for the
  node-forge manifest entries added in PerryTS#7033.
- Dropped the now-passing test_gap_zlib_3285_params from the gap snapshot.
@jdalton
jdalton force-pushed the fix/auto-provision-ext-bindings branch from 05d3a38 to 410ffd3 Compare July 31, 2026 23:24
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