Skip to content

fix(compile): reject Node native addons in compilePackages#4986

Merged
proggeramlug merged 11 commits into
PerryTS:mainfrom
JagritGumber:fix/reject-node-native-addons
Jun 12, 2026
Merged

fix(compile): reject Node native addons in compilePackages#4986
proggeramlug merged 11 commits into
PerryTS:mainfrom
JagritGumber:fix/reject-node-native-addons

Conversation

@JagritGumber

@JagritGumber JagritGumber commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Reject packages that ship Node native addons when they are opted into perry.compilePackages, matching the existing porting-doc guidance that these packages are not portable through the JS/TS compile path.

Fixes #4961.

Changes

  • Detect native-addon markers in compile-package roots: binding.gyp, prebuilds/, package gypfile, bounded-depth *.node files, and common native-addon loader dependencies (node-gyp-build / bindings).
  • Fail during module collection with an actionable diagnostic that points to perry.nativeLibrary / perry-ffi instead of late runtime/link failures.
  • Preserve Perry-native packages by skipping the Node-addon guard for packages that declare perry.nativeLibrary.
  • Expand compile-package tests for each marker type, loader dependencies, pure JS packages, and the Perry-native exception.
  • Update docs to clarify that compilePackages is for JS/TS and packages like node-pty, sharp, better-sqlite3, and sqlite3 need a native-library replacement path.

Related issue

Fixes #4961

Test plan

  • cargo fmt --all
  • cargo test -p perry compile_package_ -- --nocapture
  • cargo test -p perry perry_native_library_package_is_not_rejected_by_node_addon_guard -- --nocapture
  • git diff --check

The focused test runs passed on Windows. Existing workspace warnings were emitted but not introduced by this PR.

Screenshots / output

Representative new diagnostic shape:

package `node-pty` is in `perry.compilePackages` but uses a Node native addon (binding.gyp) at <path>.
Perry cannot load Node `.node` / N-API addons inside a native Perry binary. Remove `node-pty` from `perry.compilePackages`, choose a pure JS/TS package, or replace the native boundary with a Perry native binding (`perry.nativeLibrary` / perry-ffi).

Checklist

  • I have NOT bumped the workspace version or edited CLAUDE.md / CHANGELOG.md
  • My commits follow the loose feat: / fix: / docs: / chore: prefix convention used in the log
  • I've read CONTRIBUTING.md and agree to the Code of Conduct

@JagritGumber JagritGumber force-pushed the fix/reject-node-native-addons branch from 39228c9 to 48ae00e Compare June 11, 2026 09:50
@JagritGumber JagritGumber force-pushed the fix/reject-node-native-addons branch from 48ae00e to 4233b26 Compare June 11, 2026 10:36

@proggeramlug proggeramlug left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Audited this against #4961 — the approach is sound: the guard sits at the right hook point (module collection, before the CJS wrap and SWC parse, so both ESM and CJS-shaped packages fail before codegen/link), the perry.nativeLibrary exemption reuses the existing helper consistently, and the test matrix covers the issue's required cases. Four findings inline: two in the detection logic (gypfile substring match, devDependencies signal), one perf (per-module rescan without memoization), one robustness note on package-root resolution. Nits, take or leave: find_node_addon_file flags a directory named *.node (extension check precedes the is_dir branch — an is_file() guard would fix it), and marker_path_for_display is a one-line wrapper called three times that could be inlined.

Comment thread crates/perry/src/commands/compile/collect_modules.rs Outdated
Comment thread crates/perry/src/commands/compile/collect_modules.rs Outdated
Comment thread crates/perry/src/commands/compile/collect_modules.rs Outdated
Comment thread crates/perry/src/commands/compile/collect_modules.rs Outdated
@JagritGumber

JagritGumber commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

CI update: the remaining failure on this PR is cargo-test, specifically crates/perry/tests/gc_write_barrier_stress.rs::tenured_mutation_stress.

