Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion crates/perry-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ crate-type = ["rlib", "staticlib"]
# actually needs (see optimized_libs.rs), so the heavy subsystems below
# (regex engine, Temporal, URL/IDNA, normalize, segmenter) are *opt-in per
# app* and absent from binaries that never use them.
default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "diagnostics"]
default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "diagnostics", "mod-dgram"]
# Per-module Node-API gate (binary-size): compiles `node:dgram`'s UDP-socket
# implementation (`crate::dgram` + `crate::dgram_reactor`, ~2.2k LOC, incl. the
# `js_dgram_*` externs codegen emits direct calls to) + its dispatch arm only
# when the program imports `dgram`. The compiler enables it on `module: "dgram"`
# usage (see the auto-optimize import-detection path), so a program that never
# imports `dgram` links none of its code. Pure cfg gate, no extra deps. (This is
# the first instance of a general per-module gating pattern; more modules can
# follow the same shape.)
mod-dgram = []
# Cold-path diagnostic JSON serializers (~67 KB of code + the `serde_json`
# pulled only by them, which dead-strips when unreferenced): GC cycle telemetry
# (`PERRY_GC_DIAG`), typed-feedback trace dump (`PERRY_TYPED_FEEDBACK`), the v8
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/gc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ pub fn gc_init() {
// #4911: a bound node:dgram socket is reachable only from the dgram
// reactor's registry while its recv thread runs; scan + rewrite it so a GC
// between ticks doesn't reclaim the object whose `message` handlers fire.
#[cfg(feature = "mod-dgram")]
gc_register_mutable_root_scanner(crate::dgram_reactor::scan_roots_mut);
gc_register_mutable_root_scanner(json_parse_mutable_root_scanner);
gc_register_mutable_root_scanner(intern_table_mutable_root_scanner);
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ pub mod collection_iter;
pub mod collection_iter_object;
pub mod color_parse;
pub mod date;
#[cfg(feature = "mod-dgram")]
pub mod dgram;
#[cfg(feature = "mod-dgram")]
pub mod dgram_reactor;
pub mod disposable;
pub mod dns;
Expand Down Expand Up @@ -393,6 +395,7 @@ mod stdlib_pump {
// #4911: deliver queued UDP datagrams as `'message'` events. Lives in
// perry-runtime so node:dgram works without perry-stdlib linked.
// Zero-cost (one relaxed load) when no sockets are bound.
#[cfg(feature = "mod-dgram")]
crate::dgram_reactor::pump();
crate::process::js_process_ipc_drain();
let f = STDLIB_PUMP_FN.load(Ordering::Acquire);
Expand Down Expand Up @@ -431,6 +434,7 @@ mod stdlib_pump {
return 1;
}
// #4911: a bound + `ref`'d node:dgram socket keeps the loop alive.
#[cfg(feature = "mod-dgram")]
if crate::dgram_reactor::has_active() {
return 1;
}
Expand Down
6 changes: 6 additions & 0 deletions crates/perry-runtime/src/object/native_module_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,12 @@ pub(crate) unsafe fn dispatch_native_module_method(
("punycode.ucs2", "encode") => crate::punycode::js_punycode_ucs2_encode(arg(0)),

// ── dgram namespace (`node:dgram` / `dgram`) ──
// Gated behind `mod-dgram`: `crate::dgram` is only compiled when the
// program imports `dgram` (the compiler enables the feature on
// `module: "dgram"` usage), so this arm — and the `js_dgram_*` externs
// it calls — are absent otherwise. Unreachable when off (a dgram
// namespace can't exist without the import that enables the feature).
#[cfg(feature = "mod-dgram")]
("dgram", "createSocket") | ("dgram", "Socket") => {
crate::dgram::js_dgram_create_socket(pack_args())
}
Expand Down
6 changes: 6 additions & 0 deletions crates/perry/src/commands/compile/collect_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,12 @@ fn collect_module_finish(
{
ctx.uses_diagnostics = true;
}
// `node:dgram` (UDP) → gates `perry-runtime/mod-dgram` (~43 KB; dgram
// lowers to `NativeMethodCall { module: "dgram" }`, runtime-only so not
// in `native_module_imports`).
if hir_debug.contains("module: \"dgram\"") {
ctx.uses_dgram = true;
}
}

// Detect readline usage via process.stdin raw/lifecycle methods. These
Expand Down
10 changes: 9 additions & 1 deletion crates/perry/src/commands/compile/optimized_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ pub(super) fn build_optimized_libs(
// Cheap djb2 — no need for the SipHash overhead.
let target_str = target.unwrap_or("host");
let key_input = format!(
"{}|{}|{}|wasm={}|regex={}|temporal={}|url={}|norm={}|seg={}|diag={}|v={}",
"{}|{}|{}|wasm={}|regex={}|temporal={}|url={}|norm={}|seg={}|diag={}|dgram={}|v={}",
feature_arg,
panic_abort_safe,
target_str,
Expand All @@ -681,6 +681,7 @@ pub(super) fn build_optimized_libs(
ctx.uses_string_normalize,
ctx.uses_intl_segmenter,
ctx.uses_diagnostics,
ctx.uses_dgram,
env!("CARGO_PKG_VERSION"),
);
let mut hash: u64 = 5381;
Expand Down Expand Up @@ -804,6 +805,13 @@ pub(super) fn build_optimized_libs(
if ctx.uses_diagnostics {
cross_features.push("perry-runtime/diagnostics".to_string());
}
// Per-Node-module gating: `node:dgram`'s implementation + dispatch arm are
// behind `mod-dgram`, enabled only when the program uses dgram (detected via
// `module: "dgram"` in the HIR). codegen only emits the `js_dgram_*` externs
// for dgram programs, so detection is complete (no dangling symbols).
if ctx.uses_dgram {
cross_features.push("perry-runtime/mod-dgram".to_string());
}
if !cross_features.is_empty() {
cargo_cmd.arg("--features").arg(cross_features.join(","));
}
Expand Down
9 changes: 9 additions & 0 deletions crates/perry/src/commands/compile/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,14 @@ pub struct CompilationContext {
/// the same feature and degrade gracefully when it's off, so they're absent
/// from size-optimized binaries unless one of these APIs is also used.
pub uses_diagnostics: bool,
/// Whether any TS module imports `node:dgram` (UDP sockets). Gates
/// `perry-runtime/mod-dgram` (`crate::dgram` + `crate::dgram_reactor`,
/// ~43 KB, incl. the `js_dgram_*` externs codegen emits direct calls to).
/// Detected from `module: "dgram"` in the HIR (a `dgram` namespace can only
/// arise from importing it), so a program that never imports `dgram` links
/// none of it. NB: not via `native_module_imports`, which only tracks
/// `requires_stdlib` modules — dgram is runtime-only.
pub uses_dgram: bool,
/// Whether `perry/thread` is imported. When true, the runtime must
/// keep `panic = "unwind"` so that worker-thread panics translate to
/// promise rejections via `catch_unwind` in `perry-runtime/src/thread.rs`
Expand Down Expand Up @@ -827,6 +835,7 @@ impl CompilationContext {
uses_string_normalize: false,
uses_intl_segmenter: false,
uses_diagnostics: false,
uses_dgram: false,
needs_thread: false,
cross_module_class_field_types: HashMap::new(),
min_windows_version: "10".to_string(),
Expand Down