I reproduced the same test failure locally after merging current upstream/main, so simply pulling latest main does not clear it. The focused native-addon guard tests and compile package tests pass locally, and the failing test is in the runtime GC stress suite, not the compile package/native-addon path touched by this PR.

I also checked recent PR CI. Several current PRs have cargo-test failures, including GC stress failures on structured_clone_gc_churn_stress, while others fail manifest_consistency. I did not find another PR with the exact same tenured_mutation_stress failure. So this looks unrelated to the native-addon guard, but I am not claiming a one-to-one match with every other PR failure.

@JagritGumber

Copy link
Copy Markdown
Contributor Author

Follow-up GC bisection detail: I can make the failing stress test pass/fail with the conservative stack scan knob.

Local setup was the current PR branch after merging current upstream/main, using the already-built runtime artifacts via PERRY_RUNTIME_DIR.

Commands/results:

$env:PERRY_RUNTIME_DIR = D:\tmp\perry-native-addon-pr\target\perry-auto-8981e03852ab7578\release
& D:\tmp\perry-gc-diagnostic-target\debug\deps\gc_write_barrier_stress-90aa6112441215e7.exe tenured_mutation_stress --nocapture

Default/manual gc() behavior fails with the old-young verifier:

old-young-edge-verifier failed: checked_old_objects=2777 checked_remembered_pages=146 checked_old_to_young_edges=35488 missing_edges=4882

With conservative stack scanning disabled, the same test passes:

$env:PERRY_CONSERVATIVE_STACK_SCAN = 0
& D:\tmp\perry-gc-diagnostic-target\debug\deps\gc_write_barrier_stress-90aa6112441215e7.exe tenured_mutation_stress --nocapture
# ok

With conservative stack scanning explicitly enabled, it fails again with the same verifier class:

$env:PERRY_CONSERVATIVE_STACK_SCAN = 1
& D:\tmp\perry-gc-diagnostic-target\debug\deps\gc_write_barrier_stress-90aa6112441215e7.exe tenured_mutation_stress --nocapture
# old-young-edge-verifier missing_edges=4882

The sibling structured_clone_gc_churn_stress passes locally with both PERRY_CONSERVATIVE_STACK_SCAN=0 and =1, so this narrower failure appears to be tenured_mutation_stress + manual/full conservative scan + forced evacuation.

My read: this points at the recent manual-gc() conservative stack scan path interacting with remembered-set/old-page evacuation bookkeeping. The failure is not in this PR's compile-package/native-addon path.

…ive-addons

# Conflicts:
#	crates/perry/src/commands/compile/collect_modules/tests.rs
@JagritGumber

Copy link
Copy Markdown
Contributor Author

Latest CI after rebasing onto current upstream/main is still blocked, but the two failing signatures match the upstream run for the PR that became current main (059931619, run 27363005456):

  • compiler-output-regression: native-region-proof fails only h1_buffer_alias_negative with hot_loops_no_runtime_calls seeing js_implicit_this_set in for.body.2.i38.
  • cargo-test: perry-codegen --test manifest_consistency fails manifest_param_counts_match_dispatch_table on zlib::deflateRawSync arity drift: manifest declares 1 param, dispatch table has 2 ([NA_F64, NA_F64]).

Those are the same failures in the upstream Tests run for fix(hir): perry.define of process.env.* folds at lowering... before/while it landed, and this PR's diff does not touch the compiler-output regression harness, native-region proof workloads, zlib dispatch/manifest code, or perry-codegen. So I’m treating the latest red checks as inherited upstream breakage rather than a native-addon guard regression.

@proggeramlug proggeramlug merged commit ae7a0a7 into PerryTS:main Jun 12, 2026
13 checks passed
@JagritGumber JagritGumber deleted the fix/reject-node-native-addons branch June 12, 2026 11:34
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.

Make Node native addon packages an explicit unsupported compilePackages category

2 participants