diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7cc540c849..a979810cba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -236,6 +236,73 @@ jobs: cargo clippy "${cargo_args[@]}" fi + # --------------------------------------------------------------------------- + # rustc warnings gate + # + # `cargo check` with `-D warnings`, so a PR cannot add a rustc warning. This + # is deliberately separate from the clippy job above: clippy's own warn-level + # lints are informational here, while rustc's are not. + # + # Both legs are needed because they compile different code. `perry` depends on + # perry-runtime with `default-features = false`, so the product leg sees a + # runtime with regex-engine, diagnostics and temporal off, where items the + # workspace leg finds live are dead. The workspace leg passes `--all-targets` + # so test and bench targets count too — without it, test-only code drifts. + # + # perry-ui-macos is in the excluded scope (this runs on ubuntu), so its + # warnings are not gated here. + # --------------------------------------------------------------------------- + rustc-warnings: + name: Warnings (${{ matrix.scope }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + scope: [product, host-compatible] + env: + RUSTFLAGS: -D warnings + RUSTC_WRAPPER: sccache + SCCACHE_GHA_ENABLED: "false" + SCCACHE_DIR: ${{ github.workspace }}/.sccache + SCCACHE_CACHE_SIZE: "12G" + CARGO_INCREMENTAL: "0" + steps: + - uses: actions/checkout@v7 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Install sccache + uses: mozilla-actions/sccache-action@v0.0.10 + + - name: Cache sccache objects + uses: actions/cache@v6 + with: + path: ${{ github.workspace }}/.sccache + key: sccache-${{ runner.os }}-perry-${{ github.job }}-${{ github.run_id }} + restore-keys: | + sccache-${{ runner.os }}-perry- + + - uses: Swatinem/rust-cache@v2 + with: + shared-key: "${{ runner.os }}-perry" + save-if: ${{ github.ref == 'refs/heads/main' }} + + - name: Check for rustc warnings + run: | + if [[ "${{ matrix.scope }}" == "product" ]]; then + cargo check -p perry --bins + else + mapfile -t excluded < <(python3 scripts/workspace_architecture.py \ + --print-excluded-scope host-compatible) + cargo_args=(--workspace --all-targets) + for package in "${excluded[@]}"; do + cargo_args+=(--exclude "$package") + done + cargo check "${cargo_args[@]}" + fi + # --------------------------------------------------------------------------- # API docs drift gate (#465) # diff --git a/Cargo.toml b/Cargo.toml index eda1cee76d..bf38957d78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ members = [ # Build each platform package explicitly for its applicable target. default-members = [ "crates/perry", + "crates/perry-container-compose", ] # Aggressive release optimizations for small, fast binaries diff --git a/changelog.d/6837-rust-warnings-to-zero.md b/changelog.d/6837-rust-warnings-to-zero.md new file mode 100644 index 0000000000..332c075af5 --- /dev/null +++ b/changelog.d/6837-rust-warnings-to-zero.md @@ -0,0 +1,35 @@ +### Fixed + +- **Rust warnings: 492 → 0, and a CI gate so they stay there.** A clean + `cargo check --workspace --all-targets` on `main` emitted 492 warnings + (150 `unused_unsafe`, 121 `dead_code`, 96 `unused_imports`, 31 + `unreachable_patterns`, and eleven smaller families). Nothing in CI gated + them: the `lint` job runs only `cargo fmt --check`, and the `clippy` job + exits non-zero only on deny-level lints. Four scopes now report zero — + `--workspace --all-targets`, `-p perry --bins`, `-p perry-runtime + --no-default-features`, and the four-crate runtime/stdlib build — and a new + `rustc-warnings` job runs `cargo check` with `-D warnings` over the product + and host-compatible scopes. + + Three findings the sweep turned up, each fixed rather than silenced: + + - `js_node_http_res_write` and `js_node_http_res_end` were declared in an + `extern "C"` block in `perry-ext-http-server` for symbols that crate + defines itself. A local declaration of a symbol you also define is never + checked against the definition — the defect class that shipped an ABI + mismatch in #6646. Both signatures matched; the declarations are gone and + the remaining ~62 in that block are tracked separately. + - `duplex_allow_half_open_defaults_true_and_honors_false_option` had no + `#[test]` attribute, so it had never run. + - `test_seed_class_parent_closure_root` existed twice, both writing the same + `CLASS_PARENT_CLOSURES` static. + + Eleven warnings appeared only under the reduced feature set `perry` selects + (`default-features = false` on perry-runtime), where regex-engine, + diagnostics and temporal are off. Those items are gated at the item, not + suppressed. The cross-host UI crates (ios/tvos/watchOS/visionos/android/ + windows/gtk4) cannot be checked from a macOS or Linux host and are untouched. + +- Restore `perry-container-compose` to the workspace default build set. This + keeps the container feature's auto-optimized archive available and satisfies + the workspace invariant exercised by the hermetic test tier. diff --git a/crates/perry-codegen-arkts/src/tests/containers.rs b/crates/perry-codegen-arkts/src/tests/containers.rs index 9265f7e1e9..5998d7ed61 100644 --- a/crates/perry-codegen-arkts/src/tests/containers.rs +++ b/crates/perry-codegen-arkts/src/tests/containers.rs @@ -4,7 +4,6 @@ // Section, string + number formatting, and the perry/media drain glue. use super::*; -#[test] // ----- Phase 2 v12: Tabs / Modal / Menu / Grid ----- #[test] fn tabs_emits_tabcontent_per_spec() { diff --git a/crates/perry-codegen-arkts/src/tests/widgets.rs b/crates/perry-codegen-arkts/src/tests/widgets.rs index 7ee39fc1fd..c97a63beff 100644 --- a/crates/perry-codegen-arkts/src/tests/widgets.rs +++ b/crates/perry-codegen-arkts/src/tests/widgets.rs @@ -340,7 +340,7 @@ fn animation_modifier_maps_curve_string_to_curve_enum() { } #[test] -fn shadow_modifier_maps_blur_to_radius_offsets_to_offsetXY() { +fn shadow_modifier_maps_blur_to_radius_offsets_to_offset_xy() { let mut m = empty_module(); m.init.push(app_with_body(nmc( "Text", @@ -518,7 +518,7 @@ fn inline_style_border_combines_color_and_width() { } #[test] -fn text_with_id_string_is_NOT_treated_as_style() { +fn text_with_id_string_is_not_treated_as_style() { // Text("Count: 0", "counter") — second string arg is the reactive // id, NOT a style object. extract_style_object returns None for // String args, so the v3.2 reactive path still wins. diff --git a/crates/perry-codegen-wasm/src/emit/mod.rs b/crates/perry-codegen-wasm/src/emit/mod.rs index c479ce49f8..fec4d4e5b6 100644 --- a/crates/perry-codegen-wasm/src/emit/mod.rs +++ b/crates/perry-codegen-wasm/src/emit/mod.rs @@ -53,7 +53,6 @@ use wasm_encoder::{ use closures::{collect_closures_from_expr, collect_closures_from_stmts}; // `f64_const_bits` is held alive for future use (matches the pre-split // `#[allow(dead_code)]` annotation on its original definition). -use constants::f64_const_bits; use constants::{ f64_const, EnumResolvedValue, STRING_TAG, TAG_FALSE, TAG_NULL, TAG_TRUE, TAG_UNDEFINED, }; diff --git a/crates/perry-codegen-wasm/src/emit/runtime_imports.rs b/crates/perry-codegen-wasm/src/emit/runtime_imports.rs index a2e26a3292..f1cda09ca8 100644 --- a/crates/perry-codegen-wasm/src/emit/runtime_imports.rs +++ b/crates/perry-codegen-wasm/src/emit/runtime_imports.rs @@ -3,8 +3,6 @@ //! //! Pure code-movement from `mod.rs`. -use super::*; - /// Import function indices (must match the order imports are added) /// Most fields are unused directly but their indices define the WASM import order. #[derive(Clone, Copy)] diff --git a/crates/perry-codegen-wasm/src/emit/ui_method_map.rs b/crates/perry-codegen-wasm/src/emit/ui_method_map.rs index 817ccb5399..e2fe58c7de 100644 --- a/crates/perry-codegen-wasm/src/emit/ui_method_map.rs +++ b/crates/perry-codegen-wasm/src/emit/ui_method_map.rs @@ -1,8 +1,6 @@ //! `map_ui_method`: maps perry/ui and perry/system method names to bridge //! function names. Pure code-movement from `mod.rs`. -use super::*; - /// Map perry/ui and perry/system method names to bridge function names. /// Mirrors the mapping in perry-codegen-js's emit_ui_method_call. pub(super) fn map_ui_method(method: &str, class_name: Option<&str>) -> &'static str { diff --git a/crates/perry-codegen/src/codegen/helpers.rs b/crates/perry-codegen/src/codegen/helpers.rs index c8bfdf5822..6b7580d29c 100644 --- a/crates/perry-codegen/src/codegen/helpers.rs +++ b/crates/perry-codegen/src/codegen/helpers.rs @@ -1084,57 +1084,6 @@ pub(super) fn init_static_fields_late( Ok(()) } -/// Returns true if `stmt` contains, at any nesting depth (through -/// if/while/do-while/for/labeled/try/switch bodies), an `Expr( -/// StaticMethodCall)` invoking the (`class_name`, `method_name`) pair — -/// the shape HIR lowering emits at the class-decl position for each -/// `__perry_static_init_*` synthetic method. Used by -/// `init_static_fields_late` to skip per-(class, block) pairs that -/// have already been invoked inline. (#2278) -/// -/// Must recurse: a class declared inside `try { class C { static {...} -/// } }` (test262 static-init-abrupt.js wraps its whole class this way) -/// lowers its inline `StaticMethodCall` into the `Try`'s `body`, not at -/// `hir.init`'s top level. A shallow top-level-only scan missed it, so -/// this late fallback re-invoked the block a second time — outside the -/// user's `try`, so a throwing block's second run surfaced as an -/// uncaught exception instead of staying silently absent. -fn init_calls_static_block(stmt: &perry_hir::Stmt, class_name: &str, method_name: &str) -> bool { - use perry_hir::Stmt; - let any_calls = |stmts: &[Stmt]| { - stmts - .iter() - .any(|s| init_calls_static_block(s, class_name, method_name)) - }; - match stmt { - Stmt::Expr(perry_hir::Expr::StaticMethodCall { - class_name: c, - method_name: m, - .. - }) => c == class_name && m == method_name, - Stmt::If { - then_branch, - else_branch, - .. - } => any_calls(then_branch) || else_branch.as_ref().is_some_and(|b| any_calls(b)), - Stmt::While { body, .. } | Stmt::DoWhile { body, .. } | Stmt::For { body, .. } => { - any_calls(body) - } - Stmt::Labeled { body, .. } => init_calls_static_block(body, class_name, method_name), - Stmt::Try { - body, - catch, - finally, - } => { - any_calls(body) - || catch.as_ref().is_some_and(|c| any_calls(&c.body)) - || finally.as_ref().is_some_and(|f| any_calls(f)) - } - Stmt::Switch { cases, .. } => cases.iter().any(|case| any_calls(&case.body)), - _ => false, - } -} - /// #5989: collect every `(class, method)` invoked via a `StaticMethodCall` /// ANYWHERE in the module — module init, top-level function bodies, and /// (crucially) recursively inside nested closures. `init_calls_static_block` diff --git a/crates/perry-codegen/src/codegen/mod.rs b/crates/perry-codegen/src/codegen/mod.rs index f5cda67240..be26ac34fb 100644 --- a/crates/perry-codegen/src/codegen/mod.rs +++ b/crates/perry-codegen/src/codegen/mod.rs @@ -2221,7 +2221,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result> let Some(sites) = spec_facts.call_sites.get(&f.id) else { continue; }; - let mut reject = + let reject = |reason: typed_abi::TypedCloneRejectionReason, records: &mut Vec| { record_typed_clone_rejection( diff --git a/crates/perry-codegen/src/codegen/spec_abi.rs b/crates/perry-codegen/src/codegen/spec_abi.rs index 5d630372c8..fa3a724eac 100644 --- a/crates/perry-codegen/src/codegen/spec_abi.rs +++ b/crates/perry-codegen/src/codegen/spec_abi.rs @@ -84,13 +84,6 @@ pub(crate) struct SpecFnPlan { pub dispatch: SpecDispatch, } -impl SpecFnPlan { - /// Phase-2 budget: exactly ONE specialized entry per function (the - /// dominant tuple). Kept as an explicit constant so raising it later is a - /// knob, not a rewrite. - pub(crate) const MAX_ENTRIES_PER_FUNCTION: usize = 1; -} - /// LLVM parameter type for a rep slot. pub(crate) fn spec_rep_llvm_ty(rep: SpecParamRep) -> LlvmType { match rep { diff --git a/crates/perry-codegen/src/codegen/typed_abi.rs b/crates/perry-codegen/src/codegen/typed_abi.rs index 88c9172004..85e6af4b4a 100644 --- a/crates/perry-codegen/src/codegen/typed_abi.rs +++ b/crates/perry-codegen/src/codegen/typed_abi.rs @@ -635,10 +635,6 @@ pub(crate) fn typed_i1_closure_rejection_reason_with_types( typed_i1_body_rejection_reason(body, locals) } -pub(crate) fn typed_i32_closure_rejection_reason(expr: &Expr) -> Option { - typed_i32_closure_rejection_reason_with_types(expr, &HashMap::new()) -} - pub(crate) fn typed_i32_closure_rejection_reason_with_types( expr: &Expr, module_local_types: &HashMap, @@ -922,6 +918,10 @@ fn typed_receiver_chain_fields<'a>( if link.extends_expr.is_some() || link.native_extends.is_some() || link.heritage_lexically_shadowed + // A name-only parent is imported. Its authoritative layout is + // source-prefix-resolved elsewhere; this name-keyed map cannot + // disambiguate same-named cross-module classes safely. + || (link.extends_name.is_some() && link.extends.is_none()) || (link.extends.is_some() && link.extends_name.is_none()) { return Err(TypedCloneRejectionReason::ReceiverClassExtends); @@ -1891,7 +1891,7 @@ pub(crate) fn lower_typed_i1_body( #[cfg(test)] mod tests { use super::*; - use perry_hir::Param; + use perry_hir::{Class, Param}; fn param(id: u32, name: &str, ty: Type) -> Param { Param { @@ -1928,6 +1928,46 @@ mod tests { vec![Stmt::Return(Some(expr))] } + fn class(id: u32, name: &str, extends: Option, extends_name: Option<&str>) -> Class { + Class { + id, + name: name.to_string(), + type_params: Vec::new(), + extends, + extends_name: extends_name.map(str::to_string), + native_extends: None, + extends_expr: None, + heritage_lexically_shadowed: false, + fields: Vec::new(), + constructor: None, + methods: Vec::new(), + getters: Vec::new(), + setters: Vec::new(), + static_accessor_names: Vec::new(), + static_accessor_fn_ids: Vec::new(), + static_fields: Vec::new(), + static_methods: Vec::new(), + computed_members: Vec::new(), + decorators: Vec::new(), + is_exported: false, + aliases: Vec::new(), + is_nested: false, + alloc_width_hint: 0, + } + } + + #[test] + fn receiver_clone_rejects_name_only_imported_parent_chain() { + let parent = class(1, "Parent", None, None); + let child = class(2, "Child", None, Some("Parent")); + let classes = HashMap::from([("Parent".to_string(), &parent)]); + + assert!(matches!( + typed_receiver_chain_fields(&classes, &child), + Err(TypedCloneRejectionReason::ReceiverClassExtends) + )); + } + #[test] fn f64_clone_accepts_mixed_raw_params_when_return_expr_is_numeric_safe() { let f = function( diff --git a/crates/perry-codegen/src/collectors/i64_emit.rs b/crates/perry-codegen/src/collectors/i64_emit.rs index c3b4681f13..f927f4206c 100644 --- a/crates/perry-codegen/src/collectors/i64_emit.rs +++ b/crates/perry-codegen/src/collectors/i64_emit.rs @@ -32,7 +32,7 @@ pub fn emit_i64_function(llmod: &mut crate::module::LlModule, f: &Function, i64_ cx.f.block_mut(cx.cur).unwrap().ret(I64, "0"); } } -struct I64Cx<'a> { +pub(crate) struct I64Cx<'a> { f: &'a mut crate::function::LlFunction, cur: usize, locals: std::collections::HashMap, diff --git a/crates/perry-codegen/src/collectors/mod.rs b/crates/perry-codegen/src/collectors/mod.rs index 17f1af31ff..d7e2120d6d 100644 --- a/crates/perry-codegen/src/collectors/mod.rs +++ b/crates/perry-codegen/src/collectors/mod.rs @@ -78,7 +78,7 @@ pub(crate) use shadow_slots::{ collect_declared_shadow_slots_in_stmts, collect_shadow_slot_clear_points, }; pub(crate) use spec_abi_sites::{ - collect_spec_abi_facts, local_is_reassigned, reassigned_locals, SpecParamRep, SpecTaBinding, + collect_spec_abi_facts, reassigned_locals, SpecParamRep, SpecTaBinding, }; pub(crate) use this_as_value::{ class_chain_extends_builtin_error, class_chain_has_unmodeled_base, class_uses_this_as_value, diff --git a/crates/perry-codegen/src/collectors/spec_abi_sites.rs b/crates/perry-codegen/src/collectors/spec_abi_sites.rs index 5eb506df12..1122996ec7 100644 --- a/crates/perry-codegen/src/collectors/spec_abi_sites.rs +++ b/crates/perry-codegen/src/collectors/spec_abi_sites.rs @@ -126,6 +126,7 @@ pub(crate) fn reassigned_locals(stmts: &[Stmt]) -> HashSet { } /// Single-id convenience over [`reassigned_locals`]. +#[cfg(test)] pub(crate) fn local_is_reassigned(stmts: &[Stmt], id: u32) -> bool { reassigned_locals(stmts).contains(&id) } @@ -197,8 +198,10 @@ fn record_expr_use(e: &Expr, depth: u32, scan: &mut ModuleScan) { receiver, .. } => { - record_receiver_use(target, depth, scan); - record_receiver_use(receiver, depth, scan); + // Ordinary [[Set]] can invoke proxies/accessors and otherwise + // expose either object, so neither position is length-safe. + record_expr_use(target, depth, scan); + record_expr_use(receiver, depth, scan); record_expr_use(key, depth, scan); record_expr_use(value, depth, scan); } diff --git a/crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs b/crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs index d3d8b6353b..ec3349ca61 100644 --- a/crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs +++ b/crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs @@ -282,6 +282,32 @@ fn length_unsafe_source_use_demotes_const_len_only() { assert_eq!(b.const_len, None); } +#[test] +fn put_value_set_receiver_demotes_source_const_len() { + let source = Expr::LocalGet(1); + let m = module_with_init(vec![ + let_stmt( + 1, + true, + Expr::Array(vec![Expr::Integer(1), Expr::Integer(2)]), + ), + Stmt::Expr(Expr::PutValueSet { + target: Box::new(source.clone()), + key: Box::new(Expr::Integer(0)), + value: Box::new(Expr::Integer(3)), + receiver: Box::new(source), + strict: false, + }), + let_stmt( + 2, + false, + ta_new(TYPED_ARRAY_KIND_INT32, Some(Expr::LocalGet(1))), + ), + ]); + let facts = collect_spec_abi_facts(&m); + assert_eq!(facts.ta_bindings.get(&2).map(|b| b.const_len), Some(None)); +} + #[test] fn bigint_kind_binding_is_rejected() { // BigInt64Array elements are BigInt, not Number — never a `TaPtr` binding. diff --git a/crates/perry-codegen/src/ext_registry.rs b/crates/perry-codegen/src/ext_registry.rs index 0a1a091c4e..7ebca1341b 100644 --- a/crates/perry-codegen/src/ext_registry.rs +++ b/crates/perry-codegen/src/ext_registry.rs @@ -586,23 +586,23 @@ const FFI_REGISTRY: &[(&str, OwnerKind)] = &[ /// optimization horizon worth measuring. static USED_PROVIDERS: Mutex>> = Mutex::new(None); -/// Per-module capture buffer, active only between [`begin_module_capture`] -/// and [`take_module_capture`] on the same thread. -/// -/// Why a thread-local rather than another field on [`USED_PROVIDERS`]: -/// the object cache (`crates/perry/src/commands/compile/object_cache.rs`) -/// needs to know which registry symbols *this one module* emitted, so it -/// can persist them next to the module's cached `.o` and replay them on a -/// later cache hit. [`USED_PROVIDERS`] is process-wide and rayon compiles -/// many modules concurrently, so it cannot attribute a symbol to a module. -/// `perry-codegen` itself never uses rayon and `compile_module` runs start -/// to finish on its caller's worker thread, so a thread-local scoped -/// around that one call captures exactly this module's emissions. -/// -/// We record the matched symbol *names* rather than [`OwnerKind`]s: replay -/// re-runs them through [`record_ffi_call`], so the symbol→owner mapping is -/// always the one in today's table, never a stale routing decision baked -/// into a cache entry written by an older perry. +// Per-module capture buffer, active only between [`begin_module_capture`] +// and [`take_module_capture`] on the same thread. +// +// Why a thread-local rather than another field on [`USED_PROVIDERS`]: +// the object cache (`crates/perry/src/commands/compile/object_cache.rs`) +// needs to know which registry symbols *this one module* emitted, so it +// can persist them next to the module's cached `.o` and replay them on a +// later cache hit. [`USED_PROVIDERS`] is process-wide and rayon compiles +// many modules concurrently, so it cannot attribute a symbol to a module. +// `perry-codegen` itself never uses rayon and `compile_module` runs start +// to finish on its caller's worker thread, so a thread-local scoped +// around that one call captures exactly this module's emissions. +// +// We record the matched symbol *names* rather than [`OwnerKind`]s: replay +// re-runs them through [`record_ffi_call`], so the symbol→owner mapping is +// always the one in today's table, never a stale routing decision baked +// into a cache entry written by an older perry. thread_local! { static MODULE_CAPTURE: RefCell>> = const { RefCell::new(None) }; } diff --git a/crates/perry-codegen/src/lower_call/early_branches.rs b/crates/perry-codegen/src/lower_call/early_branches.rs index 737868951d..34a57dfd7e 100644 --- a/crates/perry-codegen/src/lower_call/early_branches.rs +++ b/crates/perry-codegen/src/lower_call/early_branches.rs @@ -31,14 +31,6 @@ fn typed_i1_closure_signature_note(reps: &[crate::codegen::TypedParamRep]) -> St } } -fn typed_string_closure_signature_note(arg_count: usize) -> String { - if arg_count <= 1 { - "typed_signature=string(i64 closure, string)->string".to_string() - } else { - "typed_signature=string(i64 closure, string, ...)->string".to_string() - } -} - fn typed_closure_signature_note(ret: &str, reps: &[crate::codegen::TypedParamRep]) -> String { let first = reps.first().map(|rep| rep.label()).unwrap_or("void"); if reps.len() <= 1 { @@ -48,14 +40,6 @@ fn typed_closure_signature_note(ret: &str, reps: &[crate::codegen::TypedParamRep } } -fn typed_i32_closure_signature_note(arg_count: usize) -> String { - if arg_count <= 1 { - "typed_signature=i32(i64 closure, i32)->i32".to_string() - } else { - "typed_signature=i32(i64 closure, i32, ...)->i32".to_string() - } -} - fn is_async_dispose_symbol_index(index: &Expr) -> bool { let Expr::SymbolFor(symbol_name) = index else { return false; diff --git a/crates/perry-codegen/src/lower_call/func_ref.rs b/crates/perry-codegen/src/lower_call/func_ref.rs index 6b833d4fe6..f1f477345a 100644 --- a/crates/perry-codegen/src/lower_call/func_ref.rs +++ b/crates/perry-codegen/src/lower_call/func_ref.rs @@ -45,14 +45,6 @@ fn typed_i1_signature_note(reps: &[crate::codegen::TypedParamRep]) -> String { } } -fn typed_i32_signature_note(arg_count: usize) -> String { - match arg_count { - 0 => "typed_signature=i32()->i32".to_string(), - 1 => "typed_signature=i32(i32)->i32".to_string(), - _ => "typed_signature=i32(i32, ...)->i32".to_string(), - } -} - /// Representation-selection Phase 2, Tier A: statically-proven dispatch to a /// specialized entry — direct call with raw args, no guard, no diamond, no /// phi. Every non-`Boxed` slot must be proven BY CONSTRUCTION at this exact diff --git a/crates/perry-codegen/src/lower_call/method_override.rs b/crates/perry-codegen/src/lower_call/method_override.rs index 7017e8eb67..25dba1a303 100644 --- a/crates/perry-codegen/src/lower_call/method_override.rs +++ b/crates/perry-codegen/src/lower_call/method_override.rs @@ -22,22 +22,6 @@ fn typed_i1_method_signature_note(reps: &[crate::codegen::TypedParamRep]) -> Str } } -fn typed_i32_method_signature_note(arg_count: usize) -> String { - if arg_count <= 1 { - "typed_signature=i32(i32)->i32".to_string() - } else { - "typed_signature=i32(i32, ...)->i32".to_string() - } -} - -fn typed_string_method_signature_note(arg_count: usize) -> String { - if arg_count <= 1 { - "typed_signature=string(string)->string".to_string() - } else { - "typed_signature=string(string, ...)->string".to_string() - } -} - fn typed_method_signature_note(ret: &str, reps: &[crate::codegen::TypedParamRep]) -> String { let first = reps.first().map(|rep| rep.label()).unwrap_or("void"); if reps.len() <= 1 { diff --git a/crates/perry-codegen/src/nm_install.rs b/crates/perry-codegen/src/nm_install.rs index fd48b62044..f918708f40 100644 --- a/crates/perry-codegen/src/nm_install.rs +++ b/crates/perry-codegen/src/nm_install.rs @@ -70,6 +70,7 @@ pub(crate) fn nm_install_symbol(name: &str) -> Option<&'static str> { /// All dispatch-install symbols + the dynamic fallback — declared so codegen can /// emit calls to them. +#[allow(dead_code)] // consumed only by codegen configurations that emit dispatch declarations pub(crate) const NM_INSTALL_SYMBOLS: &[&str] = &[ "js_nm_install_assert", "js_nm_install_async_hooks", @@ -139,6 +140,7 @@ pub(crate) fn nm_submod_install_symbol(key: &str) -> Option<&'static str> { } } +#[allow(dead_code)] // consumed only by codegen configurations that emit dispatch declarations pub(crate) const NM_SUBMOD_INSTALL_SYMBOLS: &[&str] = &[ "js_node_submod_install_vm", "js_node_submod_install_timers", diff --git a/crates/perry-codegen/tests/native_proof_buffer_views.rs b/crates/perry-codegen/tests/native_proof_buffer_views.rs index eaf9ea63ff..3eae5e7f70 100644 --- a/crates/perry-codegen/tests/native_proof_buffer_views.rs +++ b/crates/perry-codegen/tests/native_proof_buffer_views.rs @@ -1,3 +1,9 @@ +// The `native_proof_*` integration tests share one hand-written HIR builder +// toolkit, and each file drives a different subset of it. Per-file pruning +// would make the next test in this family re-add the builder it needs, so the +// toolkit stays whole. +#![allow(dead_code)] + use perry_codegen::{compile_module, AppMetadata, CompileOptions}; use perry_hir::types::{FunctionType, ObjectType, PropertyInfo, Type}; use perry_hir::{ diff --git a/crates/perry-codegen/tests/native_proof_regressions.rs b/crates/perry-codegen/tests/native_proof_regressions.rs index 846b8c0c42..a3f7efcc87 100644 --- a/crates/perry-codegen/tests/native_proof_regressions.rs +++ b/crates/perry-codegen/tests/native_proof_regressions.rs @@ -1,3 +1,5 @@ +// See native_proof_buffer_views.rs — shared HIR builder toolkit, each file in +// this family drives a different subset. use perry_codegen::{compile_module, AppMetadata, CompileOptions}; use perry_hir::types::{ObjectType, PropertyInfo, Type, TypeParam}; use perry_hir::{ @@ -414,6 +416,7 @@ fn buffer_let(id: u32, name: &str, size: Expr) -> Stmt { } } +#[allow(dead_code)] // shared HIR-builder vocabulary retained for sibling regressions fn typed_array_let(id: u32, name: &str, class_name: &str, kind: u8, length: Expr) -> Stmt { Stmt::Let { id, @@ -560,6 +563,7 @@ fn buffer_set(buffer_id: u32, index: Expr) -> Stmt { }) } +#[allow(dead_code)] // shared HIR-builder vocabulary retained for sibling regressions fn buffer_read(buffer_id: u32, method: &str, index: Expr) -> Expr { call( Expr::PropertyGet { @@ -6769,6 +6773,28 @@ fn boxed_local_slot_uses_i64_js_value_bits_until_helper_edges() { } } +#[test] +fn tdz_numeric_const_read_is_not_constant_folded() { + let mut fixture = module("tdz_numeric_const.ts", Vec::new()); + fixture.init = vec![ + Stmt::PreallocateTdzBoxes(vec![9]), + Stmt::Expr(Expr::LocalGet(9)), + Stmt::Let { + id: 9, + name: "value".to_string(), + ty: Type::Number, + mutable: false, + init: Some(Expr::Number(42.0)), + }, + ]; + + let ir = String::from_utf8(compile_module(&fixture, empty_opts()).unwrap()).unwrap(); + assert!( + ir.contains("call i64 @js_box_get_bits(i64 "), + "the pre-declaration read must retain the TDZ box check:\n{ir}" + ); +} + #[test] fn boxed_param_slot_uses_i64_js_value_bits_until_helper_edges() { let module = boxed_param_capture_module("boxed_param_js_value_bits_ir.ts"); diff --git a/crates/perry-container-compose/tests/backend_tests.rs b/crates/perry-container-compose/tests/backend_tests.rs index 79a7ad8ae4..4d1671ebbb 100644 --- a/crates/perry-container-compose/tests/backend_tests.rs +++ b/crates/perry-container-compose/tests/backend_tests.rs @@ -1,6 +1,5 @@ use perry_container_compose::backend::*; use perry_container_compose::types::ContainerSpec; -use std::collections::HashMap; // Feature: perry-container | Layer: unit | Req: 1.1 | Property: - #[test] diff --git a/crates/perry-doc-tests/src/main.rs b/crates/perry-doc-tests/src/main.rs index 5ae18b8f38..7e8511447c 100644 --- a/crates/perry-doc-tests/src/main.rs +++ b/crates/perry-doc-tests/src/main.rs @@ -609,16 +609,6 @@ fn run_one( } } -/// Whether a given `--target` value can be built from this host. Any -/// other combination gets reported as `XCOMPILE_SKIP` with the reason so -/// coverage stays visible without failing the job. Also checks whether -/// the host has the toolchain installed (Xcode, Android NDK) so that -/// local dev boxes don't hit false failures when they're simply missing -/// the mobile SDKs. -fn target_buildable_on_host(target: &str, host: &str) -> bool { - target_buildable_reason(target, host).is_none() -} - /// Reason the target can't be built from this host, or None if it can. fn target_buildable_reason(target: &str, host: &str) -> Option { match target { diff --git a/crates/perry-ext-http/src/agent.rs b/crates/perry-ext-http/src/agent.rs index 78d1ba4f40..a8b7fb30ec 100644 --- a/crates/perry-ext-http/src/agent.rs +++ b/crates/perry-ext-http/src/agent.rs @@ -78,10 +78,6 @@ fn bind_agent_method_value(handle: Handle, name: &'static [u8]) -> f64 { unsafe { js_class_method_bind(instance, name.as_ptr(), name.len()) } } -fn pointer_value(handle: Handle) -> f64 { - f64::from_bits(POINTER_TAG | (handle as u64 & PTR_MASK)) -} - // ------------------------------------------------------------------ // AgentHandle // ------------------------------------------------------------------ diff --git a/crates/perry-ext-http/src/lib.rs b/crates/perry-ext-http/src/lib.rs index 56de802405..c9785173be 100644 --- a/crates/perry-ext-http/src/lib.rs +++ b/crates/perry-ext-http/src/lib.rs @@ -102,7 +102,6 @@ use perry_ffi::{ }; use std::collections::HashMap; use std::sync::{Mutex, Once}; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; const STRING_TAG: u64 = 0x7FFF_0000_0000_0000; const POINTER_TAG: u64 = 0x7FFD_0000_0000_0000; diff --git a/crates/perry-ext-http/src/server/handle_dispatch.rs b/crates/perry-ext-http/src/server/handle_dispatch.rs index 4221717753..f20084bbfc 100644 --- a/crates/perry-ext-http/src/server/handle_dispatch.rs +++ b/crates/perry-ext-http/src/server/handle_dispatch.rs @@ -155,9 +155,7 @@ extern "C" { fn js_node_http_res_set_strict_content_length(handle: i64, value: f64); fn js_node_http_res_req_handle(handle: i64) -> i64; fn js_node_http_res_write_head(handle: i64, status: f64, arg2: i64, arg3: i64); - fn js_node_http_res_write(handle: i64, chunk: f64) -> i32; fn js_node_http_res_add_trailers(handle: i64, headers_value: f64); - fn js_node_http_res_end(handle: i64, chunk: f64); fn js_node_http_res_flush_headers(handle: i64); fn js_node_http_res_cork(handle: i64); fn js_node_http_res_uncork(handle: i64); @@ -210,26 +208,6 @@ pub extern "C" fn js_ext_http_server_response_is_handle(handle: i64) -> i32 { } } -/// Methods this dispatcher claims. Kept in sync with the -/// `class_filter: Some("HttpServer")` rows in -/// `crates/perry-codegen/src/lower_call/native_table/http.rs`. -pub const HTTP_SERVER_METHODS: &[&str] = &[ - "listen", - "close", - "closeAllConnections", - "closeIdleConnections", - "address", - "on", - "addListener", - "removeAllListeners", - "removeListener", - "off", - "setTimeout", - "ref", - "unref", - "@@__perry_wk_asyncDispose", -]; - fn http_server_method_bytes(name: &str) -> Option<&'static [u8]> { match name { "listen" => Some(b"listen"), diff --git a/crates/perry-ext-http/src/server/http2_server.rs b/crates/perry-ext-http/src/server/http2_server.rs index 6453edd7a8..7a5187f934 100644 --- a/crates/perry-ext-http/src/server/http2_server.rs +++ b/crates/perry-ext-http/src/server/http2_server.rs @@ -20,17 +20,14 @@ use std::sync::atomic::{AtomicI64, Ordering}; use std::sync::{Arc, Mutex}; use bytes::Bytes; -use http_body_util::{BodyExt, Full}; -use hyper::header::{HeaderName, HeaderValue}; use hyper::service::service_fn; -use hyper::{body::Incoming, Request, Response, Version}; +use hyper::{body::Incoming, Request}; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server::conn::auto::Builder as AutoBuilder; use lazy_static::lazy_static; use perry_ffi::{ - alloc_buffer, alloc_string, get_handle, get_handle_mut, iter_handle_ids_of, iter_handles_of, - iter_handles_of_mut, register_handle, JsClosure, JsValue, ObjectHeader, RawClosureHeader, - StringHeader, + alloc_buffer, alloc_string, get_handle_mut, register_handle, JsClosure, JsValue, ObjectHeader, + RawClosureHeader, StringHeader, }; use tokio::net::TcpListener; use tokio::sync::{mpsc, oneshot}; @@ -38,24 +35,16 @@ use tokio_rustls::TlsAcceptor; use crate::server::ensure_gc_scanner_registered; use crate::server::http2_session_settings::Http2SettingsState; -use crate::server::request::{ - alloc_incoming_message, emit_no_arg_to_listeners, handle_to_pointer_f64, with_implicit_this, - IncomingMessage, -}; -use crate::server::response::{ - alloc_server_response_for_request, HyperResponseShape, ResponseBody, -}; -use crate::server::server::{ - synthesize_default_response_if_needed, HttpPendingRequest, HttpServer, -}; +use crate::server::request::handle_to_pointer_f64; +use crate::server::response::HyperResponseShape; +use crate::server::server::{HttpPendingRequest, HttpServer}; use crate::server::tls::{ build_server_config, has_pem_material, json_value_to_pem_bytes, parse_cert_chain, parse_private_key, }; use crate::server::types::{ - extract_host, extract_port, js_promise_run_microtasks, js_value_is_closure, - jsvalue_to_body_bytes, jsvalue_to_owned_string, read_string_header, POINTER_TAG, PTR_MASK, - STRING_TAG, TAG_NULL, TAG_UNDEFINED, + extract_host, extract_port, js_value_is_closure, jsvalue_to_owned_string, POINTER_TAG, + PTR_MASK, STRING_TAG, TAG_NULL, TAG_UNDEFINED, }; extern "C" { @@ -76,13 +65,13 @@ pub(crate) use controls::{ numeric_value, queue_session_goaway, queue_session_ping, queue_session_settings, }; pub(crate) use pump::{ - has_active_h2_clients, has_pending_h2_events, process_pending_h2, process_pending_h2_events, + has_active_h2_clients, process_pending_h2, process_pending_h2_events, try_recv_pending_h2_nonblocking, }; pub(crate) use session::{ - h2_listening_server_for_authority, local_client_connect_ready, local_server_handle_for_client, - local_server_session_event_ready, mark_server_sessions_closed, mark_session_closed, - parse_headers_object, register_server_session, start_client_request, + local_client_connect_ready, local_server_handle_for_client, local_server_session_event_ready, + mark_server_sessions_closed, mark_session_closed, parse_headers_object, + register_server_session, start_client_request, }; // `handle_h2_request` is consumed by `js_node_http2_server_listen` below. diff --git a/crates/perry-ext-http/src/server/mod.rs b/crates/perry-ext-http/src/server/mod.rs index 4e193713e0..b41c0b3ed2 100644 --- a/crates/perry-ext-http/src/server/mod.rs +++ b/crates/perry-ext-http/src/server/mod.rs @@ -69,9 +69,7 @@ mod tls; mod types; mod upgrade; -pub use handle_dispatch::*; pub use http2_server::*; -pub use http2_settings::*; pub use https_server::*; pub use request::*; pub use response::*; diff --git a/crates/perry-ext-net/src/lib.rs b/crates/perry-ext-net/src/lib.rs index 05b0ac97e5..396712853d 100644 --- a/crates/perry-ext-net/src/lib.rs +++ b/crates/perry-ext-net/src/lib.rs @@ -221,11 +221,6 @@ pub(crate) mod statics { P.get_or_init(|| Mutex::new(Vec::new())) } - pub fn next_net_id() -> &'static Mutex { - static N: OnceLock> = OnceLock::new(); - N.get_or_init(|| Mutex::new(1)) - } - /// Server registry — `net.createServer(...)` returns a handle here. /// Separate from the socket map: server handles host an accept-loop /// shutdown channel and a bound port; sockets host a per-connection diff --git a/crates/perry-ext-zlib/src/stream.rs b/crates/perry-ext-zlib/src/stream.rs index f4eda4fd8e..7bddffa30e 100644 --- a/crates/perry-ext-zlib/src/stream.rs +++ b/crates/perry-ext-zlib/src/stream.rs @@ -426,6 +426,7 @@ impl CodecState { } } +#[allow(dead_code)] // test scaffolding: default-level wrapper used only by the cfg(test) streaming tests fn make_codec_state(codec: Codec) -> Option { make_codec_state_with_level(codec, Compression::default()) } diff --git a/crates/perry-hir/src/destructuring/assignment_expr.rs b/crates/perry-hir/src/destructuring/assignment_expr.rs index 6a801db669..a08cd3323e 100644 --- a/crates/perry-hir/src/destructuring/assignment_expr.rs +++ b/crates/perry-hir/src/destructuring/assignment_expr.rs @@ -88,11 +88,6 @@ pub(crate) fn lower_destructuring_assignment( value: Box::new(index_expr), }); } - _ => { - return Err(anyhow!( - "Unsupported member expression in destructuring" - )); - } } } _ => { diff --git a/crates/perry-hir/src/destructuring/assignment_stmt.rs b/crates/perry-hir/src/destructuring/assignment_stmt.rs index b0573cdf36..b9e704ed47 100644 --- a/crates/perry-hir/src/destructuring/assignment_stmt.rs +++ b/crates/perry-hir/src/destructuring/assignment_stmt.rs @@ -471,9 +471,6 @@ fn prepare_assignment_target( None, )) } - _ => Err(anyhow!( - "Unsupported member expression in destructuring assignment" - )), } } _ => Err(anyhow!( diff --git a/crates/perry-hir/src/js_transform/imports.rs b/crates/perry-hir/src/js_transform/imports.rs index ff92d49bd3..dc12b3b38d 100644 --- a/crates/perry-hir/src/js_transform/imports.rs +++ b/crates/perry-hir/src/js_transform/imports.rs @@ -15,7 +15,7 @@ pub struct JsImportInfo { /// Context for tracking JS values during transformation #[derive(Debug, Clone, Default)] -struct JsValueTracker { +pub(crate) struct JsValueTracker { /// LocalIds that hold JS values (from imports or JS function results) js_locals: HashSet, /// Class names that are JS classes (from imports) diff --git a/crates/perry-hir/src/lower/const_fold_fn.rs b/crates/perry-hir/src/lower/const_fold_fn.rs index 516f18d633..7e870ab481 100644 --- a/crates/perry-hir/src/lower/const_fold_fn.rs +++ b/crates/perry-hir/src/lower/const_fold_fn.rs @@ -1443,7 +1443,7 @@ fn extract_fn_expr(module: &ast::Module) -> Option<&ast::FnExpr> { } } -/// Owning arrow analog of [`extract_fn_expr_owned`] — pull the `ArrowExpr` +/// Owning arrow analog of `extract_fn_expr` — pull the `ArrowExpr` /// out of a synthesized `(() => { ... });` module. fn extract_arrow_expr_owned(module: ast::Module) -> Option { let item = module.body.into_iter().next()?; @@ -1460,23 +1460,6 @@ fn extract_arrow_expr_owned(module: ast::Module) -> Option { } } -/// Owning variant of [`extract_fn_expr`] — consumes the module so the caller -/// can mutate the function body before lowering. -fn extract_fn_expr_owned(module: ast::Module) -> Option { - let item = module.body.into_iter().next()?; - let ast::ModuleItem::Stmt(ast::Stmt::Expr(expr_stmt)) = item else { - return None; - }; - let mut e = *expr_stmt.expr; - loop { - match e { - ast::Expr::Paren(p) => e = *p.expr, - ast::Expr::Fn(fn_expr) => return Some(fn_expr), - _ => return None, - } - } -} - /// Build `__perry_cv = ` by cloning a parsed `__perry_cv = undefined` /// assignment template and swapping its right-hand side. Avoids hand-building /// version-sensitive SWC `AssignExpr` nodes. diff --git a/crates/perry-hir/src/lower/context.rs b/crates/perry-hir/src/lower/context.rs index 74b3c0b5b0..7a36be6e2f 100644 --- a/crates/perry-hir/src/lower/context.rs +++ b/crates/perry-hir/src/lower/context.rs @@ -1234,17 +1234,6 @@ impl LoweringContext { } } - /// Current depth of the module-shadow stack (a scope mark). - pub(crate) fn module_shadow_mark(&self) -> usize { - self.module_shadow_stack.len() - } - - /// Restore the module-shadow stack to `mark`, re-exposing modules whose - /// shadowing local bindings went out of scope. - pub(crate) fn truncate_module_shadow(&mut self, mark: usize) { - self.module_shadow_stack.truncate(mark); - } - pub(crate) fn register_builtin_module_alias( &mut self, local_name: String, diff --git a/crates/perry-hir/src/lower/expr_call/intrinsics/eval_strict.rs b/crates/perry-hir/src/lower/expr_call/intrinsics/eval_strict.rs index 01838eba97..bda915e193 100644 --- a/crates/perry-hir/src/lower/expr_call/intrinsics/eval_strict.rs +++ b/crates/perry-hir/src/lower/expr_call/intrinsics/eval_strict.rs @@ -500,37 +500,3 @@ fn eval_module_has_strict_eval_arguments_violation( }; stmts.iter().any(|s| stmt_has_violation(s, top_strict)) } - -fn strict_eval_source_assigns_arguments(source: &str) -> bool { - let bytes = source.as_bytes(); - let needle = b"arguments"; - let mut i = 0usize; - while i + needle.len() <= bytes.len() { - if &bytes[i..i + needle.len()] != needle { - i += 1; - continue; - } - let before_ok = i == 0 || !is_ident_continue(bytes[i - 1]); - let after = i + needle.len(); - let after_ok = after == bytes.len() || !is_ident_continue(bytes[after]); - if before_ok && after_ok { - let mut j = after; - while j < bytes.len() && bytes[j].is_ascii_whitespace() { - j += 1; - } - if j < bytes.len() - && bytes[j] == b'=' - && bytes.get(j + 1).copied() != Some(b'=') - && bytes.get(j + 1).copied() != Some(b'>') - { - return true; - } - } - i = after; - } - false -} - -fn is_ident_continue(byte: u8) -> bool { - byte == b'_' || byte == b'$' || byte.is_ascii_alphanumeric() -} diff --git a/crates/perry-hir/src/lower/fn_ctor_env.rs b/crates/perry-hir/src/lower/fn_ctor_env.rs index 074220ea85..13b742a5ab 100644 --- a/crates/perry-hir/src/lower/fn_ctor_env.rs +++ b/crates/perry-hir/src/lower/fn_ctor_env.rs @@ -44,6 +44,8 @@ pub(crate) enum FnCtorShape { DynCtor(DynFnCtorKind), /// `var f = async function () {};` — a function-literal var, recorded so /// a later `f.constructor` resolves to the right dynamic ctor kind. + #[allow(dead_code)] + // payload retained for the planned `f.constructor` kind resolution; matched only via `_` today FnLiteral(DynFnCtorKind), } diff --git a/crates/perry-hir/src/lower/lowering_context.rs b/crates/perry-hir/src/lower/lowering_context.rs index 26f1db2818..e9a68f7639 100644 --- a/crates/perry-hir/src/lower/lowering_context.rs +++ b/crates/perry-hir/src/lower/lowering_context.rs @@ -57,9 +57,6 @@ pub(crate) struct PrivateScope { /// synthesize a real class extending the concrete base. #[derive(Debug, Clone)] pub(crate) struct MixinFn { - /// The mixin function's single parameter — the name the returned class - /// `extends`. - pub(crate) param_name: String, /// The returned class EXPRESSION's own name, if it has one (`return class /// Named extends B {}`). `None` for the anonymous form, whose `.name` is /// the empty string per spec — a directly-returned class expression gets diff --git a/crates/perry-hir/src/lower/misc.rs b/crates/perry-hir/src/lower/misc.rs index 92e6d34ebc..ac19171eec 100644 --- a/crates/perry-hir/src/lower/misc.rs +++ b/crates/perry-hir/src/lower/misc.rs @@ -44,27 +44,6 @@ pub(crate) fn stmt_list_starts_with_use_strict_directive(stmts: &[ast::Stmt]) -> false } -pub(crate) fn module_starts_with_use_strict_directive(module: &ast::Module) -> bool { - for item in &module.body { - match item { - ast::ModuleItem::Stmt(stmt) => match is_use_strict_directive_stmt(stmt) { - Some(true) => return true, - Some(false) => continue, - None => return false, - }, - ast::ModuleItem::ModuleDecl(_) => return false, - } - } - false -} - -pub(crate) fn module_has_module_declaration(module: &ast::Module) -> bool { - module - .body - .iter() - .any(|item| matches!(item, ast::ModuleItem::ModuleDecl(_))) -} - /// Map a function's declared return type to a native-instance class when it /// matches a known stdlib pattern. Lets a wrapper function like /// `function openSocket(host, port): Socket { ... }` advertise that calls diff --git a/crates/perry-hir/src/lower/pre_scan.rs b/crates/perry-hir/src/lower/pre_scan.rs index 3aa2d09454..66d1ae75ed 100644 --- a/crates/perry-hir/src/lower/pre_scan.rs +++ b/crates/perry-hir/src/lower/pre_scan.rs @@ -409,7 +409,6 @@ pub(crate) fn pre_scan_mixin_functions(ast_module: &ast::Module, ctx: &mut Lower ctx.mixin_funcs.insert( fn_name, crate::lower::MixinFn { - param_name, class_expr_name: class_expr.ident.as_ref().map(|i| i.sym.to_string()), class_ast: Box::new((*class_expr.class).clone()), }, diff --git a/crates/perry-hir/src/lower_decl/class_captures.rs b/crates/perry-hir/src/lower_decl/class_captures.rs index 19220ab839..6673e4a262 100644 --- a/crates/perry-hir/src/lower_decl/class_captures.rs +++ b/crates/perry-hir/src/lower_decl/class_captures.rs @@ -274,13 +274,6 @@ pub fn synthesize_class_captures( // prologue ids when the closure body references them, and a closure // whose ONLY reference is the appended arg gets the id added to its // captures list below. - fn append_self_new_args_expr( - expr: &mut Expr, - class_name: &str, - cap_args: &[(LocalId, LocalId)], - ) { - append_new_args_expr(expr, class_name, cap_args, false) - } fn append_self_new_args_stmt( stmt: &mut Stmt, class_name: &str, diff --git a/crates/perry-hir/src/stable_hash/expr.rs b/crates/perry-hir/src/stable_hash/expr.rs index 9684beea9d..5cddce2581 100644 --- a/crates/perry-hir/src/stable_hash/expr.rs +++ b/crates/perry-hir/src/stable_hash/expr.rs @@ -549,7 +549,6 @@ impl SH for Expr { Expr::UrlSearchParamsSort(e) => { tag(h, 703); e.as_ref().hash(h); } Expr::UrlSearchParamsForEach { params, callback, this_arg } => { tag(h, 704); params.as_ref().hash(h); callback.as_ref().hash(h); match this_arg { Some(v) => { tag(h, 1); v.as_ref().hash(h); } None => tag(h, 0), } } Expr::Delete(e) => { tag(h, 381); e.as_ref().hash(h); } - Expr::NewTarget => { tag(h, 12271); } Expr::Closure { func_id, params, return_type, body, captures, mutable_captures, captures_this, captures_new_target, enclosing_class, is_arrow, is_async, is_generator, is_strict, } => { tag(h, 382); func_id.hash(h); params.hash(h); return_type.hash(h); body.hash(h); captures.hash(h); mutable_captures.hash(h); captures_this.hash(h); captures_new_target.hash(h); enclosing_class.hash(h); is_arrow.hash(h); is_async.hash(h); is_generator.hash(h); is_strict.hash(h); } Expr::RegExp { pattern, flags } => { tag(h, 383); pattern.hash(h); flags.hash(h); } Expr::RegExpDynamic { pattern, flags, is_call } => { tag(h, 475); pattern.as_ref().hash(h); if let Some(f_box) = flags { tag(h, 476); f_box.as_ref().hash(h); } else { tag(h, 477); } tag(h, if *is_call { 478 } else { 479 }); } diff --git a/crates/perry-hir/src/walker/expr_mut.rs b/crates/perry-hir/src/walker/expr_mut.rs index 9eab517228..77d9335b8d 100644 --- a/crates/perry-hir/src/walker/expr_mut.rs +++ b/crates/perry-hir/src/walker/expr_mut.rs @@ -28,7 +28,6 @@ where | Expr::NewTarget | Expr::ClassRef(_) | Expr::This - | Expr::NewTarget | Expr::SuperPropertyGet { .. } | Expr::EnumMember { .. } | Expr::StaticFieldGet { .. } diff --git a/crates/perry-hir/src/walker/expr_ref.rs b/crates/perry-hir/src/walker/expr_ref.rs index 98842b2973..7bad53d64e 100644 --- a/crates/perry-hir/src/walker/expr_ref.rs +++ b/crates/perry-hir/src/walker/expr_ref.rs @@ -29,7 +29,6 @@ where | Expr::NewTarget | Expr::ClassRef(_) | Expr::This - | Expr::NewTarget | Expr::SuperPropertyGet { .. } | Expr::EnumMember { .. } | Expr::StaticFieldGet { .. } diff --git a/crates/perry-runtime/src/abi_trampoline.rs b/crates/perry-runtime/src/abi_trampoline.rs index 28f0eed6a6..eadf84c795 100644 --- a/crates/perry-runtime/src/abi_trampoline.rs +++ b/crates/perry-runtime/src/abi_trampoline.rs @@ -378,7 +378,7 @@ mod tests { for i in 1..70 { args.push(i as f64); } - let got = unsafe { call_all_f64(sum70 as usize, &args) }; + let got = unsafe { call_all_f64(sum70 as *const () as usize, &args) }; // expected = sum(args[i]*(i+1)) let expected: f64 = args .iter() @@ -409,7 +409,7 @@ mod tests { #[test] fn trampoline_stack_spill_args_9_and_10() { let args = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 42.0, 7.0]; - let got = unsafe { call_all_f64(pick as usize, &args) }; + let got = unsafe { call_all_f64(pick as *const () as usize, &args) }; assert_eq!(got, 42.0 * 1000.0 + 7.0); } } diff --git a/crates/perry-runtime/src/array/from_concat.rs b/crates/perry-runtime/src/array/from_concat.rs index bb65ce3600..d936f6011d 100644 --- a/crates/perry-runtime/src/array/from_concat.rs +++ b/crates/perry-runtime/src/array/from_concat.rs @@ -140,7 +140,8 @@ pub extern "C" fn js_array_from_value(boxed: f64) -> *mut ArrayHeader { js_array_clone(ptr_bits as *const ArrayHeader) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FROM_VALUE: extern "C" fn(f64) -> *mut ArrayHeader = js_array_from_value; /// `Array.from(source, mapFn, thisArg)` — the mapped form. Throws for nullish @@ -165,7 +166,8 @@ pub extern "C" fn js_array_from_mapped( as *mut ArrayHeader } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FROM_MAPPED: extern "C" fn(f64, f64, f64) -> *mut ArrayHeader = js_array_from_mapped; @@ -282,7 +284,8 @@ pub extern "C" fn js_array_concat_variadic( result } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_CONCAT_VARIADIC: extern "C" fn( *const ArrayHeader, *const f64, @@ -537,12 +540,10 @@ fn items_is_iterable(items: f64) -> bool { } // A bare iterator / generator object exposes a callable `next`. let next_key = crate::string::js_string_from_bytes(b"next".as_ptr(), 4); - let next_val = unsafe { - crate::object::js_object_get_field_by_name( - raw as *const crate::object::ObjectHeader, - next_key, - ) - }; + let next_val = crate::object::js_object_get_field_by_name( + raw as *const crate::object::ObjectHeader, + next_key, + ); if next_val.is_undefined() { return false; } diff --git a/crates/perry-runtime/src/array/generic.rs b/crates/perry-runtime/src/array/generic.rs index 38957fae5b..1c9aae9614 100644 --- a/crates/perry-runtime/src/array/generic.rs +++ b/crates/perry-runtime/src/array/generic.rs @@ -1120,7 +1120,8 @@ fn clamp_index(v: f64, len: i64) -> i64 { // Keep the generic entry points anchored against dead-strip in the default // (codegen-only reference) compile path (see #3320 — `#[no_mangle]` alone is // not enough once the bitcode is re-linked). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_CB: [extern "C" fn(f64, f64, f64) -> f64; 9] = [ js_arraylike_forEach, js_arraylike_map, @@ -1132,20 +1133,25 @@ static KEEP_ARRAYLIKE_CB: [extern "C" fn(f64, f64, f64) -> f64; 9] = [ js_arraylike_findLast, js_arraylike_findLastIndex, ]; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_REDUCE: [extern "C" fn(f64, f64, i32, f64) -> f64; 2] = [js_arraylike_reduce, js_arraylike_reduceRight]; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_SEARCH: [extern "C" fn(f64, f64, f64, i32) -> f64; 3] = [ js_arraylike_indexOf, js_arraylike_lastIndexOf, js_arraylike_includes, ]; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_AT: extern "C" fn(f64, f64) -> f64 = js_arraylike_at; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_JOIN: extern "C" fn(f64, f64) -> f64 = js_arraylike_join; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_SLICE: extern "C" fn(f64, f64, i32, f64, i32) -> f64 = js_arraylike_slice; // --------------------------------------------------------------------------- @@ -1636,9 +1642,11 @@ pub extern "C" fn js_arraylike_splice(recv: f64, args_ptr: *const f64, count: i3 object_splice(o, args_ptr, count) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_SORT: extern "C" fn(f64, f64) -> f64 = js_arraylike_sort; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAYLIKE_VARIADIC: [extern "C" fn(f64, *const f64, i32) -> f64; 2] = [js_arraylike_concat, js_arraylike_splice]; diff --git a/crates/perry-runtime/src/array/header.rs b/crates/perry-runtime/src/array/header.rs index ee0a8e130d..3daca230cf 100644 --- a/crates/perry-runtime/src/array/header.rs +++ b/crates/perry-runtime/src/array/header.rs @@ -171,7 +171,8 @@ pub extern "C" fn js_tagged_template_get_or_init( cooked_handle.get_raw_mut_ptr::() } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TAGGED_TEMPLATE_GET_OR_INIT: extern "C" fn( u64, *mut ArrayHeader, @@ -341,6 +342,7 @@ pub(crate) unsafe fn array_named_property_set( /// bypassing `js_array_set_string_key`'s guard ladder sound. Keys must not be /// numeric index strings or `"length"` (those live in element storage / /// the header, not this side table). +#[cfg(feature = "regex-engine")] pub(crate) unsafe fn array_named_props_install_fresh( arr: *mut ArrayHeader, entries: &[(&'static str, f64)], @@ -1487,22 +1489,28 @@ pub extern "C" fn js_array_is_numeric_f64_layout(arr: *const ArrayHeader) -> i32 // These raw numeric-array helpers are called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` exports. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_NUMERIC_VALUE_TO_RAW_F64: extern "C" fn(f64) -> f64 = js_array_numeric_value_to_raw_f64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_MARK_NUMERIC_F64_LAYOUT: extern "C" fn(*mut ArrayHeader) -> i32 = js_array_mark_numeric_f64_layout; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_CLEAR_NUMERIC_LAYOUT: extern "C" fn(*mut ArrayHeader) = js_array_clear_numeric_layout; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_NOTE_NUMERIC_WRITE: extern "C" fn(*mut ArrayHeader, u64) = js_array_note_numeric_write; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_IS_NUMERIC_F64_LAYOUT: extern "C" fn(*const ArrayHeader) -> i32 = js_array_is_numeric_f64_layout; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_REFRESH_LOCAL_HEAD: extern "C" fn(f64) -> f64 = js_array_refresh_local_head; /// Calculate the byte size for an array with N elements capacity diff --git a/crates/perry-runtime/src/array/indexing.rs b/crates/perry-runtime/src/array/indexing.rs index 3c77eeaaba..4265d69a93 100644 --- a/crates/perry-runtime/src/array/indexing.rs +++ b/crates/perry-runtime/src/array/indexing.rs @@ -440,7 +440,8 @@ pub(crate) unsafe fn keys_array_len_capped_to_capacity(arr: *const ArrayHeader) /// native-region wrappers (`__perry_wrap_*`) and elsewhere, so it must be a /// `#[no_mangle]` C export AND survive dead-stripping even when no Rust caller /// keeps it referenced — mirroring the neighbouring `js_array_push`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_LENGTH: extern "C" fn(*const ArrayHeader) -> u32 = js_array_length; #[no_mangle] @@ -852,10 +853,12 @@ pub extern "C" fn js_array_numeric_set_f64_unboxed( // These raw numeric-array helpers are called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` exports. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_NUMERIC_GET_F64_UNBOXED: extern "C" fn(*mut ArrayHeader, u32) -> f64 = js_array_numeric_get_f64_unboxed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_NUMERIC_SET_F64_UNBOXED: extern "C" fn(*mut ArrayHeader, u32, f64) -> i32 = js_array_numeric_set_f64_unboxed; @@ -1450,27 +1453,33 @@ pub extern "C" fn js_array_numeric_range_add_len(receiver: f64, start: f64, delt array_numeric_range_add_impl(receiver, start, None, delta) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FILL_F64_CONST_EXTEND: extern "C" fn( *mut ArrayHeader, u32, f64, ) -> *mut ArrayHeader = js_array_fill_f64_const_extend; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FILL_F64_IOTA_EXTEND: extern "C" fn(*mut ArrayHeader, u32) -> *mut ArrayHeader = js_array_fill_f64_iota_extend; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FILL_F64_CONST_LEN_EXTEND: extern "C" fn( *mut ArrayHeader, f64, ) -> *mut ArrayHeader = js_array_fill_f64_const_len_extend; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_FILL_F64_IOTA_LEN_EXTEND: extern "C" fn(*mut ArrayHeader) -> *mut ArrayHeader = js_array_fill_f64_iota_len_extend; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_NUMERIC_RANGE_ADD: extern "C" fn(f64, f64, f64, f64) -> i64 = js_array_numeric_range_add; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_NUMERIC_RANGE_ADD_LEN: extern "C" fn(f64, f64, f64) -> i64 = js_array_numeric_range_add_len; diff --git a/crates/perry-runtime/src/array/iter_methods.rs b/crates/perry-runtime/src/array/iter_methods.rs index 19ce7ef947..9159279c5e 100644 --- a/crates/perry-runtime/src/array/iter_methods.rs +++ b/crates/perry-runtime/src/array/iter_methods.rs @@ -1044,7 +1044,8 @@ pub extern "C" fn js_array_join_value( // The feature-gated `#[used]` static pins the symbol for the bitcode-LTO // link (`keepalive-anchors`); the classic link keeps it via the program's // own undefined reference. Same pattern as `node_stream_keepalive.rs`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_JOIN_VALUE: extern "C" fn( *const ArrayHeader, f64, @@ -1109,7 +1110,8 @@ pub extern "C" fn js_array_to_locale_string( crate::string::js_string_from_bytes(out.as_ptr(), out.len() as u32) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ARRAY_TO_LOCALE_STRING: extern "C" fn( *const ArrayHeader, f64, @@ -1232,7 +1234,8 @@ pub extern "C" fn js_validate_array_callback(cb_boxed: f64) -> i64 { throw_not_a_function(render_callback_typeof(cb_boxed)); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_ARRAY_CALLBACK: extern "C" fn(f64) -> i64 = js_validate_array_callback; /// Validate a `map` callback (#4091). Identical to @@ -1253,6 +1256,7 @@ pub extern "C" fn js_validate_array_map_callback(arr: i64, cb_boxed: f64) -> i64 throw_not_a_function(rendered); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_ARRAY_MAP_CALLBACK: extern "C" fn(i64, f64) -> i64 = js_validate_array_map_callback; diff --git a/crates/perry-runtime/src/array/iter_object.rs b/crates/perry-runtime/src/array/iter_object.rs index 9f6ef39baf..06dbd9caef 100644 --- a/crates/perry-runtime/src/array/iter_object.rs +++ b/crates/perry-runtime/src/array/iter_object.rs @@ -88,24 +88,22 @@ pub fn array_values_iter_null_done( if arr_ptr.is_null() { return f64::from_bits(TAG_UNDEFINED); } - unsafe { - let obj = js_object_alloc(ARRAY_ITERATOR_CLASS_ID, 5); - js_object_set_field( - obj, - 0, - JSValue::from_bits(js_nanbox_pointer(arr_ptr as i64).to_bits()), - ); - js_object_set_field(obj, 1, JSValue::number(0.0)); - js_object_set_field(obj, 2, JSValue::number(KIND_VALUES_NULL_DONE as f64)); - js_object_set_field( - obj, - 3, - JSValue::pointer(iteration_epoch as *const _ as *const u8), - ); - js_object_set_field(obj, 4, JSValue::number(epoch as f64)); - crate::object::attach_iterator_prototype(obj, ARRAY_ITERATOR_CLASS_ID); - js_nanbox_pointer(obj as i64) - } + let obj = js_object_alloc(ARRAY_ITERATOR_CLASS_ID, 5); + js_object_set_field( + obj, + 0, + JSValue::from_bits(js_nanbox_pointer(arr_ptr as i64).to_bits()), + ); + js_object_set_field(obj, 1, JSValue::number(0.0)); + js_object_set_field(obj, 2, JSValue::number(KIND_VALUES_NULL_DONE as f64)); + js_object_set_field( + obj, + 3, + JSValue::pointer(iteration_epoch as *const _ as *const u8), + ); + js_object_set_field(obj, 4, JSValue::number(epoch as f64)); + crate::object::attach_iterator_prototype(obj, ARRAY_ITERATOR_CLASS_ID); + js_nanbox_pointer(obj as i64) } /// `arr.keys()` iterator — yields each index `0..length`. diff --git a/crates/perry-runtime/src/array/mod.rs b/crates/perry-runtime/src/array/mod.rs index 705f61e95b..d7e4451ef4 100644 --- a/crates/perry-runtime/src/array/mod.rs +++ b/crates/perry-runtime/src/array/mod.rs @@ -155,10 +155,9 @@ pub(crate) use self::flat_clone::flattenable_array_ptr; pub(crate) use self::header::{ array_byte_size, array_is_frozen, array_is_sealed_or_no_extend, array_named_property_delete, array_named_property_get, array_named_property_get_by_name, array_named_property_has, - array_named_property_names, array_named_property_set, array_named_props_install_fresh, - array_numeric_raw_f64_get, array_numeric_raw_f64_push_inbounds, - array_numeric_raw_f64_set_inbounds, array_object_flags, array_ptr_as_proxy, - canonicalize_array_numeric_store_value, clean_arr_ptr, clean_arr_ptr_mut, + array_named_property_names, array_named_property_set, array_numeric_raw_f64_get, + array_numeric_raw_f64_push_inbounds, array_numeric_raw_f64_set_inbounds, array_object_flags, + array_ptr_as_proxy, canonicalize_array_numeric_store_value, clean_arr_ptr, clean_arr_ptr_mut, clear_array_numeric_layout, clear_array_numeric_layout_ptr, gc_element_slot_range, mark_array_layout_unknown, mark_array_raw_f64_holes_fresh, normalize_array_receiver, note_array_slot, note_array_slot_layout_only, rebuild_array_layout, rebuild_array_layout_exact, @@ -167,5 +166,10 @@ pub(crate) use self::header::{ MIN_ARRAY_CAPACITY, }; +// Sole caller is the regex-engine-gated `regex::exec_array`, so the helper and +// this re-export are gated with it (same cross-gate shape as regex/utf16.rs). +#[cfg(feature = "regex-engine")] +pub(crate) use self::header::array_named_props_install_fresh; + #[cfg(test)] pub(crate) use self::header::{test_seed_template_raw_roots, test_template_raw_roots}; diff --git a/crates/perry-runtime/src/array/push_pop.rs b/crates/perry-runtime/src/array/push_pop.rs index 8b53b4a945..f622d069a4 100644 --- a/crates/perry-runtime/src/array/push_pop.rs +++ b/crates/perry-runtime/src/array/push_pop.rs @@ -417,7 +417,8 @@ pub extern "C" fn js_array_numeric_push_f64_unboxed( // This raw numeric-array helper is called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` export. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_NUMERIC_PUSH_F64_UNBOXED: extern "C" fn( *mut ArrayHeader, f64, @@ -559,7 +560,7 @@ pub extern "C" fn js_array_set_length_strict(arr: *mut ArrayHeader, new_length: if arr.is_null() { return; } - if unsafe { array_object_flags(arr) } & crate::gc::OBJ_FLAG_FROZEN != 0 { + if array_object_flags(arr) & crate::gc::OBJ_FLAG_FROZEN != 0 { throw_non_writable_length(); } js_array_set_length(arr, new_length); @@ -822,7 +823,8 @@ pub extern "C" fn js_array_unshift_variadic( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_UNSHIFT_VARIADIC: extern "C" fn(*mut ArrayHeader, *const f64, u32) -> *mut ArrayHeader = js_array_unshift_variadic; diff --git a/crates/perry-runtime/src/array/sort.rs b/crates/perry-runtime/src/array/sort.rs index 8ac2b33ff0..2522e89eee 100644 --- a/crates/perry-runtime/src/array/sort.rs +++ b/crates/perry-runtime/src/array/sort.rs @@ -597,7 +597,8 @@ pub extern "C" fn js_validate_array_comparator(cmp_boxed: f64) -> i64 { throw_invalid_comparator(cmp_boxed); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_ARRAY_COMPARATOR: extern "C" fn(f64) -> i64 = js_validate_array_comparator; #[cold] diff --git a/crates/perry-runtime/src/array/species.rs b/crates/perry-runtime/src/array/species.rs index 938b4bda31..08e8011994 100644 --- a/crates/perry-runtime/src/array/species.rs +++ b/crates/perry-runtime/src/array/species.rs @@ -208,14 +208,6 @@ pub(crate) unsafe fn array_species_create_with_capacity( } } -/// `true` when `array_species_create` would take the default fast path — used -/// by callers that only need to *validate* the constructor (throwing on a bad -/// one) while keeping their existing plain-array result building, and want to -/// know whether a custom container must instead be populated element-by-element. -pub(crate) unsafe fn species_is_default(original: f64) -> bool { - matches!(resolve_species(original), SpeciesChoice::Default) -} - /// `true` when a species `result` (NaN-boxed) is an ordinary `ArrayHeader` — /// i.e. the default fast path — so the caller can use direct slot writes. pub(crate) unsafe fn species_result_is_plain_array(result: f64) -> bool { diff --git a/crates/perry-runtime/src/array/tests.rs b/crates/perry-runtime/src/array/tests.rs index 536df35a29..17f315d57c 100644 --- a/crates/perry-runtime/src/array/tests.rs +++ b/crates/perry-runtime/src/array/tests.rs @@ -1245,7 +1245,7 @@ fn test_array_pop_and_push() { } #[test] -fn test_array_indexOf() { +fn test_array_index_of() { let arr = js_array_alloc(4); js_array_push_f64(arr, 10.0); js_array_push_f64(arr, 20.0); diff --git a/crates/perry-runtime/src/bigint/convert.rs b/crates/perry-runtime/src/bigint/convert.rs index 69255958c6..582c163d3a 100644 --- a/crates/perry-runtime/src/bigint/convert.rs +++ b/crates/perry-runtime/src/bigint/convert.rs @@ -65,7 +65,8 @@ pub extern "C" fn js_bigint_from_i128_parts(lo: u64, hi: i64) -> *mut BigIntHead bigint_alloc_with_limbs(limbs) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BIGINT_FROM_I128_PARTS: extern "C" fn(u64, i64) -> *mut BigIntHeader = js_bigint_from_i128_parts; diff --git a/crates/perry-runtime/src/box.rs b/crates/perry-runtime/src/box.rs index 37ad6d0e95..0a32d90262 100644 --- a/crates/perry-runtime/src/box.rs +++ b/crates/perry-runtime/src/box.rs @@ -251,9 +251,11 @@ pub extern "C" fn js_tdz_suppress_end() { /// Keepalive anchors for the auto-optimize whole-program build (generated-code- /// only callees — without these the symbols dead-strip and the app link fails). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TDZ_SUPPRESS_BEGIN: extern "C" fn() = js_tdz_suppress_begin; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TDZ_SUPPRESS_END: extern "C" fn() = js_tdz_suppress_end; /// Compatibility wrapper for legacy f64-lowered boxed locals. @@ -483,29 +485,41 @@ fn is_registered_bool_box_ptr(ptr: *mut BoolBox) -> bool { BOOL_BOX_REGISTRY.with(|r| r.borrow().contains(&(ptr as usize))) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_ALLOC_BITS: extern "C" fn(i64) -> *mut Box = js_box_alloc_bits; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_GET_BITS: extern "C" fn(*mut Box) -> i64 = js_box_get_bits; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_SET_BITS: extern "C" fn(*mut Box, i64) = js_box_set_bits; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_ALLOC: extern "C" fn(f64) -> *mut Box = js_box_alloc; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_GET: extern "C" fn(*mut Box) -> f64 = js_box_get; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOX_SET: extern "C" fn(*mut Box, f64) = js_box_set; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_I32_BOX_ALLOC: extern "C" fn(i32) -> *mut I32Box = js_i32_box_alloc; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_I32_BOX_GET: extern "C" fn(*mut I32Box) -> i32 = js_i32_box_get; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_I32_BOX_SET: extern "C" fn(*mut I32Box, i32) = js_i32_box_set; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOOL_BOX_ALLOC: extern "C" fn(i32) -> *mut BoolBox = js_bool_box_alloc; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOOL_BOX_GET: extern "C" fn(*mut BoolBox) -> i32 = js_bool_box_get; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BOOL_BOX_SET: extern "C" fn(*mut BoolBox, i32) = js_bool_box_set; #[cfg(test)] diff --git a/crates/perry-runtime/src/buffer/access.rs b/crates/perry-runtime/src/buffer/access.rs index 71a0c2e478..37c88146cb 100644 --- a/crates/perry-runtime/src/buffer/access.rs +++ b/crates/perry-runtime/src/buffer/access.rs @@ -226,9 +226,10 @@ pub extern "C" fn js_buffer_index_get_value(buf_ptr: *const BufferHeader, index: // #6088: force-keep the JS-value buffer index getter under LTO / // auto-optimize. It has zero internal Rust callers — codegen emits the only // call (in `perry-codegen/src/expr/index_get.rs`), so a whole-program bitcode -// link is otherwise free to internalize and dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` +// link is otherwise free to internalize and dead-strip it. The `#[used]` // anchor pins it (mirrors `KEEP_JS_TYPED_ARRAY_INDEX_GET_DYNAMIC`). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_BUFFER_INDEX_GET_VALUE: extern "C" fn(*const BufferHeader, i32) -> f64 = js_buffer_index_get_value; diff --git a/crates/perry-runtime/src/buffer/dataview.rs b/crates/perry-runtime/src/buffer/dataview.rs index 8c7e902fc9..3eaa14ce0f 100644 --- a/crates/perry-runtime/src/buffer/dataview.rs +++ b/crates/perry-runtime/src/buffer/dataview.rs @@ -39,16 +39,6 @@ pub enum DataViewKind { } impl DataViewKind { - #[inline] - fn width(self) -> usize { - match self { - DataViewKind::Int8 | DataViewKind::Uint8 => 1, - DataViewKind::Int16 | DataViewKind::Uint16 => 2, - DataViewKind::Int32 | DataViewKind::Uint32 | DataViewKind::Float32 => 4, - DataViewKind::Float64 | DataViewKind::BigInt64 | DataViewKind::BigUint64 => 8, - } - } - /// Is this a BigInt-valued accessor (`getBigInt64`/`setBigUint64`/…)? Those /// read/write a NaN-boxed BigInt rather than a Number. #[inline] @@ -453,10 +443,12 @@ fn data_view_method_name<'a>(buf: &'a mut [u8; 16], prefix: &str, kind: DataView } // Called from generated code — keep the exports alive under release/LTO. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DATA_VIEW_GET_DIRECT: extern "C" fn(f64, f64, f64, i32, i32) -> f64 = js_data_view_get_direct; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DATA_VIEW_SET_DIRECT: extern "C" fn(f64, f64, f64, f64, i32, i32) -> f64 = js_data_view_set_direct; diff --git a/crates/perry-runtime/src/buffer/encode.rs b/crates/perry-runtime/src/buffer/encode.rs index e4a3e8eee6..1f088eb4db 100644 --- a/crates/perry-runtime/src/buffer/encode.rs +++ b/crates/perry-runtime/src/buffer/encode.rs @@ -221,9 +221,10 @@ pub extern "C" fn js_value_to_string_with_encoding_or_radix( /// Keepalive anchor: `js_value_to_string_with_encoding_or_radix` is emitted /// only from generated `.o`, so the auto-optimize whole-program LLVM rebuild -/// would internalize + dead-strip it without a `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see +/// would internalize + dead-strip it without a `#[used]` reference (see /// project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALUE_TO_STRING_ENCODING_OR_RADIX: extern "C" fn(f64, i32, f64) -> *mut StringHeader = js_value_to_string_with_encoding_or_radix; diff --git a/crates/perry-runtime/src/buffer/from.rs b/crates/perry-runtime/src/buffer/from.rs index 67eaa88d34..1ff26a2654 100644 --- a/crates/perry-runtime/src/buffer/from.rs +++ b/crates/perry-runtime/src/buffer/from.rs @@ -955,7 +955,7 @@ fn throw_buffer_alloc_size_out_of_range() -> ! { crate::object::js_register_class_extends_error(crate::error::CLASS_ID_RANGE_ERROR); }); let obj = crate::object::js_object_alloc(crate::error::CLASS_ID_RANGE_ERROR, 4); - unsafe { + { let set = |key: &[u8], value: f64| { let key_ptr = crate::string::js_string_from_bytes(key.as_ptr(), key.len() as u32); crate::object::js_object_set_field_by_name(obj, key_ptr, value); @@ -1218,7 +1218,7 @@ fn throw_buffer_concat_invalid_arg_type(index: usize, element: f64) -> ! { }); let obj = crate::object::js_object_alloc(crate::error::CLASS_ID_TYPE_ERROR, 4); - unsafe { + { let set = |key: &[u8], value: f64| { let key_ptr = crate::string::js_string_from_bytes(key.as_ptr(), key.len() as u32); crate::object::js_object_set_field_by_name(obj, key_ptr, value); diff --git a/crates/perry-runtime/src/buffer/header.rs b/crates/perry-runtime/src/buffer/header.rs index 306d7ae27d..0a0824690a 100644 --- a/crates/perry-runtime/src/buffer/header.rs +++ b/crates/perry-runtime/src/buffer/header.rs @@ -17,12 +17,6 @@ fn buffer_payload_size(capacity: usize) -> usize { std::mem::size_of::() + capacity } -#[inline] -fn buffer_gc_total_size(capacity: usize) -> usize { - let payload = buffer_payload_size(capacity); - (crate::gc::GC_HEADER_SIZE + payload + 7) & !7 -} - /// Thread-local registry of buffer pointers for instanceof checks. /// Since BufferHeader has the same layout as ArrayHeader (no type_id field), /// we track buffer pointers separately to distinguish them from arrays. diff --git a/crates/perry-runtime/src/buffer/mutate.rs b/crates/perry-runtime/src/buffer/mutate.rs index 4ddf43ff46..e055582f99 100644 --- a/crates/perry-runtime/src/buffer/mutate.rs +++ b/crates/perry-runtime/src/buffer/mutate.rs @@ -6,7 +6,7 @@ fn throw_invalid_buffer_size() -> ! { crate::object::js_register_class_extends_error(crate::error::CLASS_ID_RANGE_ERROR); }); let obj = crate::object::js_object_alloc(crate::error::CLASS_ID_RANGE_ERROR, 4); - unsafe { + { let set = |key: &[u8], value: f64| { let key_ptr = crate::string::js_string_from_bytes(key.as_ptr(), key.len() as u32); crate::object::js_object_set_field_by_name(obj, key_ptr, value); diff --git a/crates/perry-runtime/src/buffer/query.rs b/crates/perry-runtime/src/buffer/query.rs index cbf86b3ce7..23aa567358 100644 --- a/crates/perry-runtime/src/buffer/query.rs +++ b/crates/perry-runtime/src/buffer/query.rs @@ -62,7 +62,8 @@ pub unsafe extern "C" fn js_value_buffer_or_typedarray_data( // Referenced only from the prebuilt `perry-ext-http` archive, so the // auto-optimize LTO pass would otherwise dead-strip it. Pin it. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_VALUE_BUFFER_OR_TYPEDARRAY_DATA: unsafe extern "C" fn(f64, *mut u32) -> *const u8 = js_value_buffer_or_typedarray_data; diff --git a/crates/perry-runtime/src/buffer/u8_codec.rs b/crates/perry-runtime/src/buffer/u8_codec.rs index 47e80652df..5d25bd9d7e 100644 --- a/crates/perry-runtime/src/buffer/u8_codec.rs +++ b/crates/perry-runtime/src/buffer/u8_codec.rs @@ -560,17 +560,23 @@ pub extern "C" fn js_u8_set_from_hex(addr: i64, str_handle: i64) -> f64 { // Keepalive anchors: these `#[no_mangle]` symbols are only referenced from // generated `.o` files, so the auto-optimize whole-program-LLVM bitcode -// rebuild would dead-strip them without an `#[cfg_attr(feature = "keepalive-anchors", used)]` reference. See +// rebuild would dead-strip them without an `#[used]` reference. See // project_auto_optimize_keepalive_3320. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_TO_BASE64: extern "C" fn(i64, f64) -> *mut StringHeader = js_u8_to_base64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_TO_HEX: extern "C" fn(i64) -> *mut StringHeader = js_u8_to_hex; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_FROM_BASE64: extern "C" fn(i64, f64) -> *mut BufferHeader = js_u8_from_base64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_FROM_HEX: extern "C" fn(i64) -> *mut BufferHeader = js_u8_from_hex; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_SET_FROM_BASE64: extern "C" fn(i64, i64, f64) -> f64 = js_u8_set_from_base64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_U8_SET_FROM_HEX: extern "C" fn(i64, i64) -> f64 = js_u8_set_from_hex; diff --git a/crates/perry-runtime/src/builtins/arithmetic.rs b/crates/perry-runtime/src/builtins/arithmetic.rs index aaf6103d76..1e773466c5 100644 --- a/crates/perry-runtime/src/builtins/arithmetic.rs +++ b/crates/perry-runtime/src/builtins/arithmetic.rs @@ -460,15 +460,19 @@ pub extern "C" fn js_rel_ge(x: f64, y: f64) -> f64 { // The `js_rel_*` helpers are reached only from Perry-emitted LLVM (the relational // fallthrough in codegen), so a bitcode/auto-optimize link can dead-strip them -// and leave `undefined _js_rel_lt …`. Pin them with `#[cfg_attr(feature = "keepalive-anchors", used)]` statics — same +// and leave `undefined _js_rel_lt …`. Pin them with `#[used]` statics — same // pattern as the write-barrier roots in `gc/barrier.rs`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REL_LT: extern "C" fn(f64, f64) -> f64 = js_rel_lt; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REL_GT: extern "C" fn(f64, f64) -> f64 = js_rel_gt; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REL_LE: extern "C" fn(f64, f64) -> f64 = js_rel_le; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REL_GE: extern "C" fn(f64, f64) -> f64 = js_rel_ge; #[no_mangle] diff --git a/crates/perry-runtime/src/builtins/formatting.rs b/crates/perry-runtime/src/builtins/formatting.rs index 50916b0bec..a4cbc6c576 100644 --- a/crates/perry-runtime/src/builtins/formatting.rs +++ b/crates/perry-runtime/src/builtins/formatting.rs @@ -210,11 +210,11 @@ pub(crate) fn format_bigint_literal(val: f64) -> String { } } -/// Per-thread override for the depth at which nested objects/arrays -/// collapse to `[Object]` / `[Array]`. Defaults to Node's `util.inspect` -/// default of 2. The `%o` format specifier raises this temporarily to -/// Node's object-format depth of 4, while `%O` uses the current inspect -/// options unchanged. +// Per-thread override for the depth at which nested objects/arrays +// collapse to `[Object]` / `[Array]`. Defaults to Node's `util.inspect` +// default of 2. The `%o` format specifier raises this temporarily to +// Node's object-format depth of 4, while `%O` uses the current inspect +// options unchanged. thread_local! { static INSPECT_DEPTH_LIMIT: std::cell::Cell = const { std::cell::Cell::new(2) }; } @@ -467,11 +467,11 @@ pub fn function_source_for_func_ptr(func_ptr: usize) -> String { format!("function {name}() {{ [native code] }}") } -/// Per-thread override for the `showHidden` inspect option. Defaults to -/// `false` (Node default): `util.inspect` / `console.log` only show -/// enumerable properties. `console.dir(value, { showHidden: true })` -/// flips this for the duration of the print so non-enumerable props -/// surface in `[bracketed]` form. See #1200. +// Per-thread override for the `showHidden` inspect option. Defaults to +// `false` (Node default): `util.inspect` / `console.log` only show +// enumerable properties. `console.dir(value, { showHidden: true })` +// flips this for the duration of the print so non-enumerable props +// surface in `[bracketed]` form. See #1200. thread_local! { static INSPECT_SHOW_HIDDEN: std::cell::Cell = const { std::cell::Cell::new(false) }; static INSPECT_SHOW_PROXY: std::cell::Cell = const { std::cell::Cell::new(false) }; @@ -517,12 +517,12 @@ impl Drop for InspectShowProxyGuard { } } -/// Per-thread override for the `customInspect` inspect option. Defaults to -/// `true` (Node default for `util.inspect` / `console.log`): when an object -/// has a `[util.inspect.custom]` symbol-keyed method, the hook is invoked -/// and its return value replaces the default object body. `console.dir` -/// flips this to `false` so the symbol surfaces as a property listing. -/// See #1201. +// Per-thread override for the `customInspect` inspect option. Defaults to +// `true` (Node default for `util.inspect` / `console.log`): when an object +// has a `[util.inspect.custom]` symbol-keyed method, the hook is invoked +// and its return value replaces the default object body. `console.dir` +// flips this to `false` so the symbol surfaces as a property listing. +// See #1201. thread_local! { static INSPECT_CUSTOM_INSPECT: std::cell::Cell = const { std::cell::Cell::new(true) }; } @@ -1531,7 +1531,7 @@ fn format_accessor_property(acc: crate::object::AccessorDescriptor, depth: usize let closure = (acc.get & crate::value::POINTER_MASK) as *const crate::closure::ClosureHeader; if !closure.is_null() { - let value = unsafe { crate::closure::js_closure_call0(closure) }; + let value = crate::closure::js_closure_call0(closure); return format!("[{}: {}]", label, format_jsvalue_for_json(value, depth + 1)); } } diff --git a/crates/perry-runtime/src/builtins/formatting/util_format.rs b/crates/perry-runtime/src/builtins/formatting/util_format.rs index eeb93b77f7..cb37ff5023 100644 --- a/crates/perry-runtime/src/builtins/formatting/util_format.rs +++ b/crates/perry-runtime/src/builtins/formatting/util_format.rs @@ -316,7 +316,7 @@ pub extern "C" fn js_util_format(arr_ptr: *const crate::array::ArrayHeader) -> f } } b'j' => { - unsafe { + { if util_format_json_arg_has_cycle(val) { out.push_str("[Circular]"); i += 2; diff --git a/crates/perry-runtime/src/builtins/globals.rs b/crates/perry-runtime/src/builtins/globals.rs index 5961cc498e..859bd87fe7 100644 --- a/crates/perry-runtime/src/builtins/globals.rs +++ b/crates/perry-runtime/src/builtins/globals.rs @@ -1174,7 +1174,7 @@ mod structured_clone_tests { /// `{}`-born objects with no inline capacity) must survive structuredClone. #[test] fn structured_clone_keeps_overflow_properties() { - unsafe { + { let src = crate::object::js_object_alloc(0, 0); let mut names = Vec::new(); for i in 0..50 { diff --git a/crates/perry-runtime/src/builtins/mod.rs b/crates/perry-runtime/src/builtins/mod.rs index 9123bfb8d6..bba2da3eb0 100644 --- a/crates/perry-runtime/src/builtins/mod.rs +++ b/crates/perry-runtime/src/builtins/mod.rs @@ -112,9 +112,6 @@ pub use numbers::{ js_to_integer_or_infinity, reject_symbol_to_string, }; -#[cfg(test)] -pub(crate) use numbers::parse_float_bytes; - pub use table::{js_console_table, js_console_table_with_properties}; #[cfg(test)] diff --git a/crates/perry-runtime/src/builtins/numbers.rs b/crates/perry-runtime/src/builtins/numbers.rs index 27fd792422..412f4cc49d 100644 --- a/crates/perry-runtime/src/builtins/numbers.rs +++ b/crates/perry-runtime/src/builtins/numbers.rs @@ -561,7 +561,7 @@ pub extern "C" fn js_number_coerce(value: f64) -> f64 { if crate::array::js_array_is_array(value).to_bits() == TAG_TRUE_BITS { let arr_ptr = jsval.as_pointer::(); let comma = crate::string::js_string_from_bytes(b",".as_ptr(), 1); - let joined = unsafe { crate::array::js_array_join(arr_ptr, comma) }; + let joined = crate::array::js_array_join(arr_ptr, comma); return js_number_coerce(crate::value::js_nanbox_string(joined as i64)); } // TypedArray → OrdinaryToPrimitive(number): a *patched own* diff --git a/crates/perry-runtime/src/bun_ffi/call.rs b/crates/perry-runtime/src/bun_ffi/call.rs index 46c150009d..c40a152730 100644 --- a/crates/perry-runtime/src/bun_ffi/call.rs +++ b/crates/perry-runtime/src/bun_ffi/call.rs @@ -600,7 +600,7 @@ mod tests { let image = image_from(&[1, 2, 3, 4, 5, 6, 7, 8], &[]); let r = unsafe { raw::call_int( - sum8_i32 as usize, + sum8_i32 as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -615,7 +615,7 @@ mod tests { let image = image_from(&[], &[0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5]); let r = unsafe { raw::call_f64( - dsum8 as usize, + dsum8 as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -633,7 +633,7 @@ mod tests { let image = image_from(&[10, 20, 30], &[2.0, 4.0, f_img]); let r = unsafe { raw::call_f64( - mixed as usize, + mixed as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -648,7 +648,7 @@ mod tests { let image = image_from(&[], &[f64::from_bits((21.0f32).to_bits() as u64)]); let r = unsafe { raw::call_f32( - f32_half as usize, + f32_half as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -663,7 +663,7 @@ mod tests { let image = image_from(&[u64::MAX as usize], &[]); let r = unsafe { raw::call_int( - u64_id as usize, + u64_id as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -678,7 +678,7 @@ mod tests { let image = image_from(&[1], &[]); let r = unsafe { raw::call_int( - bool_not as usize, + bool_not as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -691,7 +691,7 @@ mod tests { let image = image_from(&[5], &[]); let r = unsafe { raw::call_int( - i8_neg as usize, + i8_neg as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -716,7 +716,7 @@ mod tests { let image = image_from(&[100, 20, 3], &[]); let r = unsafe { raw::call_int( - add3 as usize, + add3 as *const () as usize, image.n_int, &image.ints, image.n_float, @@ -731,7 +731,7 @@ mod tests { let image = image_from(&[], &[]); let r = unsafe { raw::call_int( - noargs as usize, + noargs as *const () as usize, image.n_int, &image.ints, image.n_float, diff --git a/crates/perry-runtime/src/child_process/sync_run.rs b/crates/perry-runtime/src/child_process/sync_run.rs index 85587f7e1f..fe20771c72 100644 --- a/crates/perry-runtime/src/child_process/sync_run.rs +++ b/crates/perry-runtime/src/child_process/sync_run.rs @@ -12,7 +12,7 @@ use super::{ const CP_DEFAULT_MAX_BUFFER: usize = 1024 * 1024; /// Options that affect buffered sync execution after the `Command` is built. -pub(super) struct CpRunOptions { +pub(crate) struct CpRunOptions { input: Option>, timeout: Option, kill_signal: i32, @@ -183,7 +183,7 @@ impl CpRunError { } /// Outcome of running a child to completion (buffered). -pub(super) struct CpRun { +pub(crate) struct CpRun { pub(super) stdout: Vec, pub(super) stderr: Vec, pub(super) stdout_piped: bool, diff --git a/crates/perry-runtime/src/child_process/v8_serde.rs b/crates/perry-runtime/src/child_process/v8_serde.rs index f554e812fd..78ebcb52c3 100644 --- a/crates/perry-runtime/src/child_process/v8_serde.rs +++ b/crates/perry-runtime/src/child_process/v8_serde.rs @@ -277,16 +277,18 @@ impl Serializer { fn write_bigint(&mut self, value: f64) { let ptr = JSValue::from_bits(value.to_bits()).as_bigint_ptr(); - let negative = unsafe { crate::bigint::js_bigint_is_negative(ptr) } != 0; + let negative = crate::bigint::js_bigint_is_negative(ptr) != 0; // Read the magnitude as big-endian bytes (negate first if needed), then // reverse to the little-endian order V8's bigint digits use. let mag_ptr = if negative { - unsafe { crate::bigint::js_bigint_neg(ptr) as *const crate::bigint::BigIntHeader } + { + crate::bigint::js_bigint_neg(ptr) as *const crate::bigint::BigIntHeader + } } else { ptr }; let nbytes = crate::bigint::BIGINT_LIMBS * 8; - let be_buf = unsafe { crate::bigint::js_bigint_to_buffer(mag_ptr, nbytes as i32) }; + let be_buf = crate::bigint::js_bigint_to_buffer(mag_ptr, nbytes as i32); let mut le: Vec = if be_buf.is_null() { Vec::new() } else { diff --git a/crates/perry-runtime/src/child_process/validate.rs b/crates/perry-runtime/src/child_process/validate.rs index 4c21286860..eb39dd5aaf 100644 --- a/crates/perry-runtime/src/child_process/validate.rs +++ b/crates/perry-runtime/src/child_process/validate.rs @@ -94,8 +94,10 @@ pub extern "C" fn js_child_process_validate_args(value: f64) -> f64 { /// referenced only from generated `.o`, so the bitcode internalizer would /// otherwise drop them there; the classic link keeps them via the program's /// own undefined references and builds without the anchors. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CP_VALIDATE_COMMAND: unsafe extern "C" fn(f64, *const u8, u32) -> f64 = js_child_process_validate_command; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CP_VALIDATE_ARGS: extern "C" fn(f64) -> f64 = js_child_process_validate_args; diff --git a/crates/perry-runtime/src/closure/alloc.rs b/crates/perry-runtime/src/closure/alloc.rs index 6814d72b0f..591793a165 100644 --- a/crates/perry-runtime/src/closure/alloc.rs +++ b/crates/perry-runtime/src/closure/alloc.rs @@ -490,9 +490,11 @@ pub extern "C" fn js_closure_set_capture_ptr(closure: *mut ClosureHeader, index: js_closure_set_capture_bits(closure, index, value as u64); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLOSURE_GET_CAPTURE_BITS: extern "C" fn(*const ClosureHeader, u32) -> u64 = js_closure_get_capture_bits; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLOSURE_SET_CAPTURE_BITS: extern "C" fn(*mut ClosureHeader, u32, u64) = js_closure_set_capture_bits; diff --git a/crates/perry-runtime/src/closure/dispatch/bound.rs b/crates/perry-runtime/src/closure/dispatch/bound.rs index 4f32a5ed65..69664b7c2c 100644 --- a/crates/perry-runtime/src/closure/dispatch/bound.rs +++ b/crates/perry-runtime/src/closure/dispatch/bound.rs @@ -269,7 +269,7 @@ pub(crate) fn coerce_call_this(target: f64, this_arg: f64) -> f64 { return this_arg; } if std::ptr::eq(unsafe { (*closure).func_ptr }, BOUND_FUNCTION_FUNC_PTR) { - let inner = unsafe { js_closure_get_capture_f64(closure, 0) }; + let inner = js_closure_get_capture_f64(closure, 0); let ij = crate::value::JSValue::from_bits(inner.to_bits()); if !ij.is_pointer() { return this_arg; @@ -485,9 +485,10 @@ pub unsafe extern "C" fn js_function_bind( /// Keepalive anchor for the `js_function_bind` symbol. The auto-optimize /// whole-program LLVM rebuild dead-strips `#[no_mangle]` fns that are only -/// referenced from generated `.o` / other crates; this `#[cfg_attr(feature = "keepalive-anchors", used)]` static +/// referenced from generated `.o` / other crates; this `#[used]` static /// survives the bitcode pipeline. See project_auto_optimize_keepalive_3320. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_FUNCTION_BIND: unsafe extern "C" fn(f64, *const f64, usize) -> f64 = js_function_bind; diff --git a/crates/perry-runtime/src/closure/dynamic_props.rs b/crates/perry-runtime/src/closure/dynamic_props.rs index 20c9989832..7e01feb752 100644 --- a/crates/perry-runtime/src/closure/dynamic_props.rs +++ b/crates/perry-runtime/src/closure/dynamic_props.rs @@ -554,7 +554,7 @@ pub fn closure_get_dynamic_prop(ptr: usize, prop: &str) -> f64 { crate::object::js_implicit_this_set(prev); return result; } - unsafe { + { let key_hdr = crate::string::js_string_from_bytes(prop.as_ptr(), prop.len() as u32); let v = crate::object::js_object_get_field_by_name( proto_ptr as *const crate::object::ObjectHeader, @@ -589,7 +589,7 @@ pub fn closure_get_dynamic_prop(ptr: usize, prop: &str) -> f64 { } return f64::from_bits(crate::value::TAG_UNDEFINED); } - unsafe { + { let key_hdr = crate::string::js_string_from_bytes(prop.as_ptr(), prop.len() as u32); let v = crate::object::js_object_get_field_by_name( proto_ptr as *const crate::object::ObjectHeader, diff --git a/crates/perry-runtime/src/closure/registry.rs b/crates/perry-runtime/src/closure/registry.rs index d745d35a35..e9d74f9123 100644 --- a/crates/perry-runtime/src/closure/registry.rs +++ b/crates/perry-runtime/src/closure/registry.rs @@ -380,7 +380,8 @@ pub extern "C" fn js_register_closure_strict_function(func_ptr: *const u8) { /// Keepalive anchor for the auto-optimize whole-program build — the strict /// registration is emitted only from generated module-init code. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_REGISTER_CLOSURE_STRICT_FUNCTION: extern "C" fn(*const u8) = js_register_closure_strict_function; diff --git a/crates/perry-runtime/src/closure/unbox.rs b/crates/perry-runtime/src/closure/unbox.rs index ff20a75fa1..c365a0be1e 100644 --- a/crates/perry-runtime/src/closure/unbox.rs +++ b/crates/perry-runtime/src/closure/unbox.rs @@ -58,6 +58,7 @@ pub extern "C" fn js_closure_unbox_callee_checked_rebind(callee: f64, receiver: } /// Keepalive: generated code is the only caller (#6475). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLOSURE_UNBOX_CALLEE_CHECKED_REBIND: extern "C" fn(f64, f64) -> i64 = js_closure_unbox_callee_checked_rebind; diff --git a/crates/perry-runtime/src/collection_iter.rs b/crates/perry-runtime/src/collection_iter.rs index d31cc88430..96712925d1 100644 --- a/crates/perry-runtime/src/collection_iter.rs +++ b/crates/perry-runtime/src/collection_iter.rs @@ -16,19 +16,9 @@ //! `js_set_from_iterable`, `js_weakmap_init_iterable`, `js_weakset_init_iterable`) //! call into here so the throw-vs-empty-vs-consume decision lives in one place. -use crate::array::ArrayHeader; use crate::value::{js_jsvalue_to_string, js_nanbox_get_pointer, JSValue, TAG_NULL, TAG_UNDEFINED}; use std::os::raw::c_int; -/// Outcome of classifying a constructor init argument. -pub(crate) enum InitIter { - /// `null` / `undefined` — treat as empty init, no entries. - Empty, - /// An iterable; the yielded values have been materialized into this - /// (NaN-box-stripped) Array pointer. - Values(*mut ArrayHeader), -} - /// `typeof`-style word for a non-iterable value, used to build the Node /// " is not iterable" message. `null`/`undefined` are handled by the /// caller (they never throw), so they are not produced here. @@ -398,25 +388,3 @@ fn value_display(value: f64) -> String { String::from_utf8_lossy(std::slice::from_raw_parts(data, byte_len)).into_owned() } } - -/// Classify a collection-constructor init argument: -/// - `null`/`undefined` → [`InitIter::Empty`], -/// - any iterable → materialize the yielded values into an Array -/// ([`InitIter::Values`]), -/// - anything else → throw the Node "not iterable" `TypeError`. -/// -/// The returned Array pointer is NaN-box-stripped (a raw `*mut ArrayHeader`). -pub(crate) fn classify_init(value: f64) -> InitIter { - let bits = value.to_bits(); - if bits == TAG_UNDEFINED || bits == TAG_NULL { - return InitIter::Empty; - } - if !is_iterable(value) { - throw_not_iterable(value); - } - // `js_for_of_to_array` returns a NaN-boxed (POINTER_TAG) Array f64 whose - // elements are exactly what `for...of value` would yield. - let arr_f64 = crate::array::js_for_of_to_array(value); - let arr_ptr = js_nanbox_get_pointer(arr_f64) as *mut ArrayHeader; - InitIter::Values(arr_ptr) -} diff --git a/crates/perry-runtime/src/collection_iter_object.rs b/crates/perry-runtime/src/collection_iter_object.rs index fd1e658ac4..b8d1fb295d 100644 --- a/crates/perry-runtime/src/collection_iter_object.rs +++ b/crates/perry-runtime/src/collection_iter_object.rs @@ -138,17 +138,23 @@ pub extern "C" fn js_set_entries_iter_obj(set: *const SetHeader) -> i64 { // Rust callers. The whole-program auto-optimize bitcode link would // otherwise internalize + dead-strip the `#[no_mangle]` exports and break // the default compile path (see project_auto_optimize_keepalive). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_MAP_ENTRIES_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_entries_iter_obj; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_MAP_KEYS_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_keys_iter_obj; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_MAP_VALUES_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_values_iter_obj; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_VALUES_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_values_iter_obj; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_KEYS_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_keys_iter_obj; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_ENTRIES_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_entries_iter_obj; /// Build the `{ value, done }` iterator-result object. Mirrors diff --git a/crates/perry-runtime/src/date.rs b/crates/perry-runtime/src/date.rs index 747e2c6d28..81f9c11491 100644 --- a/crates/perry-runtime/src/date.rs +++ b/crates/perry-runtime/src/date.rs @@ -790,8 +790,9 @@ pub extern "C" fn js_date_utc(args_ptr: *const f64, argc: i32) -> f64 { /// Keepalive anchor for `js_date_utc` — codegen-only `#[no_mangle]` symbols /// get dead-stripped by the auto-optimize whole-program LLVM bitcode rebuild -/// without a `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +/// without a `#[used]` reference (see project_auto_optimize_keepalive_3320). +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DATE_UTC: extern "C" fn(*const f64, i32) -> f64 = js_date_utc; /// Coerce a NaN-boxed JS value to a number (ECMAScript ToNumber, restricted @@ -872,13 +873,6 @@ fn jsvalue_to_number(v: f64) -> f64 { } } -/// True if a NaN-boxed JS value is `undefined`. -#[inline] -fn jsvalue_is_undefined(v: f64) -> bool { - let bits = v.to_bits(); - ((bits >> 48) & 0xFFFF) == 0x7FFC && (bits & 0xFF) == 0x01 -} - /// `Date.prototype.set*` family with optional trailing arguments (#2851). /// /// `field` selects which component the *leading* argument sets: @@ -1008,7 +1002,8 @@ pub extern "C" fn js_date_apply_setter( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DATE_APPLY_SETTER: extern "C" fn(f64, i32, i32, *const f64, i32) -> f64 = js_date_apply_setter; diff --git a/crates/perry-runtime/src/date/parse.rs b/crates/perry-runtime/src/date/parse.rs index 1955def549..c87fc236d8 100644 --- a/crates/perry-runtime/src/date/parse.rs +++ b/crates/perry-runtime/src/date/parse.rs @@ -107,7 +107,6 @@ fn parse_iso8601(s: &str) -> Option { let mut minute: i64 = 0; let mut second: i64 = 0; let mut millis: i64 = 0; - let mut idx = year_end; // Year only ("YYYY" / "±YYYYYY"). if s.len() == year_end { @@ -132,7 +131,7 @@ fn parse_iso8601(s: &str) -> Option { if !(1..=12).contains(&month1) { return None; } - idx = year_end + 3; + let mut idx = year_end + 3; let mut has_day = false; if b.get(idx) == Some(&b'-') { if b.len() < idx + 3 { diff --git a/crates/perry-runtime/src/dgram.rs b/crates/perry-runtime/src/dgram.rs index 9e0c4db63c..685087bd08 100644 --- a/crates/perry-runtime/src/dgram.rs +++ b/crates/perry-runtime/src/dgram.rs @@ -10,7 +10,6 @@ //! touching the network. use std::collections::HashMap; -use std::net::ToSocketAddrs; use std::sync::{LazyLock, Mutex}; use crate::array::ArrayHeader; diff --git a/crates/perry-runtime/src/dgram/net.rs b/crates/perry-runtime/src/dgram/net.rs index 6bfb6672b5..d03fc40467 100644 --- a/crates/perry-runtime/src/dgram/net.rs +++ b/crates/perry-runtime/src/dgram/net.rs @@ -11,7 +11,6 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs, UdpSocket} use std::sync::Arc; use crate::object::{js_object_alloc, js_object_set_field_by_name}; -use crate::value::JSValue; pub(crate) fn allocate_port(registry: &mut DgramRegistry, address: &str) -> u16 { for _ in 0..16384 { @@ -127,33 +126,6 @@ pub(crate) fn build_rinfo(address: &str, family: &str, port: u16, size: usize) - boxed_pointer(obj as *const u8) } -pub(crate) fn message_value(value: f64) -> Option<(f64, usize)> { - let jsval = JSValue::from_bits(value.to_bits()); - if jsval.is_any_string() { - let ptr = crate::value::js_get_string_pointer_unified(value) as *const crate::StringHeader; - if ptr.is_null() { - return None; - } - let buf = crate::buffer::js_buffer_from_string(ptr, 0); - let len = unsafe { (*buf).length as usize }; - return Some((boxed_pointer(buf as *const u8), len)); - } - let raw = raw_ptr_from_value(value); - if raw >= 0x10000 && crate::buffer::is_registered_buffer(raw) { - let buf = raw as *const crate::buffer::BufferHeader; - return Some((value, unsafe { (*buf).length as usize })); - } - if raw >= 0x10000 && crate::typedarray::lookup_typed_array_kind(raw).is_some() { - let len = unsafe { - crate::typedarray::typed_array_bytes(raw as *const crate::typedarray::TypedArrayHeader) - .map(|bytes| bytes.len()) - .unwrap_or(0) - }; - return Some((value, len)); - } - None -} - /// Whether `PERRY_DETERMINISTIC_NET=1` — use the in-process loopback registry /// instead of real OS sockets (#4911). pub(crate) fn deterministic() -> bool { diff --git a/crates/perry-runtime/src/disposable.rs b/crates/perry-runtime/src/disposable.rs index e7907dc3a6..effbfb4e72 100644 --- a/crates/perry-runtime/src/disposable.rs +++ b/crates/perry-runtime/src/disposable.rs @@ -498,44 +498,57 @@ pub extern "C" fn js_suppressed_error_new(error: f64, suppressed: f64, message: // Keepalive anchors — these `#[no_mangle]` fns are only ever called from // generated code (the codegen `new` arm + the native-module dispatch table), // so the whole-program-LLVM auto-optimize bitcode rebuild would otherwise -// dead-strip them (see project_auto_optimize_keepalive_3320). `#[cfg_attr(feature = "keepalive-anchors", used)]` +// dead-strip them (see project_auto_optimize_keepalive_3320). `#[used]` // survives the bitcode pipeline. // --------------------------------------------------------------------------- -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_NEW: extern "C" fn() -> *mut ObjectHeader = js_disposable_stack_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ASYNC_DISPOSABLE_STACK_NEW: extern "C" fn() -> *mut ObjectHeader = js_async_disposable_stack_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_DISPOSED: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_disposed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_DEFER: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_disposable_stack_defer; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_USE: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_disposable_stack_use; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ASYNC_DISPOSABLE_STACK_USE: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_async_disposable_stack_use; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_ADOPT: extern "C" fn(*mut ObjectHeader, f64, f64) -> f64 = js_disposable_stack_adopt; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_dispose; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_SYMBOL_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_symbol_dispose; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DISPOSABLE_STACK_MOVE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_move; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ASYNC_DISPOSABLE_STACK_DISPOSE_ASYNC: extern "C" fn(*mut ObjectHeader) -> f64 = js_async_disposable_stack_dispose_async; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ASYNC_DISPOSABLE_STACK_SYMBOL_ASYNC_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_async_disposable_stack_symbol_async_dispose; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SUPPRESSED_ERROR_NEW: extern "C" fn(f64, f64, f64) -> f64 = js_suppressed_error_new; diff --git a/crates/perry-runtime/src/dns.rs b/crates/perry-runtime/src/dns.rs index 0d3b6a78db..5453e9f7d5 100644 --- a/crates/perry-runtime/src/dns.rs +++ b/crates/perry-runtime/src/dns.rs @@ -58,7 +58,7 @@ const RESOLVER_RESOLVE_METHODS: &[&str] = &[ const RESOLVER_SERVERS_FIELD: &str = "__dns_servers"; #[derive(Clone, Copy)] -enum RecordKind { +pub(crate) enum RecordKind { A, Aaaa, Any, diff --git a/crates/perry-runtime/src/embedded.rs b/crates/perry-runtime/src/embedded.rs index 4493904869..7dcc6dad38 100644 --- a/crates/perry-runtime/src/embedded.rs +++ b/crates/perry-runtime/src/embedded.rs @@ -265,16 +265,20 @@ pub extern "C" fn js_perry_read_embedded(path_value: f64) -> *mut crate::buffer: // Keep the FFI symbols external under the thin-LTO + `strip=true` release // profile. A `#[no_mangle] pub extern "C"` alone is internalized and -// dead-stripped; only individual `#[cfg_attr(feature = "keepalive-anchors", used)]` typed fn-pointer statics survive +// dead-stripped; only individual `#[used]` typed fn-pointer statics survive // (see the note in `typed_feedback/trace.rs`). `js_register_embedded_asset` is // called only from the generated C constructor, and `js_perry_read_embedded` // only from codegen-emitted callsites — both are invisible to Rust's reachability. #[rustfmt::skip] +#[cfg(feature = "keepalive-anchors")] mod keep_embedded { use super::*; - #[cfg_attr(feature = "keepalive-anchors", used)] static K0: unsafe extern "C" fn(*const u8, usize, *const u8, usize) = js_register_embedded_asset; - #[cfg_attr(feature = "keepalive-anchors", used)] static K1: extern "C" fn(f64) -> *mut crate::buffer::BufferHeader = js_perry_read_embedded; - #[cfg_attr(feature = "keepalive-anchors", used)] static K2: extern "C" fn() -> *mut crate::array::ArrayHeader = js_perry_embedded_files; + #[cfg(feature = "keepalive-anchors")] +#[used] static K0: unsafe extern "C" fn(*const u8, usize, *const u8, usize) = js_register_embedded_asset; + #[cfg(feature = "keepalive-anchors")] +#[used] static K1: extern "C" fn(f64) -> *mut crate::buffer::BufferHeader = js_perry_read_embedded; + #[cfg(feature = "keepalive-anchors")] +#[used] static K2: extern "C" fn() -> *mut crate::array::ArrayHeader = js_perry_embedded_files; } #[cfg(test)] diff --git a/crates/perry-runtime/src/error.rs b/crates/perry-runtime/src/error.rs index 9e74c2541f..923a9edb09 100644 --- a/crates/perry-runtime/src/error.rs +++ b/crates/perry-runtime/src/error.rs @@ -121,7 +121,8 @@ pub unsafe extern "C" fn js_set_call_location(file_ptr: *const u8, file_len: usi // Generated-code-only callee: anchor against the auto-optimize LTO dead-strip // (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_CALL_LOCATION: unsafe extern "C" fn(*const u8, usize, u32) = js_set_call_location; @@ -436,8 +437,9 @@ pub unsafe extern "C" fn js_node_system_error_value( // These FFI entries are referenced only from extension archives (linked after // the runtime's bitcode is optimized), so the auto-optimize LTO pass would // otherwise dead-strip them (see project_auto_optimize_keepalive_3320). The -// `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors pin them. -#[cfg_attr(feature = "keepalive-anchors", used)] +// `#[used]` anchors pin them. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ERROR_VALUE_WITH_CODE: unsafe extern "C" fn( *const u8, usize, @@ -446,7 +448,8 @@ static KEEP_JS_ERROR_VALUE_WITH_CODE: unsafe extern "C" fn( i32, ) -> f64 = js_error_value_with_code; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_NODE_SYSTEM_ERROR_VALUE: unsafe extern "C" fn( *const u8, usize, @@ -457,7 +460,8 @@ static KEEP_JS_NODE_SYSTEM_ERROR_VALUE: unsafe extern "C" fn( f64, ) -> f64 = js_node_system_error_value; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_THROW_ERROR_WITH_CODE: unsafe extern "C" fn( *const u8, usize, @@ -855,7 +859,8 @@ pub extern "C" fn js_throw_eval_syntax_error(message: f64) -> f64 { } // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_THROW_EVAL_SYNTAX_ERROR: extern "C" fn(f64) -> f64 = js_throw_eval_syntax_error; #[no_mangle] @@ -867,7 +872,8 @@ pub extern "C" fn js_throw_restricted_function_property_assignment() -> f64 { } // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_THROW_RESTRICTED_FN_PROP_ASSIGN: extern "C" fn() -> f64 = js_throw_restricted_function_property_assignment; @@ -955,7 +961,8 @@ pub extern "C" fn js_throw_reference_error_tdz(name: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code- /// and runtime-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_THROW_REFERENCE_ERROR_TDZ: extern "C" fn(f64) -> f64 = js_throw_reference_error_tdz; #[no_mangle] @@ -965,7 +972,8 @@ pub extern "C" fn js_throw_reference_error_unresolved_get() -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_GLOBAL_GET_OR_THROW_UNRESOLVED: extern "C" fn(f64) -> f64 = js_global_get_or_throw_unresolved; @@ -989,7 +997,7 @@ pub extern "C" fn js_global_get_or_throw_unresolved(name_value: f64) -> f64 { let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *const crate::object::ObjectHeader; if !gptr.is_null() && !key.is_null() { - let v = unsafe { crate::object::js_object_get_field_by_name(gptr, key) }; + let v = crate::object::js_object_get_field_by_name(gptr, key); if !v.is_undefined() { return f64::from_bits(v.bits()); } @@ -1018,10 +1026,12 @@ pub extern "C" fn js_global_get_or_throw_unresolved(name_value: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_GLOBAL_GET_OPTIONAL: extern "C" fn(f64) -> f64 = js_global_get_optional; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_GLOBAL_UPDATE: extern "C" fn(f64, f64, f64) -> f64 = js_global_update; /// `++x` / `x++` / `--x` / `x--` where `x` resolves to no lexical binding — @@ -1054,21 +1064,17 @@ pub extern "C" fn js_global_update(name_value: f64, is_increment: f64, is_prefix let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *const crate::object::ObjectHeader; if !gptr.is_null() { - let v = unsafe { - crate::object::js_object_get_field_by_name( - gptr, - key_handle.get_raw_const_ptr::(), - ) - }; + let v = crate::object::js_object_get_field_by_name( + gptr, + key_handle.get_raw_const_ptr::(), + ); if !v.is_undefined() - || unsafe { - crate::object::js_object_has_own( - f64::from_bits(g_handle.get_heap_word_u64()), - name_value, - ) - .to_bits() - == crate::value::TAG_TRUE - } + || crate::object::js_object_has_own( + f64::from_bits(g_handle.get_heap_word_u64()), + name_value, + ) + .to_bits() + == crate::value::TAG_TRUE { present = true; } @@ -1084,7 +1090,7 @@ pub extern "C" fn js_global_update(name_value: f64, is_increment: f64, is_prefix let msg = format!("{} is not defined", name); let msg_str = js_string_from_bytes(msg.as_ptr(), msg.len() as u32); let err_ptr = js_referenceerror_new(msg_str); - return crate::exception::js_throw(crate::value::js_nanbox_pointer(err_ptr as i64)); + crate::exception::js_throw(crate::value::js_nanbox_pointer(err_ptr as i64)); } let old_handle = scope.root_nanbox_f64(old); let numeric = unsafe { crate::value::js_to_numeric(old_handle.get_nanbox_f64()) }; @@ -1094,13 +1100,11 @@ pub extern "C" fn js_global_update(name_value: f64, is_increment: f64, is_prefix let stepped_handle = scope.root_nanbox_f64(stepped); let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *mut crate::object::ObjectHeader; - unsafe { - crate::object::js_object_set_field_by_name( - gptr, - key_handle.get_raw_const_ptr::(), - stepped_handle.get_nanbox_f64(), - ) - }; + crate::object::js_object_set_field_by_name( + gptr, + key_handle.get_raw_const_ptr::(), + stepped_handle.get_nanbox_f64(), + ); let numeric = numeric_handle.get_nanbox_f64(); let stepped = stepped_handle.get_nanbox_f64(); if is_prefix { @@ -1112,7 +1116,8 @@ pub extern "C" fn js_global_update(name_value: f64, is_increment: f64, is_prefix /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_GLOBAL_ASSIGN_EXISTING_OR_THROW: extern "C" fn(f64, f64) -> f64 = js_global_assign_existing_or_throw; @@ -1150,21 +1155,17 @@ pub extern "C" fn js_global_assign_existing_or_throw(name_value: f64, value: f64 let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *const crate::object::ObjectHeader; if !gptr.is_null() { - let v = unsafe { - crate::object::js_object_get_field_by_name( - gptr, - key_handle.get_raw_const_ptr::(), - ) - }; + let v = crate::object::js_object_get_field_by_name( + gptr, + key_handle.get_raw_const_ptr::(), + ); if !v.is_undefined() - || unsafe { - crate::object::js_object_has_own( - f64::from_bits(g_handle.get_heap_word_u64()), - name_value, - ) - .to_bits() - == crate::value::TAG_TRUE - } + || crate::object::js_object_has_own( + f64::from_bits(g_handle.get_heap_word_u64()), + name_value, + ) + .to_bits() + == crate::value::TAG_TRUE { present = true; } @@ -1175,7 +1176,7 @@ pub extern "C" fn js_global_assign_existing_or_throw(name_value: f64, value: f64 let msg = format!("{} is not defined", name); let msg_str = js_string_from_bytes(msg.as_ptr(), msg.len() as u32); let err_ptr = js_referenceerror_new(msg_str); - return crate::exception::js_throw(crate::value::js_nanbox_pointer(err_ptr as i64)); + crate::exception::js_throw(crate::value::js_nanbox_pointer(err_ptr as i64)); } let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *mut crate::object::ObjectHeader; @@ -1206,7 +1207,7 @@ pub extern "C" fn js_global_get_optional(name_value: f64) -> f64 { let g = f64::from_bits(g_handle.get_heap_word_u64()); let gptr = (g.to_bits() & crate::value::POINTER_MASK) as *const crate::object::ObjectHeader; if !gptr.is_null() && !key.is_null() { - let v = unsafe { crate::object::js_object_get_field_by_name(gptr, key) }; + let v = crate::object::js_object_get_field_by_name(gptr, key); return f64::from_bits(v.bits()); } } @@ -1744,22 +1745,25 @@ pub(crate) fn throw_immutable_write(kind: u32, key: &str) -> ! { // #2836/#2838/#2904: keep the codegen-emitted error FFIs alive through the // auto-optimize whole-program-bitcode link. These `#[no_mangle]` fns are -// reachable only from generated `.o`; without `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors the +// reachable only from generated `.o`; without `#[used]` anchors the // internalize+dead-strip pass drops them and the default `perry file.ts -o` // link fails (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ERROR_NEW_KIND_WITH_OPTIONS: extern "C" fn( u32, *mut StringHeader, f64, ) -> *mut ErrorHeader = js_error_new_kind_with_options; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_AGGREGATEERROR_NEW_FULL: extern "C" fn( f64, *mut StringHeader, f64, ) -> *mut ErrorHeader = js_aggregateerror_new_full; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ERROR_IS_ERROR: extern "C" fn(f64) -> f64 = js_error_is_error; #[cfg(test)] diff --git a/crates/perry-runtime/src/event_target.rs b/crates/perry-runtime/src/event_target.rs index 947a1d5a18..e31860cf8b 100644 --- a/crates/perry-runtime/src/event_target.rs +++ b/crates/perry-runtime/src/event_target.rs @@ -326,7 +326,8 @@ pub extern "C" fn js_event_subclass_init( /// Keepalive anchor for the auto-optimize whole-program build — /// `js_event_subclass_init` is a generated-code-only callee. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_EVENT_SUBCLASS_INIT: extern "C" fn(f64, f64, f64, u32, u32) -> f64 = js_event_subclass_init; diff --git a/crates/perry-runtime/src/fs/cp.rs b/crates/perry-runtime/src/fs/cp.rs index f25b736a80..633a3bc5ed 100644 --- a/crates/perry-runtime/src/fs/cp.rs +++ b/crates/perry-runtime/src/fs/cp.rs @@ -432,13 +432,6 @@ pub extern "C" fn js_fs_cp_sync_options(from_value: f64, to_value: f64, options_ } } -pub(crate) fn js_fs_cp_async_options(from_value: f64, to_value: f64, options_value: f64) -> i32 { - match js_fs_cp_async_result(from_value, to_value, options_value) { - Ok(()) => 1, - Err(err) => crate::exception::js_throw(err), - } -} - pub(crate) fn js_fs_cp_async_result( from_value: f64, to_value: f64, diff --git a/crates/perry-runtime/src/fs/dir_glob_watch/glob.rs b/crates/perry-runtime/src/fs/dir_glob_watch/glob.rs index 16e1eadc4d..3051db49ff 100644 --- a/crates/perry-runtime/src/fs/dir_glob_watch/glob.rs +++ b/crates/perry-runtime/src/fs/dir_glob_watch/glob.rs @@ -7,12 +7,16 @@ use super::string_value; // directly here (we are a grandchild of `fs`); the two private-to-`fs/mod.rs` // helpers are named explicitly so a glob that skips privates can't drop them. +#[cfg(feature = "regex-engine")] use std::collections::BTreeMap; +#[cfg(feature = "regex-engine")] use std::fs; +#[cfg(feature = "regex-engine")] use std::path::Path; #[cfg(feature = "regex-engine")] use std::path::PathBuf; +#[cfg(feature = "regex-engine")] use crate::closure::ClosureHeader; /// Compiled exclude-pattern type for `fs.glob`. Backed by `fancy_regex::Regex`. diff --git a/crates/perry-runtime/src/fs/fd_ops.rs b/crates/perry-runtime/src/fs/fd_ops.rs index 74b90b55cc..5c30b4de8d 100644 --- a/crates/perry-runtime/src/fs/fd_ops.rs +++ b/crates/perry-runtime/src/fs/fd_ops.rs @@ -471,8 +471,8 @@ pub extern "C" fn js_fs_readv_sync(fd_value: f64, buffers_value: f64, position_v // common-case `readv(123, [buf])` → `EBADF` parity and leave the // empty-array divergence as a follow-up. let buffers_for_check = array_ptr_from_value(buffers_value); - let buffers_nonempty = !buffers_for_check.is_null() - && unsafe { crate::array::js_array_length(buffers_for_check) } > 0; + let buffers_nonempty = + !buffers_for_check.is_null() && crate::array::js_array_length(buffers_for_check) > 0; if buffers_nonempty { crate::fs::validate::validate_fd_open(fd_value, "read"); } else { @@ -551,8 +551,8 @@ pub extern "C" fn js_fs_writev_sync(fd_value: f64, buffers_value: f64, position_ // own writev returns 0 without touching the fd), validate only when // there's something to write. let buffers_for_check = array_ptr_from_value(buffers_value); - let buffers_nonempty = !buffers_for_check.is_null() - && unsafe { crate::array::js_array_length(buffers_for_check) } > 0; + let buffers_nonempty = + !buffers_for_check.is_null() && crate::array::js_array_length(buffers_for_check) > 0; if buffers_nonempty { // #2013: upgrade from type-only validation to type + EBADF so // `fs.writevSync(123, [buf])` matches Node's diff --git a/crates/perry-runtime/src/fs/fd_sync_ops.rs b/crates/perry-runtime/src/fs/fd_sync_ops.rs index 437fa08536..48f9dd2416 100644 --- a/crates/perry-runtime/src/fs/fd_sync_ops.rs +++ b/crates/perry-runtime/src/fs/fd_sync_ops.rs @@ -1,9 +1,8 @@ use super::*; use std::fs; -use std::io::Write; #[cfg(unix)] -use std::os::unix::fs::{MetadataExt, PermissionsExt}; +use std::os::unix::fs::PermissionsExt; #[cfg(unix)] use std::os::unix::io::AsRawFd; diff --git a/crates/perry-runtime/src/fs/mod.rs b/crates/perry-runtime/src/fs/mod.rs index 13be7bd167..f4a4e3f990 100644 --- a/crates/perry-runtime/src/fs/mod.rs +++ b/crates/perry-runtime/src/fs/mod.rs @@ -8,9 +8,7 @@ use std::collections::HashMap as StdHashMap; use std::fs; use std::io::{Read, Seek, SeekFrom, Write}; #[cfg(unix)] -use std::os::unix::fs::{MetadataExt, PermissionsExt}; -#[cfg(unix)] -use std::os::unix::io::AsRawFd; +use std::os::unix::fs::PermissionsExt; use std::path::Path; use std::sync::atomic::{AtomicI32, Ordering}; @@ -1742,7 +1740,7 @@ pub extern "C" fn js_fs_readlink_sync_options(path_value: f64, options_value: f6 let enc = fs_encoding_option(options_value).unwrap_or_else(|| "utf8".to_string()); encoded_string_ptr(&bytes, &enc) as i64 } - Err(err_val) => unsafe { crate::exception::js_throw(err_val) }, + Err(err_val) => crate::exception::js_throw(err_val), } } @@ -1751,7 +1749,7 @@ pub extern "C" fn js_fs_readlink_dispatch(path_value: f64, options_value: f64) - validate::validate_path("path", path_value); match readlink_value_result(path_value, options_value) { Ok(v) => v, - Err(err_val) => unsafe { crate::exception::js_throw(err_val) }, + Err(err_val) => crate::exception::js_throw(err_val), } } diff --git a/crates/perry-runtime/src/fs/validate.rs b/crates/perry-runtime/src/fs/validate.rs index 3013513501..02a73ecccf 100644 --- a/crates/perry-runtime/src/fs/validate.rs +++ b/crates/perry-runtime/src/fs/validate.rs @@ -369,12 +369,13 @@ pub unsafe extern "C" fn js_validate_event_listener( throw_type_error_with_code(&message, "ERR_INVALID_ARG_TYPE"); } -/// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive so the auto-optimize whole-program-LLVM rebuild does +/// `#[used]` keepalive so the auto-optimize whole-program-LLVM rebuild does /// not dead-strip this codegen-invoked `#[no_mangle]` entry point (see /// project_auto_optimize_keepalive_3320). Called only from generated `.o` /// via the stdlib/ext events validators, so without an anchor the bitcode /// internalizer drops it and the default `perry file.ts -o out` link fails. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_VALIDATE_EVENT_LISTENER: unsafe extern "C" fn(i64, *const u8, u32) -> i64 = js_validate_event_listener; @@ -627,18 +628,6 @@ pub(crate) fn validate_fs_mode(value: f64) { } } -pub(crate) fn fs_mode_value(value: f64) -> i32 { - validate_fs_mode(value); - let jv = JSValue::from_bits(value.to_bits()); - if is_nullish(jv) { - 0 - } else if jv.is_int32() { - jv.as_int32() - } else { - jv.as_number() as i32 - } -} - /// Validate that `value` is a finite integer in `[min, max]`. On type or /// range failure throws Node's `ERR_INVALID_ARG_TYPE` / `ERR_OUT_OF_RANGE` /// with the same `Received` clause shape Node uses. diff --git a/crates/perry-runtime/src/gc/barrier.rs b/crates/perry-runtime/src/gc/barrier.rs index a4d93f6d89..22f785c399 100644 --- a/crates/perry-runtime/src/gc/barrier.rs +++ b/crates/perry-runtime/src/gc/barrier.rs @@ -99,6 +99,8 @@ impl DirtyHeaderSlotScan { } } GcMutableSlotDescriptor::Range { range, layout_kind } => { + // Preserve single-slot scan semantics for every dirty range + // entry: weak-target skip, layout tracking, accounting, visit. for (start, end) in dirty_slot_ranges_for(range, dirty_pages, stats) { work.push(DirtySlotWork::Range(DirtySlotRangeWork { slots: range.slots(), @@ -490,134 +492,6 @@ pub(super) unsafe fn scan_dirty_slot_with_layout( visit_slot(slot, stats); } -pub(super) unsafe fn scan_dirty_slot_range( - slots: *mut u64, - slot_count: usize, - dirty_pages: &crate::fast_hash::PtrHashSet, - stats: &mut RememberedSetTraceStats, - visit_slot: &mut dyn FnMut(*mut u64, &mut RememberedSetTraceStats), -) { - if slots.is_null() || slot_count == 0 || dirty_pages.is_empty() { - return; - } - const PAGE_SHIFT: usize = 12; - const PAGE_SIZE: usize = 1 << PAGE_SHIFT; - - let slots_start = slots as usize; - let Some(slots_bytes) = slot_count.checked_mul(std::mem::size_of::()) else { - return; - }; - let Some(slots_end) = slots_start.checked_add(slots_bytes) else { - return; - }; - let mut ranges = Vec::<(usize, usize)>::new(); - - for &page in dirty_pages { - let page_start = page << PAGE_SHIFT; - let page_end = page_start + PAGE_SIZE; - if page_end <= slots_start || page_start >= slots_end { - continue; - } - stats.dirty_slot_pages_considered += 1; - let start_addr = page_start.max(slots_start); - let end_addr = page_end.min(slots_end); - let start_idx = (start_addr - slots_start).div_ceil(8); - let end_idx = (end_addr - slots_start).div_ceil(8); - if start_idx < end_idx && start_idx < slot_count { - ranges.push((start_idx, end_idx.min(slot_count))); - } - } - - if ranges.is_empty() { - return; - } - ranges.sort_unstable(); - let mut merged = Vec::<(usize, usize)>::with_capacity(ranges.len()); - for (start, end) in ranges { - if let Some((_, last_end)) = merged.last_mut() { - if start <= *last_end { - *last_end = (*last_end).max(end); - continue; - } - } - merged.push((start, end)); - } - - for (start, end) in merged { - stats.dirty_slot_ranges_scanned += 1; - for i in start..end { - stats.dirty_slots_scanned += 1; - let slot = slots.add(i); - crate::arena::old_page_account_dirty_slot(slot as usize); - visit_slot(slot, stats); - } - } -} - -pub(super) unsafe fn scan_dirty_slot_range_with_layout( - range: HeapSlotRange, - layout_kind: HeapChildSlotReadKind, - dirty_pages: &crate::fast_hash::PtrHashSet, - stats: &mut RememberedSetTraceStats, - visit_slot: &mut dyn FnMut(*mut u64, &mut RememberedSetTraceStats), -) { - if range.slots().is_null() || range.slot_count() == 0 || dirty_pages.is_empty() { - return; - } - const PAGE_SHIFT: usize = 12; - const PAGE_SIZE: usize = 1 << PAGE_SHIFT; - - let slots = range.slots(); - let slot_count = range.slot_count(); - let slots_start = slots as usize; - let Some(slots_bytes) = slot_count.checked_mul(std::mem::size_of::()) else { - return; - }; - let Some(slots_end) = slots_start.checked_add(slots_bytes) else { - return; - }; - let mut ranges = Vec::<(usize, usize)>::new(); - for &page in dirty_pages { - let page_start = page << PAGE_SHIFT; - let page_end = page_start + PAGE_SIZE; - let start = slots_start.max(page_start); - let end = slots_end.min(page_end); - if start >= end { - continue; - } - stats.dirty_slot_pages_considered += 1; - let first = (start - slots_start) / std::mem::size_of::(); - let last = (end - slots_start).div_ceil(std::mem::size_of::()); - ranges.push((first.min(slot_count), last.min(slot_count))); - } - - if ranges.is_empty() { - return; - } - ranges.sort_unstable(); - let mut merged = Vec::<(usize, usize)>::with_capacity(ranges.len()); - for (start, end) in ranges { - if let Some((_, last_end)) = merged.last_mut() { - if start <= *last_end { - *last_end = (*last_end).max(end); - continue; - } - } - merged.push((start, end)); - } - - for (start, end) in merged { - stats.dirty_slot_ranges_scanned += 1; - for i in start..end { - stats.dirty_slots_scanned += 1; - let slot = slots.add(i); - record_layout_child_slot_read(layout_kind); - crate::arena::old_page_account_dirty_slot(slot as usize); - visit_slot(slot, stats); - } - } -} - pub(super) unsafe fn scan_dirty_object_slots( header: *mut GcHeader, dirty_pages: &crate::fast_hash::PtrHashSet, @@ -1537,11 +1411,13 @@ pub extern "C" fn js_write_barrier_root_nanbox(value_bits: u64) { // the runtime through whole-program LLVM bitcode and is free to internalize and // dead-strip an unreferenced `#[no_mangle]` symbol — which broke the default // `perry file.ts -o out` link with `undefined _js_write_barrier_root_*`. The -// `#[cfg_attr(feature = "keepalive-anchors", used)]` statics pin retained reference edges so both survive every link mode. +// `#[used]` statics pin retained reference edges so both survive every link mode. // Same pattern as `node_stream_keepalive.rs` / `typedarray.rs`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_WRITE_BARRIER_ROOT_HEAP_WORD: extern "C" fn(u64) = js_write_barrier_root_heap_word; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_WRITE_BARRIER_ROOT_NANBOX: extern "C" fn(u64) = js_write_barrier_root_nanbox; #[inline] diff --git a/crates/perry-runtime/src/gc/cycle.rs b/crates/perry-runtime/src/gc/cycle.rs index 043a4ce62b..e2bfc8c762 100644 --- a/crates/perry-runtime/src/gc/cycle.rs +++ b/crates/perry-runtime/src/gc/cycle.rs @@ -856,6 +856,8 @@ impl AtomicFinalizeCycleState { pub(super) struct GcCycleState { collection_kind: GcCollectionKind, + #[allow(dead_code)] + // captured trigger classification retained alongside collection_kind/progress_kind for cycle diagnostics trigger_kind: GcTriggerKind, progress_kind: GcProgressKind, phase: GcCyclePhase, diff --git a/crates/perry-runtime/src/gc/heap_snapshot.rs b/crates/perry-runtime/src/gc/heap_snapshot.rs index baf3b3767f..11448c2cf5 100644 --- a/crates/perry-runtime/src/gc/heap_snapshot.rs +++ b/crates/perry-runtime/src/gc/heap_snapshot.rs @@ -505,7 +505,7 @@ mod tests { #[test] fn snapshot_has_real_nodes_and_edges() { // Allocate a recognizable object graph, then snapshot. - unsafe { + { let marker = b"__heap_snapshot_test_marker__"; let key = crate::string::js_string_from_bytes(marker.as_ptr(), marker.len() as u32); let obj = crate::object::js_object_alloc(0, 1); diff --git a/crates/perry-runtime/src/gc/mod.rs b/crates/perry-runtime/src/gc/mod.rs index 3e813a3840..6c98904171 100644 --- a/crates/perry-runtime/src/gc/mod.rs +++ b/crates/perry-runtime/src/gc/mod.rs @@ -41,7 +41,7 @@ pub use policy::*; mod progress; pub use progress::*; mod heap_budget; -pub use heap_budget::*; +pub(crate) use heap_budget::*; mod pressure; pub use pressure::*; mod telemetry; @@ -367,6 +367,7 @@ thread_local! { /// Suppress (or re-enable) lazy `ensure_gc_initialized` on this thread, returning /// the previous value. Used by the GC tests' `ScopedRootScannerRegistryGuard` to /// run collections against a hand-controlled root set. +#[allow(dead_code)] // test scaffolding: used only by ScopedRootScannerRegistryGuard under cfg(test) pub(crate) fn set_auto_gc_init_suppressed(suppressed: bool) -> bool { AUTO_GC_INIT_SUPPRESSED.with(|c| c.replace(suppressed)) } diff --git a/crates/perry-runtime/src/gc/oldgen.rs b/crates/perry-runtime/src/gc/oldgen.rs index 28f9b4dbfd..b145941258 100644 --- a/crates/perry-runtime/src/gc/oldgen.rs +++ b/crates/perry-runtime/src/gc/oldgen.rs @@ -197,9 +197,9 @@ pub(super) fn select_old_page_defrag_pages_from_snapshot( selection } -/// gh #6206 test hook: the defrag machinery's unit tests exercise the -/// selection/copy/re-remember mechanics directly and must bypass the -/// production off-gate below. Thread-local so parallel tests don't race. +// gh #6206 test hook: the defrag machinery's unit tests exercise the +// selection/copy/re-remember mechanics directly and must bypass the +// production off-gate below. Thread-local so parallel tests don't race. #[cfg(test)] thread_local! { pub(crate) static OLD_DEFRAG_TEST_OVERRIDE: std::cell::Cell> = diff --git a/crates/perry-runtime/src/gc/policy.rs b/crates/perry-runtime/src/gc/policy.rs index 62b29b80fd..0c411d0fca 100644 --- a/crates/perry-runtime/src/gc/policy.rs +++ b/crates/perry-runtime/src/gc/policy.rs @@ -608,7 +608,7 @@ impl GcStepSnapshot { } #[derive(Clone, Copy)] -pub(super) struct GcTriggerSnapshot { +pub(crate) struct GcTriggerSnapshot { pub(super) kind: GcTriggerKind, pub(super) steps_before: Option, } diff --git a/crates/perry-runtime/src/gc/roots.rs b/crates/perry-runtime/src/gc/roots.rs index 9c5b9ff579..d79f45bee3 100644 --- a/crates/perry-runtime/src/gc/roots.rs +++ b/crates/perry-runtime/src/gc/roots.rs @@ -193,6 +193,7 @@ thread_local! { /// Set (or clear) this thread's conservative-scan mode override, returning the /// previous value. +#[allow(dead_code)] // test-only scaffolding: GC unit tests (gc/tests) pin the scan mode via this override pub(crate) fn set_conservative_stack_scan_override( mode: Option, ) -> Option { diff --git a/crates/perry-runtime/src/gc/telemetry.rs b/crates/perry-runtime/src/gc/telemetry.rs index a88fbedead..ec41683930 100644 --- a/crates/perry-runtime/src/gc/telemetry.rs +++ b/crates/perry-runtime/src/gc/telemetry.rs @@ -92,6 +92,8 @@ pub(super) struct OldYoungEdgeVerifyStats { } impl OldYoungEdgeVerifyStats { + #[allow(dead_code)] + // GC old→young edge-verify telemetry hook; simple companion to record_missing_diag for diagnostic call sites #[inline] pub(super) fn record_missing(&mut self, parent: usize, slot: usize, child: usize) { self.record_missing_diag(parent, slot, child, 0, 0, false, false); @@ -599,6 +601,7 @@ pub(super) struct GcPauseStepTrace { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))] pub(super) enum AllocatorMaintenanceStatus { Skipped, Executed, @@ -618,6 +621,7 @@ impl AllocatorMaintenanceStatus { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))] pub(super) enum AllocatorMaintenanceReason { OrdinaryBudgeted, NotSupported, @@ -1194,7 +1198,7 @@ pub(super) fn take_test_last_gc_trace_json() -> Option { TEST_LAST_GC_TRACE_JSON.with(|slot| slot.borrow_mut().take()) } -pub(super) struct GcCollectOutcome { +pub(crate) struct GcCollectOutcome { pub(super) freed_bytes: u64, pub(super) malloc_swept: bool, pub(super) trace: Option, diff --git a/crates/perry-runtime/src/gc/tests/cycle_state.rs b/crates/perry-runtime/src/gc/tests/cycle_state.rs index 3820333d4e..112519b8b3 100644 --- a/crates/perry-runtime/src/gc/tests/cycle_state.rs +++ b/crates/perry-runtime/src/gc/tests/cycle_state.rs @@ -767,14 +767,12 @@ fn born_black_build_phase_object_is_traced() { (*ch).gc_flags &= !GC_FLAG_MARKED; } crate::object::test_seed_overflow_fields_root(child as usize, 7f64.to_bits()); - unsafe { - runtime_store_jsvalue_slot( - parent as usize, - fields as usize, - 0, - ptr_bits(child as usize), - ); - } + runtime_store_jsvalue_slot( + parent as usize, + fields as usize, + 0, + ptr_bits(child as usize), + ); // Age the parent/child block out of the block-persistence window // (BLOCK_PERSIST_WINDOW = 5 recent general blocks get their objects @@ -784,7 +782,7 @@ fn born_black_build_phase_object_is_traced() { let mut filler_blocks = 0usize; while filler_blocks < 7 { for _ in 0..64 { - let _ = unsafe { crate::arena::arena_alloc_gc(4096, 8, GC_TYPE_STRING) }; + let _ = crate::arena::arena_alloc_gc(4096, 8, GC_TYPE_STRING); } filler_blocks = crate::arena::general_block_count().saturating_sub(aged_from); } @@ -844,14 +842,12 @@ fn gap_born_child_stored_between_finalize_and_sweep_survives() { // same way the production failure was (a swept child has its // OVERFLOW_FIELDS entry cleared by the dead-payload sweep arm). crate::object::test_seed_overflow_fields_root(child as usize, 42f64.to_bits()); - unsafe { - runtime_store_jsvalue_slot( - parent as usize, - fields as usize, - 0, - ptr_bits(child as usize), - ); - } + runtime_store_jsvalue_slot( + parent as usize, + fields as usize, + 0, + ptr_bits(child as usize), + ); run_cycle_in_single_unit_steps(&mut state); let _ = state.take_outcome().expect("cycle should complete"); @@ -915,7 +911,7 @@ fn overflow_slots_beyond_layout_mask_are_traced() { let mut filler_blocks = 0usize; while filler_blocks < 7 { for _ in 0..64 { - let _ = unsafe { crate::arena::arena_alloc_gc(4096, 8, GC_TYPE_STRING) }; + let _ = crate::arena::arena_alloc_gc(4096, 8, GC_TYPE_STRING); } filler_blocks = crate::arena::general_block_count().saturating_sub(aged_from); } diff --git a/crates/perry-runtime/src/gc/trace.rs b/crates/perry-runtime/src/gc/trace.rs index 45cf646cde..bef18bb800 100644 --- a/crates/perry-runtime/src/gc/trace.rs +++ b/crates/perry-runtime/src/gc/trace.rs @@ -754,6 +754,7 @@ pub(super) fn trace_one_worklist_header( } /// Trace from marked objects: follow references iteratively using a worklist. +#[allow(dead_code)] // test scaffolding: exercised only by gc::tests::layout_trace under cfg(test) pub(super) fn trace_marked_objects(valid_ptrs: &ValidPointerSet) { // Same MARK_SEEDS-based approach as the minor variant — root scans // populated `MARK_SEEDS` via `try_mark_value`, no need to walk arena diff --git a/crates/perry-runtime/src/gc/verify.rs b/crates/perry-runtime/src/gc/verify.rs index 072ada8414..ef15f56b32 100644 --- a/crates/perry-runtime/src/gc/verify.rs +++ b/crates/perry-runtime/src/gc/verify.rs @@ -191,7 +191,7 @@ pub(super) fn restore_surviving_dirty_coverage(snapshot: &RememberedDirtySnapsho { return; } - visit_gc_rewrite_slots(header, |slot| unsafe { + visit_gc_rewrite_slots(header, |slot| { if crate::weakref::is_weak_target_trace_slot(header, slot.slot) { return; } @@ -614,6 +614,7 @@ impl MarkInvariantVerifyStats { } #[cold] +#[allow(dead_code)] // GC heap-invariant verifier (PERRY_GC_VERIFY_EVACUATION); driven by verify_marked_heap_no_unmarked_children and its cfg(test) callers in gc/tests/barrier.rs pub(super) fn panic_mark_invariant_verifier_failed(stats: MarkInvariantVerifyStats) -> ! { let missing = stats.first_missing.unwrap_or_default(); panic!( @@ -656,6 +657,7 @@ pub(super) unsafe fn verify_marked_object_child_marks( }); } +#[allow(dead_code)] // GC heap-invariant verifier exercised by cfg(test) suite in gc/tests/barrier.rs pub(super) fn verify_marked_heap_no_unmarked_children() -> MarkInvariantVerifyStats { let mut stats = MarkInvariantVerifyStats::default(); crate::arena::arena_walk_objects(|hp| unsafe { @@ -746,7 +748,7 @@ pub(super) fn verify_minor_unmarked_young_children_report(phase: &str) { return; } checked_parents += 1; - visit_gc_rewrite_slots(header, |slot| unsafe { + visit_gc_rewrite_slots(header, |slot| { if crate::weakref::is_weak_target_trace_slot(header, slot.slot) { return; } diff --git a/crates/perry-runtime/src/intl.rs b/crates/perry-runtime/src/intl.rs index dfbfd8990d..840750419a 100644 --- a/crates/perry-runtime/src/intl.rs +++ b/crates/perry-runtime/src/intl.rs @@ -5,6 +5,16 @@ //! DateTimeFormat, and Collator, with deterministic formatting for the common //! explicit locale/options combinations used by Perry's Node parity suite. +#![cfg_attr( + not(any( + feature = "intl-namespace", + feature = "intl-locale", + feature = "intl-datetime", + feature = "intl-segmenter" + )), + allow(dead_code, unused_imports) +)] + use crate::array::{js_array_alloc, js_array_get_f64, js_array_length, js_array_push_f64}; use crate::closure::ClosureHeader; use crate::object::{ @@ -458,6 +468,7 @@ fn throw_invalid_language_tag(tag: &str) -> ! { crate::exception::js_throw(js_nanbox_pointer(err as i64)) } +#[allow(dead_code)] // used only in the #[cfg(not(feature = "intl-locale"))] fallback branch pub(crate) fn canonical_locale(tag: &str) -> Option { if tag.is_empty() { return None; diff --git a/crates/perry-runtime/src/intl/date_collator.rs b/crates/perry-runtime/src/intl/date_collator.rs index 2d0e316c85..0758480a27 100644 --- a/crates/perry-runtime/src/intl/date_collator.rs +++ b/crates/perry-runtime/src/intl/date_collator.rs @@ -54,19 +54,6 @@ fn date_arg_to_clipped_ms(value: f64) -> f64 { ms.trunc() } -pub(crate) extern "C" fn date_time_format_format_thunk( - _closure: *const ClosureHeader, - value: f64, -) -> f64 { - let obj = this_intl_object("format", KIND_DATE_TIME); - let temporal_kind = crate::temporal::temporal_kind(value); - if let Some(kind) = temporal_kind { - validate_temporal_dtf_overlap(kind, obj); - } - let ms = date_arg_to_clipped_ms(value); - string_value(&format_ms_with_dtf_obj(obj, ms, temporal_kind)) -} - pub(crate) extern "C" fn date_time_format_bound_format_thunk( closure: *const ClosureHeader, value: f64, @@ -91,13 +78,6 @@ pub(crate) extern "C" fn date_time_format_format_getter_thunk( get_field(obj, KEY_DTF_BOUND_FORMAT) } -/// Fallback path: no DTF object context, produce short UTC date. Still used by -/// some internal callers that pre-date the obj-aware thunks. -pub(crate) fn date_time_format_format_value(value: f64) -> f64 { - let ms = date_arg_to_clipped_ms(value); - string_value(&date_short_utc_from_ms(ms)) -} - pub(crate) extern "C" fn date_time_format_to_parts_thunk( _closure: *const ClosureHeader, value: f64, @@ -696,15 +676,6 @@ fn time_zone_name_display(time_zone: &str, style: &str) -> String { "GMT".to_string() } -/// `M/D/YYYY` short form rendered directly from an integer-millisecond -/// timestamp. Shared by `format`, `formatToParts`, and both range variants so -/// all four stay byte-for-byte consistent. -pub(crate) fn date_short_utc_from_ms(ms: f64) -> String { - let secs = (ms as i64).div_euclid(1000); - let (year, month, day, _, _, _) = crate::date::timestamp_to_components(secs); - format!("{}/{}/{}", month, day, year) -} - pub(crate) fn date_range_parts_from_ms(ms: f64) -> Vec<(&'static str, String)> { let secs = (ms as i64).div_euclid(1000); let (year, month, day, _, _, _) = crate::date::timestamp_to_components(secs); diff --git a/crates/perry-runtime/src/intl/date_collator/temporal.rs b/crates/perry-runtime/src/intl/date_collator/temporal.rs index e6cba90afa..ec027f9112 100644 --- a/crates/perry-runtime/src/intl/date_collator/temporal.rs +++ b/crates/perry-runtime/src/intl/date_collator/temporal.rs @@ -9,6 +9,7 @@ use super::*; /// Context tag for [`temporal_locale_string`] — which Temporal type is being formatted. /// Controls default options, type-specific TypeError guards, and timezone handling. #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "temporal"), allow(dead_code))] pub(crate) enum TemporalLocaleCtx { PlainDate, PlainDateTime, diff --git a/crates/perry-runtime/src/intl/number_format.rs b/crates/perry-runtime/src/intl/number_format.rs index 10b783e050..386b4e5a60 100644 --- a/crates/perry-runtime/src/intl/number_format.rs +++ b/crates/perry-runtime/src/intl/number_format.rs @@ -1144,7 +1144,7 @@ fn bigint_number_parts_exact( /// where both are lossy for BigInts past 2^53. pub(crate) fn bigint_to_locale_string(value: f64, locales: f64, options: f64) -> *mut StringHeader { let ptr = JSValue::from_bits(value.to_bits()).as_bigint_ptr(); - let negative = unsafe { crate::bigint::js_bigint_is_negative(ptr) } != 0; + let negative = crate::bigint::js_bigint_is_negative(ptr) != 0; let digits_ptr = crate::bigint::js_bigint_to_string(ptr); // Copy into an owned `String` right away — `digits_ptr` is GC-managed and // `make_instance` below allocates, which can move/free it. diff --git a/crates/perry-runtime/src/iterator_helpers.rs b/crates/perry-runtime/src/iterator_helpers.rs index 027df66f44..fbf842a536 100644 --- a/crates/perry-runtime/src/iterator_helpers.rs +++ b/crates/perry-runtime/src/iterator_helpers.rs @@ -179,7 +179,8 @@ pub extern "C" fn js_iterator_from(val_f64: f64) -> f64 { } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ITERATOR_FROM: extern "C" fn(f64) -> f64 = js_iterator_from; /// `.next()` on a helper iterator object. Pulls lazily from the source per the diff --git a/crates/perry-runtime/src/json/parse_api.rs b/crates/perry-runtime/src/json/parse_api.rs index 9da7761e20..5600afc6cd 100644 --- a/crates/perry-runtime/src/json/parse_api.rs +++ b/crates/perry-runtime/src/json/parse_api.rs @@ -22,7 +22,8 @@ pub extern "C" fn js_json_text_to_string(value: f64) -> *mut StringHeader { // Anchor so the auto-optimize bitcode rebuild doesn't dead-strip this // codegen-only `#[no_mangle]` (see KEEP_RAW_JSON in json/raw_json.rs). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JSON_TEXT_TO_STRING: extern "C" fn(f64) -> *mut StringHeader = js_json_text_to_string; // ─── JSON.parse ─────────────────────────────────────────────────────────────── diff --git a/crates/perry-runtime/src/json/raw_json.rs b/crates/perry-runtime/src/json/raw_json.rs index 7ac7558c75..4c52e8c0ea 100644 --- a/crates/perry-runtime/src/json/raw_json.rs +++ b/crates/perry-runtime/src/json/raw_json.rs @@ -169,7 +169,9 @@ fn throw_raw_json_syntax_error() -> ! { // Keepalive anchors: these `#[no_mangle]` entry points are called only from // generated `.o`; the auto-optimize whole-program bitcode rebuild would // otherwise dead-strip them (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_RAW_JSON: unsafe extern "C" fn(f64) -> f64 = js_json_raw_json; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_IS_RAW_JSON: unsafe extern "C" fn(f64) -> f64 = js_json_is_raw_json; diff --git a/crates/perry-runtime/src/json/reviver.rs b/crates/perry-runtime/src/json/reviver.rs index 0e58d20807..3894f931b2 100644 --- a/crates/perry-runtime/src/json/reviver.rs +++ b/crates/perry-runtime/src/json/reviver.rs @@ -582,10 +582,6 @@ unsafe fn object_child_source( } } -unsafe fn holder_ptr_from_bits(bits: u64) -> *mut crate::ObjectHeader { - (bits & POINTER_MASK) as *mut crate::ObjectHeader -} - unsafe fn delete_property_or_keep( holder_handle: &crate::gc::RuntimeHandle<'_>, key_handle: &crate::gc::RuntimeHandle<'_>, diff --git a/crates/perry-runtime/src/map.rs b/crates/perry-runtime/src/map.rs index dde9f27660..9379a702bb 100644 --- a/crates/perry-runtime/src/map.rs +++ b/crates/perry-runtime/src/map.rs @@ -1013,7 +1013,7 @@ const SIDE_TABLE_THRESHOLD: u32 = 8; /// C-ABI: current entries-array index of `key` (SameValueZero), or `-1.0` if /// absent. Used by the delete-safe `for-of` fast path (#6075) to re-derive the /// cursor after a mid-iteration delete compacts the entries array. Only invoked -/// from generated IR, so `#[cfg_attr(feature = "keepalive-anchors", used)]` keeps it linked on the default compile path. +/// from generated IR, so `#[used]` keeps it linked on the default compile path. #[no_mangle] pub extern "C" fn js_map_find_key_index(map_boxed: f64, key: f64) -> f64 { let map = clean_map_ptr(crate::value::js_nanbox_get_pointer(map_boxed) as *const MapHeader); @@ -1022,7 +1022,8 @@ pub extern "C" fn js_map_find_key_index(map_boxed: f64, key: f64) -> f64 { } unsafe { find_key_index(map, normalize_zero(key)) as f64 } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_MAP_FIND_KEY_INDEX: extern "C" fn(f64, f64) -> f64 = js_map_find_key_index; pub(crate) unsafe fn find_key_index(map: *const MapHeader, key: f64) -> i32 { @@ -1616,67 +1617,81 @@ pub extern "C" fn js_map_delete_number_key(map: *mut MapHeader, key: f64) -> i32 // Codegen emits these string-key typed lowering helpers directly from // generated LLVM IR. Keep roots prevent whole-program LTO/dead-strip from // removing the exported symbols when the Rust crate graph has no caller. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_NUMBER: extern "C" fn( *mut MapHeader, *const StringHeader, f64, ) -> *mut MapHeader = js_map_set_string_number; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_NUMBER_KEY: extern "C" fn(*mut MapHeader, f64, f64) -> *mut MapHeader = js_map_set_number_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_KEY: extern "C" fn( *mut MapHeader, *const StringHeader, f64, ) -> *mut MapHeader = js_map_set_string_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_I32: extern "C" fn( *mut MapHeader, *const StringHeader, i32, ) -> *mut MapHeader = js_map_set_string_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_U32: extern "C" fn( *mut MapHeader, *const StringHeader, u32, ) -> *mut MapHeader = js_map_set_string_u32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_F32: extern "C" fn( *mut MapHeader, *const StringHeader, f32, ) -> *mut MapHeader = js_map_set_string_f32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_BOOL: extern "C" fn( *mut MapHeader, *const StringHeader, i32, ) -> *mut MapHeader = js_map_set_string_bool; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_SET_STRING_STRING: extern "C" fn( *mut MapHeader, *const StringHeader, *const StringHeader, ) -> *mut MapHeader = js_map_set_string_string; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_GET_STRING_KEY: extern "C" fn(*const MapHeader, *const StringHeader) -> f64 = js_map_get_string_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_GET_NUMBER_KEY: extern "C" fn(*const MapHeader, f64) -> f64 = js_map_get_number_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_HAS_STRING_KEY: extern "C" fn(*const MapHeader, *const StringHeader) -> i32 = js_map_has_string_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_HAS_NUMBER_KEY: extern "C" fn(*const MapHeader, f64) -> i32 = js_map_has_number_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_DELETE_STRING_KEY: extern "C" fn(*mut MapHeader, *const StringHeader) -> i32 = js_map_delete_string_key; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_DELETE_NUMBER_KEY: extern "C" fn(*mut MapHeader, f64) -> i32 = js_map_delete_number_key; @@ -2232,8 +2247,9 @@ pub extern "C" fn js_map_from_iterable(value: f64) -> *mut MapHeader { // `perry-codegen/src/expr/misc_methods.rs`), so it has zero internal Rust // callers. The whole-program auto-optimize bitcode link would otherwise // internalize + dead-strip the `#[no_mangle]` export and break the default -// compile path. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it (see project_auto_optimize_keepalive). -#[cfg_attr(feature = "keepalive-anchors", used)] +// compile path. The `#[used]` anchor pins it (see project_auto_optimize_keepalive). +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MAP_FROM_ITERABLE: extern "C" fn(f64) -> *mut MapHeader = js_map_from_iterable; /// `Map.prototype.forEach(callback, thisArg)` — calls `callback` with the diff --git a/crates/perry-runtime/src/module_require.rs b/crates/perry-runtime/src/module_require.rs index d4ec3a9321..0f5609d24f 100644 --- a/crates/perry-runtime/src/module_require.rs +++ b/crates/perry-runtime/src/module_require.rs @@ -492,7 +492,8 @@ pub extern "C" fn js_module_ambient_require() -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code-only /// callee; see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_AMBIENT_REQUIRE: extern "C" fn() -> f64 = js_module_ambient_require; /// Synchronous ambient `require(spec)` resolution for the #5389 Tier 2 codegen @@ -510,7 +511,8 @@ pub extern "C" fn js_module_ambient_require_apply(spec: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code-only /// callee; see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_AMBIENT_REQUIRE_APPLY: extern "C" fn(f64) -> f64 = js_module_ambient_require_apply; @@ -571,7 +573,8 @@ pub extern "C" fn js_module_dynamic_import_fallback(spec: f64) -> f64 { } /// Keepalive anchor (same pattern as the ambient-require anchors above). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_DYNAMIC_IMPORT_FALLBACK: extern "C" fn(f64) -> f64 = js_module_dynamic_import_fallback; @@ -592,7 +595,8 @@ pub extern "C" fn js_module_dynamic_import_deferred(spec: f64, msg: f64) -> f64 } /// Keepalive anchor (same pattern as the ambient-require anchors above). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_DYNAMIC_IMPORT_DEFERRED: extern "C" fn(f64, f64) -> f64 = js_module_dynamic_import_deferred; diff --git a/crates/perry-runtime/src/native_abi.rs b/crates/perry-runtime/src/native_abi.rs index 47023275e6..5bec79bd86 100644 --- a/crates/perry-runtime/src/native_abi.rs +++ b/crates/perry-runtime/src/native_abi.rs @@ -160,36 +160,48 @@ pub extern "C" fn js_typed_string_arg_to_raw(value: f64) -> i64 { // internal typed clone. They have no Rust call sites, so keep explicit // function-pointer references to prevent whole-program LTO/dead-strip from // removing the exported symbols. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_F64_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_f64_arg_guard; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_F64_ARG_TO_RAW: extern "C" fn(f64) -> f64 = js_typed_f64_arg_to_raw; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_I32_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_i32_arg_guard; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_I32_ARG_TO_RAW: extern "C" fn(f64) -> i32 = js_typed_i32_arg_to_raw; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_I1_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_i1_arg_guard; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_I1_ARG_TO_RAW: extern "C" fn(f64) -> i32 = js_typed_i1_arg_to_raw; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_STRING_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_string_arg_guard; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_STRING_ARG_TO_RAW: extern "C" fn(f64) -> i64 = js_typed_string_arg_to_raw; // Static-name and static-method lowering emits these by-id wrappers directly // from generated LLVM IR. Keep roots here so LTO cannot strip the symbols just // because the Rust crate graph has no ordinary caller. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_OBJECT_GET_FIELD_BY_PROPERTY_ID_F64: extern "C" fn(*const ObjectHeader, i64) -> f64 = crate::object::js_object_get_field_by_property_id_f64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_OBJECT_SET_FIELD_BY_PROPERTY_ID: extern "C" fn(*mut ObjectHeader, i64, f64) = crate::object::js_object_set_field_by_property_id; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_NATIVE_CALL_METHOD_BY_ID: unsafe extern "C" fn(f64, i64, *const f64, usize) -> f64 = crate::object::js_native_call_method_by_id; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_NATIVE_CALL_METHOD_APPLY_BY_ID: unsafe extern "C" fn(f64, i64, i64) -> f64 = crate::object::js_native_call_method_apply_by_id; diff --git a/crates/perry-runtime/src/node_sea.rs b/crates/perry-runtime/src/node_sea.rs index 206131b4ee..745939bf1b 100644 --- a/crates/perry-runtime/src/node_sea.rs +++ b/crates/perry-runtime/src/node_sea.rs @@ -7,15 +7,20 @@ use crate::value::{JSValue, TAG_FALSE}; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SEA_IS_SEA: extern "C" fn() -> f64 = js_sea_is_sea; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SEA_GET_ASSET: extern "C" fn(f64, f64) -> f64 = js_sea_get_asset; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SEA_GET_ASSET_AS_BLOB: extern "C" fn(f64, f64) -> f64 = js_sea_get_asset_as_blob; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SEA_GET_RAW_ASSET: extern "C" fn(f64) -> f64 = js_sea_get_raw_asset; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SEA_GET_ASSET_KEYS: extern "C" fn() -> f64 = js_sea_get_asset_keys; fn false_value() -> f64 { diff --git a/crates/perry-runtime/src/node_stream.rs b/crates/perry-runtime/src/node_stream.rs index a02bfd9a2a..703221c709 100644 --- a/crates/perry-runtime/src/node_stream.rs +++ b/crates/perry-runtime/src/node_stream.rs @@ -1723,19 +1723,19 @@ pub extern "C" fn js_node_stream_method_uncork(stream_handle: i64) -> f64 { // `pub(super)` helpers remain crate-internal. #[path = "node_stream_keys.rs"] mod keys; -pub use keys::*; +use keys::*; #[path = "node_stream_dispatch.rs"] mod dispatch; -pub use dispatch::*; +pub(crate) use dispatch::*; #[path = "node_stream_iter_helpers.rs"] mod iter_helpers; -pub use iter_helpers::*; +use iter_helpers::*; #[path = "node_stream_pipeline.rs"] mod pipeline; -pub use pipeline::*; +use pipeline::*; #[path = "node_stream_readwrite.rs"] mod readwrite; @@ -1743,19 +1743,19 @@ pub use readwrite::*; #[path = "node_stream_readable_read.rs"] mod readable_read; -pub use readable_read::*; +use readable_read::*; #[path = "node_stream_duplex_methods.rs"] mod duplex_method_table; -pub use duplex_method_table::*; +use duplex_method_table::*; #[path = "node_stream_compose_live.rs"] mod compose_live; -pub use compose_live::*; +use compose_live::*; #[path = "node_stream_json.rs"] mod json_stream; -pub use json_stream::*; +pub(crate) use json_stream::*; #[path = "node_stream_constructors.rs"] mod constructors; diff --git a/crates/perry-runtime/src/node_stream_constructors/web_adapter.rs b/crates/perry-runtime/src/node_stream_constructors/web_adapter.rs index 1fb410d1a8..fbfd78eeca 100644 --- a/crates/perry-runtime/src/node_stream_constructors/web_adapter.rs +++ b/crates/perry-runtime/src/node_stream_constructors/web_adapter.rs @@ -94,6 +94,7 @@ fn web_readable_close() -> Option { load_web_callback!(WEB_READABLE_CLOSE_PTR, WebReadableCloseFn) } +#[allow(dead_code)] // accessor for the deliberately-registered WEB_READABLE_ERROR_PTR callback (set in register); consumer not yet wired fn web_readable_error() -> Option { load_web_callback!(WEB_READABLE_ERROR_PTR, WebReadableErrorFn) } diff --git a/crates/perry-runtime/src/node_stream_iter_helpers.rs b/crates/perry-runtime/src/node_stream_iter_helpers.rs index 7fa3ef3c59..ac1b503d88 100644 --- a/crates/perry-runtime/src/node_stream_iter_helpers.rs +++ b/crates/perry-runtime/src/node_stream_iter_helpers.rs @@ -142,27 +142,6 @@ pub(super) fn promise_from_capture( crate::value::js_nanbox_get_pointer(f64::from_bits(bits)) as *mut crate::promise::Promise } -/// Abort-listener body: reject the captured Promise with an AbortError. -pub(super) extern "C" fn ns_abort_reject(closure: *const ClosureHeader) -> f64 { - let p = promise_from_capture(closure, 0); - if !p.is_null() { - crate::promise::js_promise_reject(p, abort_error()); - } - f64::from_bits(TAG_UNDEFINED) -} - -/// Deferred-resolve body: fulfill the captured Promise (slot 0) with the -/// captured value (slot 1) on the next microtask — a no-op if an abort -/// already rejected it. -pub(super) extern "C" fn ns_deferred_resolve(closure: *const ClosureHeader) -> f64 { - let p = promise_from_capture(closure, 0); - let value = f64::from_bits(js_closure_get_capture_ptr(closure, 1) as u64); - if !p.is_null() { - crate::promise::js_promise_resolve(p, value); - } - f64::from_bits(TAG_UNDEFINED) -} - pub(super) extern "C" fn ns_stream_abort_listener(closure: *const ClosureHeader) -> f64 { if closure.is_null() { return f64::from_bits(TAG_UNDEFINED); @@ -172,47 +151,6 @@ pub(super) extern "C" fn ns_stream_abort_listener(closure: *const ClosureHeader) f64::from_bits(TAG_UNDEFINED) } -/// Build a pending Promise for a consuming helper running under a -/// not-yet-aborted signal: an abort listener rejects it with an -/// AbortError, while a queued microtask fulfills it with `value` if no -/// abort fires first. This matches Node's async timing — the operation -/// is in flight when a synchronous `controller.abort()` lands before -/// the awaiter resumes. -pub(super) fn deferred_promise(signal: f64, value: f64) -> f64 { - let promise = crate::promise::js_promise_new(); - let promise_box = box_pointer(promise as *const u8); - - if let Some(sig_obj) = object_ptr_from_value(signal) { - let reject_cl = js_closure_alloc(ns_abort_reject as *const u8, 1); - crate::closure::js_closure_set_capture_ptr(reject_cl, 0, promise_box.to_bits() as i64); - crate::url::js_abort_signal_add_listener( - sig_obj, - string_value(b"abort"), - box_pointer(reject_cl as *const u8), - ); - } - - let resolve_cl = js_closure_alloc(ns_deferred_resolve as *const u8, 2); - crate::closure::js_closure_set_capture_ptr(resolve_cl, 0, promise_box.to_bits() as i64); - crate::closure::js_closure_set_capture_ptr(resolve_cl, 1, value.to_bits() as i64); - crate::builtins::js_queue_microtask(resolve_cl as i64); - - promise_box -} - -/// Settle a consuming helper's result under any governing signal: reject -/// now if already aborted, defer if a signal is pending, else resolve. -pub(super) fn settle_consuming(this: f64, opts: f64, value: f64) -> f64 { - if let Some(err) = readable_hidden_error(this) { - return rejected_promise(err); - } - match effective_signal(this, opts) { - Some(sig) if signal_is_aborted(sig) => rejected_promise(abort_error()), - Some(sig) => deferred_promise(sig, value), - None => resolved_promise(value), - } -} - /// Carry a lazy helper's source error and governing signal onto its /// freshly-built result stream so a downstream consuming helper can /// observe an abort or error that happens later in the chain. diff --git a/crates/perry-runtime/src/node_stream_keepalive.rs b/crates/perry-runtime/src/node_stream_keepalive.rs index 2b3e9599be..a619f40587 100644 --- a/crates/perry-runtime/src/node_stream_keepalive.rs +++ b/crates/perry-runtime/src/node_stream_keepalive.rs @@ -5,183 +5,253 @@ // by any Rust code in the crate graph. The default `.a` staticlib keeps // them via staticlib-export semantics, but the auto-optimize build round- // trips the runtime through whole-program LLVM bitcode and is free to -// internalize and dead-strip an unreferenced symbol. The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics +// internalize and dead-strip an unreferenced symbol. The `#[used]` statics // below pin retained reference edges so every entry point survives all link // modes. See the same pattern in `value/dyn_index.rs` and `process.rs`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_EMIT: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_emit; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_EMIT_ARGS: extern "C" fn(i64, f64, i64) -> f64 = super::js_node_stream_method_emit_args; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_READ: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_read; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_PUSH: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_push; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_UNSHIFT: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_unshift; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_HWM: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_hwm; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_LENGTH: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_length; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_OBJECT_MODE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_object_mode; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_READABLE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_READABLE_ENDED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_ended; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_READABLE_ENCODING: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_encoding; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_HWM: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_hwm; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_LENGTH: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_length; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_NEED_DRAIN: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_need_drain; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_OBJECT_MODE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_object_mode; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_ABORTED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_aborted; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_CLOSED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_closed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_ERRORED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_errored; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_DID_READ: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_did_read; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_CORKED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_corked; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_WRITABLE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_WRITABLE_ENDED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_ended; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_WRITABLE_FINISHED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_finished; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_ALLOW_HALF_OPEN: extern "C" fn(i64) -> f64 = super::js_node_stream_method_allow_half_open; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_PAUSE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_pause; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_RESUME: extern "C" fn(i64) -> f64 = super::js_node_stream_method_resume; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_SET_ENCODING: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_set_encoding; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_DESTROY: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_destroy; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_DESTROYED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_destroyed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_WRITE: extern "C" fn(i64, f64, f64, f64) -> f64 = super::js_node_stream_method_write; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_END: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_end; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_END3: extern "C" fn(i64, f64, f64, f64) -> f64 = super::js_node_stream_method_end3; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_CORK: extern "C" fn(i64) -> f64 = super::js_node_stream_method_cork; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_UNCORK: extern "C" fn(i64) -> f64 = super::js_node_stream_method_uncork; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_SET_MAX_LISTENERS: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_set_max_listeners; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_GET_MAX_LISTENERS: extern "C" fn(i64) -> f64 = super::js_node_stream_method_get_max_listeners; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_ON: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_on; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_ONCE: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_once; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_PREPEND_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_prepend_listener; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_PREPEND_ONCE_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_prepend_once_listener; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_OFF: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_off; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_REMOVE_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_remove_listener; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_REMOVE_ALL_LISTENERS: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_remove_all_listeners; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_EVENT_NAMES: extern "C" fn(i64) -> i64 = super::js_node_stream_method_event_names; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_LISTENER_COUNT: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_listener_count; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_LISTENERS: extern "C" fn(i64, f64) -> i64 = super::js_node_stream_method_listeners; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_METHOD_RAW_LISTENERS: extern "C" fn(i64, f64) -> i64 = super::js_node_stream_method_raw_listeners; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_readable_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_WRITABLE_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_writable_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_DUPLEX_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_duplex_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_TRANSFORM_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_transform_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_PASSTHROUGH_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_passthrough_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_FROM: extern "C" fn(f64) -> f64 = super::js_node_stream_readable_from; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_READABLE_FROM_OPTIONS: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_readable_from_options; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_DISTURBED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_disturbed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_ERRORED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_errored; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_READABLE: extern "C" fn(f64) -> f64 = super::js_node_stream_is_readable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_WRITABLE: extern "C" fn(f64) -> f64 = super::js_node_stream_is_writable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_ARRAY_BUFFER_VIEW: extern "C" fn(f64) -> f64 = super::js_node_stream_is_array_buffer_view; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_UINT8_ARRAY: extern "C" fn(f64) -> f64 = super::js_node_stream_is_uint8_array; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_IS_DESTROYED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_destroyed; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_UINT8_ARRAY_TO_BUFFER: extern "C" fn(f64) -> f64 = super::js_node_stream_uint8_array_to_buffer; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_GET_DEFAULT_HWM: extern "C" fn(f64) -> f64 = super::js_node_stream_get_default_hwm; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_SET_DEFAULT_HWM: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_set_default_hwm; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_ADD_ABORT_SIGNAL: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_add_abort_signal; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_COMPOSE: extern "C" fn(*const crate::array::ArrayHeader) -> f64 = super::js_node_stream_compose; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_PIPELINE: extern "C" fn(*const crate::array::ArrayHeader) -> f64 = super::js_node_stream_pipeline; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_DUPLEX_PAIR: extern "C" fn(f64) -> f64 = super::js_node_stream_duplex_pair; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_TO_WEB: extern "C" fn(f64) -> f64 = super::js_node_stream_to_web; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NS_FROM_WEB: extern "C" fn(f64) -> f64 = super::js_node_stream_from_web; diff --git a/crates/perry-runtime/src/node_stream_readable_read.rs b/crates/perry-runtime/src/node_stream_readable_read.rs index 2b42e0dd7a..69f71f2a4e 100644 --- a/crates/perry-runtime/src/node_stream_readable_read.rs +++ b/crates/perry-runtime/src/node_stream_readable_read.rs @@ -285,17 +285,3 @@ pub(super) fn read_stream_object_mode_chunk(stream: f64) -> f64 { } chunk } - -pub(super) fn string_chunk_to_buffer(value: f64) -> Option { - let jsval = JSValue::from_bits(value.to_bits()); - if !jsval.is_any_string() { - return None; - } - let ptr = crate::value::js_get_string_pointer_unified(value) as *const crate::StringHeader; - if ptr.is_null() || (ptr as usize) < 0x10000 { - return None; - } - Some(box_pointer( - crate::buffer::js_buffer_from_string(ptr, 0) as *const u8 - )) -} diff --git a/crates/perry-runtime/src/node_stream_readwrite.rs b/crates/perry-runtime/src/node_stream_readwrite.rs index 72bf46a350..92e9130110 100644 --- a/crates/perry-runtime/src/node_stream_readwrite.rs +++ b/crates/perry-runtime/src/node_stream_readwrite.rs @@ -1729,10 +1729,6 @@ pub(crate) fn js_node_stream_is_stub_ended_after_read(stream: f64) -> bool { stream_hidden_ended(stream) } -pub(crate) fn js_node_stream_is_stub_ended(stream: f64) -> bool { - stream_hidden_ended(stream) -} - #[cfg(test)] pub(crate) fn test_set_hidden_error(stream: f64, err: f64) { set_hidden_value(stream, hidden_error_key(), err); diff --git a/crates/perry-runtime/src/node_stream_tests_extra.rs b/crates/perry-runtime/src/node_stream_tests_extra.rs index 76e49e3ca8..520b19a28c 100644 --- a/crates/perry-runtime/src/node_stream_tests_extra.rs +++ b/crates/perry-runtime/src/node_stream_tests_extra.rs @@ -569,6 +569,7 @@ fn destroyed_readable_drops_late_push_data() { READABLE_DATA_CAPTURED.with(|captured| assert!(captured.borrow().is_empty())); } +#[test] fn duplex_allow_half_open_defaults_true_and_honors_false_option() { let stream = js_node_stream_duplex_new(f64::from_bits(TAG_UNDEFINED)); let handle = raw_ptr_from_value(stream) as i64; diff --git a/crates/perry-runtime/src/node_submodules/consumers.rs b/crates/perry-runtime/src/node_submodules/consumers.rs index a2196d4383..d21fe115ce 100644 --- a/crates/perry-runtime/src/node_submodules/consumers.rs +++ b/crates/perry-runtime/src/node_submodules/consumers.rs @@ -613,6 +613,7 @@ pub(crate) extern "C" fn thunk_consumers_buffer( consume_stream(ConsumerKind::Buffer, stream) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_consumers_arrayBuffer( _closure: *const ClosureHeader, stream: f64, diff --git a/crates/perry-runtime/src/node_submodules/diagnostics.rs b/crates/perry-runtime/src/node_submodules/diagnostics.rs index e5dae3768f..0f23f8ffcd 100644 --- a/crates/perry-runtime/src/node_submodules/diagnostics.rs +++ b/crates/perry-runtime/src/node_submodules/diagnostics.rs @@ -694,14 +694,14 @@ pub(crate) fn closure_ptr(v: f64) -> *const ClosureHeader { } pub(crate) fn set_field_value(obj: *mut ObjectHeader, name: &str, value: f64) { - unsafe { + { let key = js_string_from_bytes(name.as_bytes().as_ptr(), name.len() as u32); js_object_set_field_by_name(obj, key, value); } } pub(crate) fn get_field_value(obj: *mut ObjectHeader, name: &str) -> f64 { - unsafe { + { let key = js_string_from_bytes(name.as_bytes().as_ptr(), name.len() as u32); js_object_get_field_by_name_f64(obj, key) } diff --git a/crates/perry-runtime/src/node_submodules/fs_promises.rs b/crates/perry-runtime/src/node_submodules/fs_promises.rs index 02bd1f1c40..17ccde3feb 100644 --- a/crates/perry-runtime/src/node_submodules/fs_promises.rs +++ b/crates/perry-runtime/src/node_submodules/fs_promises.rs @@ -111,6 +111,7 @@ pub extern "C" fn js_fs_promises_rmdir(path: f64, options: f64) -> f64 { thunk_fs_promises_rmdir(std::ptr::null(), path, options) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_fs_promises_readFile( _closure: *const ClosureHeader, path: f64, @@ -136,6 +137,7 @@ pub(crate) extern "C" fn thunk_fs_promises_open( } } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_fs_promises_writeFile( _closure: *const ClosureHeader, path: f64, @@ -153,6 +155,7 @@ pub(crate) extern "C" fn thunk_fs_promises_writeFile( } } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_fs_promises_appendFile( _closure: *const ClosureHeader, path: f64, @@ -284,6 +287,7 @@ pub(crate) extern "C" fn thunk_fs_promises_rename( promise_from_result_undefined(|| unsafe { crate::fs::js_fs_rename_result(from, to) }) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_fs_promises_copyFile( _closure: *const ClosureHeader, from: f64, @@ -373,6 +377,7 @@ pub(crate) extern "C" fn thunk_fs_promises_mkdtemp( promise_from_sync_value(|| crate::fs::js_fs_mkdtemp_dispatch(prefix, options)) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_fs_promises_mkdtempDisposable( _closure: *const ClosureHeader, prefix: f64, @@ -781,6 +786,7 @@ fn readline_promises_create_interface(opts: f64) -> f64 { obj_value } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_readline_createInterface( _closure: *const ClosureHeader, opts: f64, @@ -788,6 +794,7 @@ pub(crate) extern "C" fn thunk_readline_createInterface( readline_promises_create_interface(opts) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_readline_Interface( _closure: *const ClosureHeader, opts: f64, @@ -960,6 +967,7 @@ pub extern "C" fn js_readline_promises_readline_new(output: f64, options: f64) - obj_value } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_readline_Readline( _closure: *const ClosureHeader, output: f64, diff --git a/crates/perry-runtime/src/node_submodules/mod.rs b/crates/perry-runtime/src/node_submodules/mod.rs index 3972d3aaad..67c3966c37 100644 --- a/crates/perry-runtime/src/node_submodules/mod.rs +++ b/crates/perry-runtime/src/node_submodules/mod.rs @@ -76,7 +76,7 @@ impl ExportThunk { /// `(submodule_key, export_name)` and falls back to `TAG_TRUE` if no /// matching entry is found (preserving the pre-#841 behavior for any /// future export Perry doesn't yet know about). -struct SubmoduleSpec { +pub(super) struct SubmoduleSpec { /// Stable key — matches the prefix used in the generated FFI symbol /// names (`js_node_submod__export_`). key: &'static str, @@ -94,6 +94,7 @@ struct SubmoduleSpec { macro_rules! thunk { ($name:ident, $msg:expr) => { + #[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn $name( _closure: *const crate::closure::ClosureHeader, _arg: f64, @@ -1257,9 +1258,7 @@ fn fs_promises_constants_value() -> f64 { fn set_named_value(obj: *mut ObjectHeader, name: &str, value: f64) { let name_bytes = name.as_bytes(); let name_header = js_string_from_bytes(name_bytes.as_ptr(), name_bytes.len() as u32); - unsafe { - crate::object::js_object_set_field_by_name(obj, name_header, value); - } + crate::object::js_object_set_field_by_name(obj, name_header, value); } fn submodule_export_value(submod: &'static SubmoduleSpec, spec: &'static ExportSpec) -> f64 { @@ -1361,9 +1360,7 @@ fn ensure_namespace_singleton(submod: &'static SubmoduleSpec) -> *mut ObjectHead let value = value_from_ptr(obj as *const u8); let name = b"default"; let name_header = js_string_from_bytes(name.as_ptr(), name.len() as u32); - unsafe { - crate::object::js_object_set_field_by_name(obj, name_header, value); - } + crate::object::js_object_set_field_by_name(obj, name_header, value); } if submod.key == "timers" { let value = crate::object::timers_promises_parent_namespace(); diff --git a/crates/perry-runtime/src/node_submodules/stream_promises.rs b/crates/perry-runtime/src/node_submodules/stream_promises.rs index 1d96a3c1f0..1de33a7384 100644 --- a/crates/perry-runtime/src/node_submodules/stream_promises.rs +++ b/crates/perry-runtime/src/node_submodules/stream_promises.rs @@ -19,7 +19,7 @@ use crate::object::{ js_object_alloc, js_object_get_field_by_name_f64, js_object_set_field_by_name, ObjectHeader, }; use crate::string::js_string_from_bytes; -use crate::value::{JSValue, TAG_FALSE}; +use crate::value::JSValue; use std::os::raw::c_int; #[inline] @@ -114,10 +114,6 @@ pub(crate) fn options_signal(options: f64) -> Option { get_object_property(options, b"signal") } -fn option_is_false(options: f64, name: &[u8]) -> bool { - get_object_property(options, name).is_some_and(|value| value.to_bits() == TAG_FALSE) -} - pub(crate) fn signal_aborted(signal: f64) -> bool { get_object_property(signal, b"aborted").is_some_and(|v| crate::value::js_is_truthy(v) != 0) } @@ -574,6 +570,7 @@ fn catch_stream_promises_throw(call: impl FnOnce()) -> Result<(), f64> { } } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_streamP_pipeline( _closure: *const ClosureHeader, source: f64, @@ -618,6 +615,7 @@ pub(crate) extern "C" fn thunk_streamP_pipeline( promise_value } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_streamP_finished( _closure: *const ClosureHeader, stream: f64, diff --git a/crates/perry-runtime/src/node_submodules/test.rs b/crates/perry-runtime/src/node_submodules/test.rs index 978f59dbf8..219c40f3d3 100644 --- a/crates/perry-runtime/src/node_submodules/test.rs +++ b/crates/perry-runtime/src/node_submodules/test.rs @@ -1612,11 +1612,6 @@ fn reporter_with_kind(kind: i32, source: f64) -> f64 { readable_from_text(output) } -pub(crate) extern "C" fn thunk_reporter(closure: *const ClosureHeader, source: f64) -> f64 { - let kind = js_closure_get_capture_f64(closure, 0) as i32; - reporter_with_kind(kind, source) -} - pub(crate) extern "C" fn thunk_reporter_spec(_closure: *const ClosureHeader, source: f64) -> f64 { reporter_with_kind(REPORTER_SPEC, source) } diff --git a/crates/perry-runtime/src/node_submodules/tests.rs b/crates/perry-runtime/src/node_submodules/tests.rs index c577205e90..6b4e62f2bd 100644 --- a/crates/perry-runtime/src/node_submodules/tests.rs +++ b/crates/perry-runtime/src/node_submodules/tests.rs @@ -84,7 +84,7 @@ fn find_submodule_for_unknown_key_returns_none() { /// expose `tracingChannel` as a callable thunk in the SUBMODULES table /// so the namespace singleton's field is a function (not TAG_TRUE). #[test] -fn diagnostics_channel_exposes_tracingChannel_export() { +fn diagnostics_channel_exposes_tracing_channel_export() { let submod = find_submodule("diagnostics_channel").expect("diagnostics_channel must be in SUBMODULES"); let names: Vec<&str> = submod.exports.iter().map(|e| e.name).collect(); diff --git a/crates/perry-runtime/src/node_submodules/trace_events.rs b/crates/perry-runtime/src/node_submodules/trace_events.rs index 861ddd3b1c..e92d718bb6 100644 --- a/crates/perry-runtime/src/node_submodules/trace_events.rs +++ b/crates/perry-runtime/src/node_submodules/trace_events.rs @@ -204,7 +204,7 @@ fn trace_id_symbol() -> f64 { fn this_trace_id() -> i64 { let this_value = crate::object::js_implicit_this_get(); - let Some(obj) = object_ptr_from_value(this_value) else { + let Some(_) = object_ptr_from_value(this_value) else { throw_invalid_this(); }; let id_value = @@ -399,7 +399,7 @@ fn validate_categories_value(categories_value: f64) -> f64 { for idx in 0..len { let value = js_array_get_f64(categories, idx); - let Some(category) = string_from_value(value) else { + let Some(_) = string_from_value(value) else { let message = format!( "The \"options.categories[{}]\" property must be of type string. Received {}", idx, @@ -442,6 +442,7 @@ fn create_trace(categories: f64, source_categories: Vec) -> f64 { obj_value } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_trace_events_createTracing( _closure: *const ClosureHeader, options: f64, @@ -452,6 +453,7 @@ pub(crate) extern "C" fn thunk_trace_events_createTracing( create_trace(categories, categories_from_array(categories)) } +#[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn thunk_trace_events_getEnabledCategories( _closure: *const ClosureHeader, _arg: f64, diff --git a/crates/perry-runtime/src/node_submodules/zlib.rs b/crates/perry-runtime/src/node_submodules/zlib.rs index f05488bf88..0b90f04a13 100644 --- a/crates/perry-runtime/src/node_submodules/zlib.rs +++ b/crates/perry-runtime/src/node_submodules/zlib.rs @@ -291,13 +291,18 @@ pub extern "C" fn js_zlib_validate_callback(callback: f64) -> i64 { /// bitcode rebuild performed by auto-optimize (see /// `project_auto_optimize_keepalive_3320`). Called only from generated `.o` / /// `perry-ext-zlib`, so without an explicit anchor the dead-stripper drops it. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ZLIB_RESOLVE_LEVEL: extern "C" fn(f64) -> i32 = js_zlib_resolve_level; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ZLIB_VALIDATE_PARAMS: extern "C" fn(f64, f64) -> i32 = js_zlib_validate_params; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ZLIB_VALIDATE_OPTIONS: extern "C" fn(f64, i32) = js_zlib_validate_options; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ZLIB_VALIDATE_BUFFER_ARG: extern "C" fn(i64) = js_zlib_validate_buffer_arg; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ZLIB_VALIDATE_CALLBACK: extern "C" fn(f64) -> i64 = js_zlib_validate_callback; diff --git a/crates/perry-runtime/src/node_test.rs b/crates/perry-runtime/src/node_test.rs index 4d4afd5c52..b6d4c7e0da 100644 --- a/crates/perry-runtime/src/node_test.rs +++ b/crates/perry-runtime/src/node_test.rs @@ -56,43 +56,6 @@ extern "C" fn zero0(_closure: *const ClosureHeader) -> f64 { 0.0 } -extern "C" fn mock_fn_thunk( - _closure: *const ClosureHeader, - _implementation: f64, - _options: f64, -) -> f64 { - mock_function_value() -} - -extern "C" fn mock_property_thunk( - _closure: *const ClosureHeader, - _target: f64, - _property: f64, - _value: f64, -) -> f64 { - object_value(crate::object::js_object_alloc(0, 0)) -} - -fn legacy_node_test_mock_fn(_implementation: f64, _options: f64) -> f64 { - mock_function_value() -} - -fn legacy_node_test_mock_property(_target: f64, _property: f64, _value: f64) -> f64 { - object_value(crate::object::js_object_alloc(0, 0)) -} - -fn test_function_value(name: &str) -> f64 { - let value = fn_value(noop3 as *const u8, name, 3); - if matches!(name, "suite" | "describe" | "it") { - let closure_ptr = crate::value::js_nanbox_get_pointer(value) as usize; - for method in ["skip", "todo", "only"] { - let method_value = fn_value(noop3 as *const u8, method, 3); - crate::closure::closure_set_dynamic_prop(closure_ptr, method, method_value); - } - } - value -} - fn mock_context_object() -> *mut ObjectHeader { let obj = crate::object::js_object_alloc(CLASS_ID_MOCK_CONTEXT, 0); let calls = crate::array::js_array_alloc(0); @@ -129,60 +92,6 @@ fn mock_function_value() -> f64 { value } -fn mock_timers_object() -> *mut ObjectHeader { - let obj = crate::object::js_object_alloc(0, 0); - for name in ["enable", "reset", "tick", "runAll", "setTime"] { - set(obj, name, fn_value(noop1 as *const u8, name, 1)); - } - let dispose_fn = fn_value(noop0 as *const u8, "[Symbol.dispose]", 0); - set(obj, "@@__perry_wk_dispose", dispose_fn); - let dispose = crate::symbol::well_known_symbol("dispose"); - if !dispose.is_null() { - let obj_value = object_value(obj); - let symbol_value = f64::from_bits(JSValue::pointer(dispose as *const u8).bits()); - unsafe { - crate::symbol::js_object_set_symbol_property(obj_value, symbol_value, dispose_fn); - } - } - obj -} - -fn mock_tracker_object() -> *mut ObjectHeader { - let obj = crate::object::js_object_alloc(CLASS_ID_MOCK_TRACKER, 0); - set(obj, "fn", fn_value(mock_fn_thunk as *const u8, "fn", 2)); - set(obj, "method", fn_value(noop3 as *const u8, "method", 3)); - set(obj, "getter", fn_value(noop3 as *const u8, "getter", 3)); - set(obj, "setter", fn_value(noop3 as *const u8, "setter", 3)); - set( - obj, - "property", - fn_value(mock_property_thunk as *const u8, "property", 3), - ); - set(obj, "reset", fn_value(noop0 as *const u8, "reset", 0)); - set( - obj, - "restoreAll", - fn_value(noop0 as *const u8, "restoreAll", 0), - ); - set(obj, "timers", object_value(mock_timers_object())); - obj -} - -fn snapshot_object() -> *mut ObjectHeader { - let obj = crate::object::js_object_alloc(0, 0); - set( - obj, - "setDefaultSnapshotSerializers", - fn_value(noop1 as *const u8, "setDefaultSnapshotSerializers", 1), - ); - set( - obj, - "setResolveSnapshotPath", - fn_value(noop1 as *const u8, "setResolveSnapshotPath", 1), - ); - obj -} - pub fn property(property: &str) -> Option { match property { // #3719: `expectFailure` is a current Node `node:test` named export diff --git a/crates/perry-runtime/src/node_v8.rs b/crates/perry-runtime/src/node_v8.rs index 769e01f24c..a9ba037dc1 100644 --- a/crates/perry-runtime/src/node_v8.rs +++ b/crates/perry-runtime/src/node_v8.rs @@ -33,46 +33,65 @@ use crate::value::JSValue; // Symbol retention: these `#[no_mangle]` entry points are emitted only by // codegen's `node:v8` dispatch — no Rust caller references them, so the // auto-optimize whole-program-LLVM build would dead-strip them without an -// anchor (see node_stream_keepalive.rs). Pin each via a `#[cfg_attr(feature = "keepalive-anchors", used)]` static. -#[cfg_attr(feature = "keepalive-anchors", used)] +// anchor (see node_stream_keepalive.rs). Pin each via a `#[used]` static. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_SERIALIZE: extern "C" fn(f64) -> f64 = js_v8_serialize; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_DESERIALIZE: extern "C" fn(f64) -> f64 = js_v8_deserialize; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_HEAP_STATS: extern "C" fn() -> f64 = js_v8_get_heap_statistics; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_CODE_STATS: extern "C" fn() -> f64 = js_v8_get_heap_code_statistics; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_SPACE_STATS: extern "C" fn() -> f64 = js_v8_get_heap_space_statistics; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_VERSION_TAG: extern "C" fn() -> f64 = js_v8_cached_data_version_tag; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_GET_HEAP_SNAPSHOT: extern "C" fn(f64) -> f64 = js_v8_get_heap_snapshot; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_WRITE_HEAP_SNAPSHOT: extern "C" fn(f64, f64) -> f64 = js_v8_write_heap_snapshot; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_GC_PROFILER_NEW: extern "C" fn() -> f64 = js_v8_gc_profiler_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_GC_PROFILER_START: extern "C" fn(f64) -> f64 = js_v8_gc_profiler_start; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_GC_PROFILER_STOP: extern "C" fn(f64) -> f64 = js_v8_gc_profiler_stop; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_GC_PROFILER_REPORT: extern "C" fn() -> f64 = js_v8_gc_profiler_report; // #3680: Serializer / Deserializer class constructors. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_SERIALIZER_NEW: extern "C" fn(f64) -> f64 = js_v8_serializer_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_DESERIALIZER_NEW: extern "C" fn(f64) -> f64 = js_v8_deserializer_new; // #3679: lifecycle / diagnostic-control surface. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_NOOP_UNDEFINED: extern "C" fn() -> f64 = js_v8_noop_undefined; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_IS_BUILDING_SNAPSHOT: extern "C" fn() -> f64 = js_v8_is_building_snapshot; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_NAMESPACE: extern "C" fn(*const u8, usize) -> f64 = js_v8_namespace; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_THROW_NOT_BUILDING: extern "C" fn() -> f64 = js_v8_throw_not_building_snapshot; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_V8_PROMISE_HOOK_REGISTER: extern "C" fn() -> f64 = js_v8_promise_hook_register; const TAG_UNDEFINED_BITS: u64 = 0x7FFC_0000_0000_0001; @@ -405,7 +424,7 @@ pub(crate) fn v8_instance_id_from_value(val: f64) -> usize { if !jsv.is_pointer() { return 0; } - unsafe { + { let obj = (val.to_bits() & crate::value::POINTER_MASK) as *mut ObjectHeader; let f = crate::object::js_object_get_field(obj, 1); if f.is_number() { @@ -533,7 +552,7 @@ pub(crate) fn v8_deserializer_read_uint64(recv: f64) -> f64 { // Node returns `[hi, lo]`. let (hi, lo) = crate::child_process::v8_class_deserializer_read_uint64(v8_instance_id_from_value(recv)); - unsafe { + { let arr = crate::array::js_array_alloc(2); crate::array::js_array_push_f64(arr, hi as f64); crate::array::js_array_push_f64(arr, lo as f64); @@ -626,7 +645,7 @@ pub extern "C" fn js_v8_promise_hook_register() -> f64 { /// `new v8.GCProfiler()` → fresh profiler object. #[no_mangle] pub extern "C" fn js_v8_gc_profiler_new() -> f64 { - unsafe { + { let module = "v8.GCProfiler"; crate::object::install_native_module_vtable(); let obj = crate::object::js_object_alloc(crate::object::NATIVE_MODULE_CLASS_ID, 2); @@ -659,9 +678,7 @@ fn gc_profiler_object(recv: f64) -> Option<*mut ObjectHeader> { #[no_mangle] pub extern "C" fn js_v8_gc_profiler_start(recv: f64) -> f64 { if let Some(obj) = gc_profiler_object(recv) { - unsafe { - crate::object::js_object_set_field(obj, 1, JSValue::bool(true)); - } + crate::object::js_object_set_field(obj, 1, JSValue::bool(true)); } undefined() } @@ -672,13 +689,11 @@ pub extern "C" fn js_v8_gc_profiler_stop(recv: f64) -> f64 { let Some(obj) = gc_profiler_object(recv) else { return undefined(); }; - let started = unsafe { crate::object::js_object_get_field(obj, 1) }; + let started = crate::object::js_object_get_field(obj, 1); if !started.is_bool() || !started.as_bool() { return undefined(); } - unsafe { - crate::object::js_object_set_field(obj, 1, JSValue::bool(false)); - } + crate::object::js_object_set_field(obj, 1, JSValue::bool(false)); js_v8_gc_profiler_report() } diff --git a/crates/perry-runtime/src/object/alloc.rs b/crates/perry-runtime/src/object/alloc.rs index 7ac31a531e..e203eca884 100644 --- a/crates/perry-runtime/src/object/alloc.rs +++ b/crates/perry-runtime/src/object/alloc.rs @@ -569,7 +569,8 @@ pub extern "C" fn js_object_alloc_class_dynamic_parent( /// Keepalive anchor — `js_object_alloc_class_dynamic_parent` is a /// generated-code-only callee, so the auto-optimize whole-program build would /// otherwise dead-strip it (see the FFI-symbol-link-break class). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_OBJECT_ALLOC_CLASS_DYNAMIC_PARENT: extern "C" fn( u32, u32, diff --git a/crates/perry-runtime/src/object/assert.rs b/crates/perry-runtime/src/object/assert.rs index 88f243d71c..43a1b7a3df 100644 --- a/crates/perry-runtime/src/object/assert.rs +++ b/crates/perry-runtime/src/object/assert.rs @@ -394,7 +394,7 @@ fn make_assertion_error( js_register_class_extends_error(crate::error::CLASS_ID_ASSERTION_ERROR); }); let obj = js_object_alloc(crate::error::CLASS_ID_ASSERTION_ERROR, 8); - unsafe { + { let set = |key: &str, value: f64| { let key_ptr = crate::string::js_string_from_bytes(key.as_ptr(), key.len() as u32); js_object_set_field_by_name(obj, key_ptr, value); @@ -485,12 +485,6 @@ fn promise_value_from_ptr(promise: *mut crate::promise::Promise) -> f64 { f64::from_bits(crate::value::JSValue::pointer(promise as *const u8).bits()) } -fn fulfilled_promise(value: f64) -> *mut crate::promise::Promise { - let promise = crate::promise::js_promise_new(); - crate::promise::js_promise_resolve(promise, value); - promise -} - fn rejected_promise(reason: f64) -> *mut crate::promise::Promise { let promise = crate::promise::js_promise_new(); crate::promise::js_promise_reject(promise, reason); @@ -1219,7 +1213,7 @@ pub extern "C" fn js_assert_assertion_error_ctor(options: f64) -> f64 { ); crate::fs::validate::throw_type_error_with_code(&message, "ERR_INVALID_ARG_TYPE"); } - unsafe { + { let read = |key: &str| -> f64 { let key_ptr = crate::string::js_string_from_bytes(key.as_ptr(), key.len() as u32); let obj_ptr = diff --git a/crates/perry-runtime/src/object/class_constructors.rs b/crates/perry-runtime/src/object/class_constructors.rs index 0a1e075215..556840a331 100644 --- a/crates/perry-runtime/src/object/class_constructors.rs +++ b/crates/perry-runtime/src/object/class_constructors.rs @@ -108,7 +108,8 @@ pub extern "C" fn js_register_class_constructor_flags( } /// Keepalive anchor (generated-code-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_REGISTER_CLASS_CONSTRUCTOR_FLAGS: extern "C" fn(i64, i64, i64) = js_register_class_constructor_flags; @@ -162,7 +163,8 @@ pub unsafe extern "C" fn js_class_register_capture_values( /// Keepalive anchor for the auto-optimize whole-program build — /// `js_class_register_capture_values` is a generated-code-only callee. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLASS_REGISTER_CAPTURE_VALUES: unsafe extern "C" fn(u32, *const f64, usize) = js_class_register_capture_values; @@ -351,12 +353,15 @@ pub(crate) fn fallback_is_tag_stripped(fallback: f64) -> bool { } /// Keepalive anchors (generated-code-only callees). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLASS_CAPTURE_VALUE: extern "C" fn(u32, u32) -> f64 = js_class_capture_value; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_CLASS_CAPTURE_VALUE_OR: extern "C" fn(u32, u32, f64) -> f64 = js_class_capture_value_or; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PARAM_OR_CLASS_CAPTURE_VALUE: extern "C" fn(f64, u32, u32) -> f64 = js_param_or_class_capture_value; @@ -554,7 +559,8 @@ pub unsafe extern "C" fn js_super_construct_apply( } /// Keepalive anchor (generated-code-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SUPER_CONSTRUCT_APPLY: unsafe extern "C" fn(u32, f64, f64) -> f64 = js_super_construct_apply; @@ -719,7 +725,8 @@ unsafe fn call_displaced_native_base_method( } /// Keepalive anchor (generated-code-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SUPER_METHOD_CALL_DYNAMIC: unsafe extern "C" fn( u32, *const u8, @@ -776,7 +783,8 @@ pub unsafe extern "C" fn js_super_method_call_dynamic_apply( } /// Keepalive anchor (generated-code-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SUPER_METHOD_CALL_DYNAMIC_APPLY: unsafe extern "C" fn( u32, *const u8, @@ -932,7 +940,8 @@ pub unsafe extern "C" fn js_array_push_spread_any( } /// Keepalive anchor (generated-code-only callee). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ARRAY_PUSH_SPREAD_ANY: unsafe extern "C" fn( *mut crate::array::ArrayHeader, f64, @@ -1030,7 +1039,8 @@ pub unsafe extern "C" fn js_error_subclass_default_init( } /// Keepalive: generated code is the only caller (#6469). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ERROR_SUBCLASS_DEFAULT_INIT: unsafe extern "C" fn( f64, f64, diff --git a/crates/perry-runtime/src/object/class_registry.rs b/crates/perry-runtime/src/object/class_registry.rs index 2c647b29a8..5129b50dc3 100644 --- a/crates/perry-runtime/src/object/class_registry.rs +++ b/crates/perry-runtime/src/object/class_registry.rs @@ -79,6 +79,8 @@ pub use prototype_objects::{js_set_function_prototype, NEXT_SYNTHETIC_CLASS_ID}; // ── class_meta.rs ─────────────────────────────────────────────────────────── #[cfg(test)] pub(crate) use class_meta::test_text_encoding_stream_new_with_constructor; +#[cfg(feature = "global-text")] +pub(crate) use class_meta::text_decoder_bool_option; pub use class_meta::{ class_name_for_id, is_anon_shape_class_id, js_compression_stream_new, js_decompression_stream_new, js_register_anon_shape_class_id, js_register_class_id, @@ -86,15 +88,13 @@ pub use class_meta::{ js_text_encoding_stream_new, ANON_SHAPE_CLASS_IDS, CLASS_NAMES, }; pub(crate) use class_meta::{ - identify_global_builtin_constructor, report_dispatch_miss, text_decoder_bool_option, + identify_global_builtin_constructor, report_dispatch_miss, text_encoding_stream_new_with_constructor, validate_web_compression_stream_format, CLASS_ID_COMPRESSION_STREAM, CLASS_ID_DECOMPRESSION_STREAM, CLASS_ID_TEXT_DECODER_STREAM, CLASS_ID_TEXT_ENCODER_STREAM, }; #[cfg(test)] pub(crate) use prototype_methods::CLASS_PROTOTYPE_FAST_GUARDS_INVALIDATED; -#[cfg(test)] -pub(crate) use state::CLASS_DELETED_KEYS; // ── prototype_methods.rs ──────────────────────────────────────────────────── pub(crate) use prototype_methods::{ @@ -131,9 +131,8 @@ pub(crate) use gc_roots::{ test_class_prototype_method_root_bits, test_class_prototype_method_value_root_bits, test_class_prototype_object_root_addr, test_clear_class_side_table_roots, test_function_class_id_key_for_class, test_seed_class_dynamic_prop_root, - test_seed_class_parent_closure_root, test_seed_class_prototype_method_root, - test_seed_class_prototype_method_value_root, test_seed_class_prototype_object_root, - test_seed_function_class_id_key, + test_seed_class_prototype_method_root, test_seed_class_prototype_method_value_root, + test_seed_class_prototype_object_root, test_seed_function_class_id_key, }; // ── registration.rs ───────────────────────────────────────────────────────── diff --git a/crates/perry-runtime/src/object/class_registry/class_meta.rs b/crates/perry-runtime/src/object/class_registry/class_meta.rs index 3f2e34f827..7a2d5abd6f 100644 --- a/crates/perry-runtime/src/object/class_registry/class_meta.rs +++ b/crates/perry-runtime/src/object/class_registry/class_meta.rs @@ -318,7 +318,7 @@ pub(crate) fn identify_global_builtin_constructor(func_value: f64) -> Option<&'s } for name in GLOBAL_THIS_BUILTIN_CONSTRUCTORS.iter().copied() { let key = crate::string::js_string_from_bytes(name.as_ptr(), name.len() as u32); - let v = unsafe { js_object_get_field_by_name(global_obj, key) }; + let v = js_object_get_field_by_name(global_obj, key); if v.bits() == jv.bits() { return Some(name); } @@ -326,6 +326,7 @@ pub(crate) fn identify_global_builtin_constructor(func_value: f64) -> Option<&'s None } +#[cfg(feature = "global-text")] pub(crate) fn text_decoder_bool_option(options: f64, name: &str) -> f64 { let jsval = crate::value::JSValue::from_bits(options.to_bits()); if !jsval.is_pointer() { diff --git a/crates/perry-runtime/src/object/class_registry/dispatch.rs b/crates/perry-runtime/src/object/class_registry/dispatch.rs index 2c78272293..bc4869f687 100644 --- a/crates/perry-runtime/src/object/class_registry/dispatch.rs +++ b/crates/perry-runtime/src/object/class_registry/dispatch.rs @@ -249,7 +249,7 @@ pub(crate) unsafe fn call_vtable_method( // `(number).forEach is not a function`. The synthesized-`arguments` slot // holds ALL passed args; a user rest slot holds only args from the rest // position onward (so `method(a, ...rest)` keeps `a` positional). - let mut adjusted_args_storage: Option> = None; + let adjusted_args_storage: Option>; let (call_args_ptr, call_args_len) = if has_synthetic_arguments || has_rest { let visible_params = (param_count as usize).saturating_sub(1); let pack_start = if has_synthetic_arguments { diff --git a/crates/perry-runtime/src/object/class_registry/gc_roots.rs b/crates/perry-runtime/src/object/class_registry/gc_roots.rs index e3e903d2c5..6d1b60d90f 100644 --- a/crates/perry-runtime/src/object/class_registry/gc_roots.rs +++ b/crates/perry-runtime/src/object/class_registry/gc_roots.rs @@ -584,11 +584,6 @@ pub(crate) fn test_class_prototype_object_root_addr(class_id: u32) -> usize { .unwrap_or(0) } -#[cfg(test)] -pub(crate) fn test_seed_class_parent_closure_root(class_id: u32, addr: usize) { - class_parent_closure_root_store(class_id, addr); -} - #[cfg(test)] pub(crate) fn test_class_parent_closure_root_addr(class_id: u32) -> usize { CLASS_PARENT_CLOSURES diff --git a/crates/perry-runtime/src/object/class_registry/registration.rs b/crates/perry-runtime/src/object/class_registry/registration.rs index 6c40b6b38d..8db0b6f8b3 100644 --- a/crates/perry-runtime/src/object/class_registry/registration.rs +++ b/crates/perry-runtime/src/object/class_registry/registration.rs @@ -109,7 +109,7 @@ pub(crate) fn class_own_static_accessor_ptrs(class_id: u32, name: &str) -> Optio /// closure calling convention. The receiver comes from `IMPLICIT_THIS`, set /// by the method-call dispatch the closure value travels through. extern "C" fn class_accessor_getter_thunk(closure: *const crate::closure::ClosureHeader) -> f64 { - let raw = unsafe { crate::closure::js_closure_get_capture_ptr(closure, 0) } as usize; + let raw = crate::closure::js_closure_get_capture_ptr(closure, 0) as usize; if raw == 0 { return f64::from_bits(crate::value::TAG_UNDEFINED); } @@ -123,7 +123,7 @@ extern "C" fn class_accessor_setter_thunk( closure: *const crate::closure::ClosureHeader, value: f64, ) -> f64 { - let raw = unsafe { crate::closure::js_closure_get_capture_ptr(closure, 0) } as usize; + let raw = crate::closure::js_closure_get_capture_ptr(closure, 0) as usize; if raw == 0 { return f64::from_bits(crate::value::TAG_UNDEFINED); } @@ -156,7 +156,7 @@ pub(crate) fn class_accessor_function_value( if closure.is_null() { return f64::from_bits(crate::value::TAG_UNDEFINED); } - unsafe { crate::closure::js_closure_set_capture_ptr(closure, 0, raw_ptr as i64) }; + crate::closure::js_closure_set_capture_ptr(closure, 0, raw_ptr as i64); // Spec `.length`: params before the first default/rest. A getter takes no // params (0); a setter takes exactly one formal param — but `set m(x = 42)` // has `.length === 0` (defaults don't count). Codegen registers the raw @@ -176,9 +176,7 @@ pub(crate) fn class_accessor_function_value( let fn_name = format!("{prefix}{prop_name}"); let name_ptr = crate::string::js_string_from_bytes(fn_name.as_ptr(), fn_name.len() as u32); let name_value = f64::from_bits(crate::value::JSValue::string_ptr(name_ptr).bits()); - unsafe { - crate::closure::closure_set_dynamic_prop(closure as usize, "name", name_value); - } + crate::closure::closure_set_dynamic_prop(closure as usize, "name", name_value); crate::object::set_builtin_property_attrs( closure as usize, "name".to_string(), @@ -291,11 +289,13 @@ pub unsafe extern "C" fn js_register_class_static_setter( // These two are only ever called from codegen-emitted module-init IR (no Rust // caller), so the auto-optimize whole-program-LLVM build would dead-strip them -// without an anchor. Pin each via a `#[cfg_attr(feature = "keepalive-anchors", used)]` static (mirrors node_v8.rs). -#[cfg_attr(feature = "keepalive-anchors", used)] +// without an anchor. Pin each via a `#[used]` static (mirrors node_v8.rs). +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REGISTER_STATIC_GETTER: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_getter; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REGISTER_STATIC_SETTER: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_setter; @@ -328,7 +328,8 @@ pub unsafe extern "C" fn js_register_class_method_bind_length( .insert((class_id as u32, name), length as u32); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REGISTER_METHOD_BIND_LENGTH: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_method_bind_length; @@ -361,7 +362,8 @@ pub unsafe extern "C" fn js_register_class_static_method_bind_length( .insert((class_id as u32, name), length as u32); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REGISTER_STATIC_METHOD_BIND_LENGTH: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_method_bind_length; diff --git a/crates/perry-runtime/src/object/delete_rest.rs b/crates/perry-runtime/src/object/delete_rest.rs index 42ffaa685b..ffed89360a 100644 --- a/crates/perry-runtime/src/object/delete_rest.rs +++ b/crates/perry-runtime/src/object/delete_rest.rs @@ -608,7 +608,7 @@ mod sso_tests_1781 { /// vacuously") and the property stayed put. #[test] fn delete_dynamic_removes_property_via_sso_key() { - unsafe { + { let obj = crate::object::js_object_alloc(0, 0); let key = crate::string::js_string_from_bytes(b"id".as_ptr(), 2); crate::object::js_object_set_field_by_name(obj, key, 42.0); diff --git a/crates/perry-runtime/src/object/descriptors.rs b/crates/perry-runtime/src/object/descriptors.rs index 67a53b02f2..4b9a50f512 100644 --- a/crates/perry-runtime/src/object/descriptors.rs +++ b/crates/perry-runtime/src/object/descriptors.rs @@ -1079,8 +1079,8 @@ pub extern "C" fn js_object_get_own_property_names(obj_value: f64) -> f64 { if !obj_ptr.is_null() { // Armed ops table (see `nm_namespace_hooks`): namespace key // enumeration links only when a namespace can exist. - if let Some(arr) = super::nm_namespace_ops() - .and_then(|ops| unsafe { (ops.own_keys_array)(obj_ptr) }) + if let Some(arr) = + super::nm_namespace_ops().and_then(|ops| (ops.own_keys_array)(obj_ptr)) { return f64::from_bits((arr as u64) | 0x7FFD_0000_0000_0000); } @@ -1548,7 +1548,8 @@ pub extern "C" fn js_object_create_with_props(proto_value: f64, props_value: f64 result } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_OBJECT_CREATE_WITH_PROPS: extern "C" fn(f64, f64) -> f64 = js_object_create_with_props; /// `Object.getOwnPropertyDescriptor` handling for native-module namespace diff --git a/crates/perry-runtime/src/object/field_get_set/enumeration.rs b/crates/perry-runtime/src/object/field_get_set/enumeration.rs index 8a729dbf03..82cde1f4ad 100644 --- a/crates/perry-runtime/src/object/field_get_set/enumeration.rs +++ b/crates/perry-runtime/src/object/field_get_set/enumeration.rs @@ -76,9 +76,7 @@ pub extern "C" fn js_object_keys_value(value: f64) -> *mut ArrayHeader { if jv.is_any_string() { let mut scratch = [0u8; crate::value::SHORT_STRING_MAX_LEN]; let len = match crate::string::str_bytes_from_jsvalue(value, &mut scratch) { - Some((ptr, blen)) if !ptr.is_null() => unsafe { - crate::string::compute_utf16_len(ptr, blen) - }, + Some((ptr, blen)) if !ptr.is_null() => crate::string::compute_utf16_len(ptr, blen), _ => 0, }; let arr = crate::array::js_array_alloc(len.max(1)); @@ -93,9 +91,7 @@ pub extern "C" fn js_object_keys_value(value: f64) -> *mut ArrayHeader { if let Some((_, payload)) = crate::builtins::boxed_primitive_payload(value) { let mut scratch = [0u8; crate::value::SHORT_STRING_MAX_LEN]; let len = match crate::string::str_bytes_from_jsvalue(payload, &mut scratch) { - Some((ptr, blen)) if !ptr.is_null() => unsafe { - crate::string::compute_utf16_len(ptr, blen) - }, + Some((ptr, blen)) if !ptr.is_null() => crate::string::compute_utf16_len(ptr, blen), _ => 0, }; let arr = crate::array::js_array_alloc(len.max(1)); diff --git a/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs b/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs index f8b5c0f679..c7e10d73f3 100644 --- a/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs +++ b/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs @@ -1423,7 +1423,7 @@ mod null_key_guard_5972 { /// SIGSEGV by dereferencing `(*key).byte_len` at offset 4. #[test] fn null_key_returns_undefined_not_segfault() { - unsafe { + { let obj = crate::object::js_object_alloc(0, 0); let key = crate::string::js_string_from_bytes(b"present".as_ptr(), 7); crate::object::js_object_set_field_by_name(obj, key, 42.0); diff --git a/crates/perry-runtime/src/object/field_get_set/has_property.rs b/crates/perry-runtime/src/object/field_get_set/has_property.rs index 63aca8e399..c52a850534 100644 --- a/crates/perry-runtime/src/object/field_get_set/has_property.rs +++ b/crates/perry-runtime/src/object/field_get_set/has_property.rs @@ -563,7 +563,7 @@ pub extern "C" fn js_object_has_property(obj: f64, key: f64) -> f64 { // (bounds) and the own/inherited members property-get can resolve. if crate::buffer::is_registered_buffer(obj_addr as usize) { let buf = obj_addr as *const crate::buffer::BufferHeader; - let len = unsafe { crate::buffer::js_buffer_length(buf) }; + let len = crate::buffer::js_buffer_length(buf); if key_val.is_int32() { let idx = key_val.as_int32(); return if idx >= 0 && idx < len { @@ -1283,7 +1283,6 @@ pub(crate) unsafe fn native_module_own_field_by_key( // accelerator, never authoritative — and a scan hit back-fills the map so // interleaved appends stay amortized O(1). pub(crate) const WIDE_KEY_INDEX_MIN_KEYS: usize = 257; -const WIDE_KEY_INDEX_CAPACITY: usize = 4; // #6759 C1: the wide-object key index folded into the shape records // (`object::shapes`, keyed on keys_array identity, unbounded — the old diff --git a/crates/perry-runtime/src/object/field_get_set/ic_miss.rs b/crates/perry-runtime/src/object/field_get_set/ic_miss.rs index 0a2dfdfb5a..30b2675d60 100644 --- a/crates/perry-runtime/src/object/field_get_set/ic_miss.rs +++ b/crates/perry-runtime/src/object/field_get_set/ic_miss.rs @@ -599,7 +599,7 @@ mod sso_tests_1781 { /// SSO lookup key lets js_string_equals match). #[test] fn in_operator_finds_object_key_via_sso_lookup() { - unsafe { + { let obj = crate::object::js_object_alloc(0, 0); let key = crate::string::js_string_from_bytes(b"id".as_ptr(), 2); crate::object::js_object_set_field_by_name(obj, key, 42.0); diff --git a/crates/perry-runtime/src/object/global_this.rs b/crates/perry-runtime/src/object/global_this.rs index 2d4512caa4..7227ccc1b2 100644 --- a/crates/perry-runtime/src/object/global_this.rs +++ b/crates/perry-runtime/src/object/global_this.rs @@ -1,5 +1,20 @@ //! `globalThis` singleton plus built-in constructor/namespace population. +#![cfg_attr( + not(any( + feature = "global-math", + feature = "global-json", + feature = "global-reflect", + feature = "global-atomics", + feature = "global-url", + feature = "global-text", + feature = "global-websocket", + feature = "global-webcrypto", + feature = "global-webfetch" + )), + allow(dead_code, unused_imports) +)] + use super::*; #[path = "global_this_webassembly.rs"] diff --git a/crates/perry-runtime/src/object/global_this/array_error.rs b/crates/perry-runtime/src/object/global_this/array_error.rs index 240a458050..797eb821ff 100644 --- a/crates/perry-runtime/src/object/global_this/array_error.rs +++ b/crates/perry-runtime/src/object/global_this/array_error.rs @@ -696,6 +696,7 @@ pub(crate) extern "C" fn array_prototype_sort_thunk( /// keeps the closure call convention independent of the spec `.length`. macro_rules! array_proto_arraylike_cb_thunk { ($name:ident, $engine:path) => { + #[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn $name(_c: *const crate::closure::ClosureHeader, rest: f64) -> f64 { let this = crate::object::js_implicit_this_get(); let args = global_this_rest_array_values(rest); @@ -732,6 +733,7 @@ array_proto_arraylike_cb_thunk!( macro_rules! array_proto_arraylike_optarg_thunk { ($name:ident, $engine:path) => { + #[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn $name(_c: *const crate::closure::ClosureHeader, rest: f64) -> f64 { let this = crate::object::js_implicit_this_get(); let args = global_this_rest_array_values(rest); @@ -757,6 +759,7 @@ fn reduce_right_engine(recv: f64, cb: f64, has_init: i32, init: f64) -> f64 { macro_rules! array_proto_arraylike_search_thunk { ($name:ident, $engine:path) => { + #[allow(non_snake_case)] // thunk name mirrors JS API surface pub(crate) extern "C" fn $name(_c: *const crate::closure::ClosureHeader, rest: f64) -> f64 { let this = crate::object::js_implicit_this_get(); let args = global_this_rest_array_values(rest); diff --git a/crates/perry-runtime/src/object/global_this/bigint_promise.rs b/crates/perry-runtime/src/object/global_this/bigint_promise.rs index 05cf938f9d..907f295314 100644 --- a/crates/perry-runtime/src/object/global_this/bigint_promise.rs +++ b/crates/perry-runtime/src/object/global_this/bigint_promise.rs @@ -272,7 +272,7 @@ fn bigint_to_bigint_arg(value: f64) -> f64 { if crate::array::js_array_is_array(value).to_bits() == TAG_TRUE_BITS { let arr_ptr = jv.as_pointer::(); let comma = crate::string::js_string_from_bytes(b",".as_ptr(), 1); - let joined = unsafe { crate::array::js_array_join(arr_ptr, comma) }; + let joined = crate::array::js_array_join(arr_ptr, comma); return bigint_to_bigint_arg(crate::value::js_nanbox_string(joined as i64)); } // Object: ToPrimitive("number") then re-coerce. Try a custom @@ -723,7 +723,7 @@ pub(crate) extern "C" fn typed_array_from_thunk( ) }); let ta_ptr = addr as *mut crate::typedarray::TypedArrayHeader; - let target_len = unsafe { crate::typedarray::js_typed_array_length(ta_ptr) } as usize; + let target_len = crate::typedarray::js_typed_array_length(ta_ptr) as usize; if target_len < len { super::super::object_ops::throw_object_type_error( b"Derived TypedArray constructor created an array which was too small", @@ -803,7 +803,7 @@ fn typed_array_create_from_values( ) }); let ta_ptr = addr as *mut crate::typedarray::TypedArrayHeader; - let target_len = unsafe { crate::typedarray::js_typed_array_length(ta_ptr) } as usize; + let target_len = crate::typedarray::js_typed_array_length(ta_ptr) as usize; if target_len < len { // `TypedArrayCreate(C, «len»)` throws a *TypeError* (not RangeError) // when the constructed typed array is shorter than the requested length diff --git a/crates/perry-runtime/src/object/global_this/builtin_thunks.rs b/crates/perry-runtime/src/object/global_this/builtin_thunks.rs index d6c80c5163..ff6bd8ea21 100644 --- a/crates/perry-runtime/src/object/global_this/builtin_thunks.rs +++ b/crates/perry-runtime/src/object/global_this/builtin_thunks.rs @@ -503,7 +503,8 @@ extern "C" fn depd_wrapfunction_outer_thunk( fn_v } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_FUNCTION_CTOR_FROM_STRINGS: extern "C" fn(*const f64, usize) -> f64 = js_function_ctor_from_strings; diff --git a/crates/perry-runtime/src/object/global_this/fetch_globals.rs b/crates/perry-runtime/src/object/global_this/fetch_globals.rs index ac0fcf7d47..aea21e48b4 100644 --- a/crates/perry-runtime/src/object/global_this/fetch_globals.rs +++ b/crates/perry-runtime/src/object/global_this/fetch_globals.rs @@ -43,9 +43,10 @@ pub extern "C" fn js_module_top_this() -> f64 { /// Keepalive anchor: `js_module_top_this` is referenced only from /// codegen-generated `.o` files, so the auto-optimize whole-program LLVM -/// rebuild would dead-strip it without this `#[cfg_attr(feature = "keepalive-anchors", used)]` pin (see +/// rebuild would dead-strip it without this `#[used]` pin (see /// project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_TOP_THIS: extern "C" fn() -> f64 = js_module_top_this; /// Issue #611: lazily allocate `globalThis` for computed global access. @@ -488,10 +489,12 @@ pub extern "C" fn js_response_subclass_init(this_box: f64, body: f64, init: f64) // `Expr::SuperCall` Request/Response arm); pin them so the auto-optimize // bitcode rebuild's dead-strip can't drop them (see // project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_REQUEST_SUBCLASS_INIT: extern "C" fn(f64, f64, f64) -> f64 = js_request_subclass_init; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_RESPONSE_SUBCLASS_INIT: extern "C" fn(f64, f64, f64) -> f64 = js_response_subclass_init; @@ -840,7 +843,8 @@ pub unsafe extern "C" fn js_fetch_or_value_super( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_FETCH_OR_VALUE_SUPER: unsafe extern "C" fn(f64, f64, *const f64, usize) -> f64 = js_fetch_or_value_super; diff --git a/crates/perry-runtime/src/object/groupby.rs b/crates/perry-runtime/src/object/groupby.rs index 411669369c..2d1caa41ec 100644 --- a/crates/perry-runtime/src/object/groupby.rs +++ b/crates/perry-runtime/src/object/groupby.rs @@ -136,9 +136,11 @@ pub extern "C" fn js_map_group_by(items_value: f64, callback: f64) -> f64 { /// Keepalive anchors: these `#[no_mangle]` helpers are only called from /// codegen-emitted `.o`. The auto-optimize whole-program LLVM rebuild /// dead-strips unreferenced `#[no_mangle]` symbols (see #3320), so pin them. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_OBJECT_GROUP_BY: extern "C" fn(f64, f64) -> f64 = js_object_group_by; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_MAP_GROUP_BY: extern "C" fn(f64, f64) -> f64 = js_map_group_by; /// Returns true if `value` is a Symbol (registered SymbolHeader pointer). diff --git a/crates/perry-runtime/src/object/map_set_subclass.rs b/crates/perry-runtime/src/object/map_set_subclass.rs index 2cbb42cfe8..cd5deec60d 100644 --- a/crates/perry-runtime/src/object/map_set_subclass.rs +++ b/crates/perry-runtime/src/object/map_set_subclass.rs @@ -171,80 +171,78 @@ pub(crate) fn super_collection_method(this_value: f64, name: &str, args: &[f64]) let this_handle = scope.root_nanbox_f64(this_value); let boxed = |ptr: i64| f64::from_bits(JSValue::pointer(ptr as *const u8).bits()); let boolean = |b: bool| f64::from_bits(JSValue::bool(b).bits()); - unsafe { - match backing { - CollectionBacking::Map(map) => match name { - "get" => Some(crate::map::js_map_get(map, *args.first()?)), - "set" => { - let key = *args.first()?; - let value = args.get(1).copied().unwrap_or(undefined); - crate::map::js_map_set(map, key, value); - Some(this_handle.get_nanbox_f64()) - } - "has" => Some(boolean(crate::map::js_map_has(map, *args.first()?) != 0)), - "delete" => Some(boolean(crate::map::js_map_delete(map, *args.first()?) != 0)), - "clear" => { - crate::map::js_map_clear(map); - Some(undefined) - } - "forEach" => { - let callback = *args.first()?; - let this_arg = args.get(1).copied().unwrap_or(undefined); - // The callback's 3rd argument must be the SUBCLASS instance, - // not the backing — same receiver-identity rule the ordinary - // dispatch path applies. - crate::map::js_map_foreach_with_collection( - map, - callback, - this_arg, - this_handle.get_nanbox_f64(), - ); - Some(undefined) - } - "keys" => Some(boxed(crate::collection_iter_object::js_map_keys_iter_obj( + match backing { + CollectionBacking::Map(map) => match name { + "get" => Some(crate::map::js_map_get(map, *args.first()?)), + "set" => { + let key = *args.first()?; + let value = args.get(1).copied().unwrap_or(undefined); + crate::map::js_map_set(map, key, value); + Some(this_handle.get_nanbox_f64()) + } + "has" => Some(boolean(crate::map::js_map_has(map, *args.first()?) != 0)), + "delete" => Some(boolean(crate::map::js_map_delete(map, *args.first()?) != 0)), + "clear" => { + crate::map::js_map_clear(map); + Some(undefined) + } + "forEach" => { + let callback = *args.first()?; + let this_arg = args.get(1).copied().unwrap_or(undefined); + // The callback's 3rd argument must be the SUBCLASS instance, + // not the backing — same receiver-identity rule the ordinary + // dispatch path applies. + crate::map::js_map_foreach_with_collection( map, - ))), - "values" => Some(boxed( - crate::collection_iter_object::js_map_values_iter_obj(map), - )), - "entries" | "Symbol.iterator" | "@@iterator" => Some(boxed( - crate::collection_iter_object::js_map_entries_iter_obj(map), - )), - _ => None, - }, - CollectionBacking::Set(set) => match name { - "add" => { - crate::set::js_set_add(set, *args.first()?); - Some(this_handle.get_nanbox_f64()) - } - "has" => Some(boolean(crate::set::js_set_has(set, *args.first()?) != 0)), - "delete" => Some(boolean(crate::set::js_set_delete(set, *args.first()?) != 0)), - "clear" => { - crate::set::js_set_clear(set); - Some(undefined) - } - "forEach" => { - let callback = *args.first()?; - let this_arg = args.get(1).copied().unwrap_or(undefined); - crate::set::js_set_foreach_with_collection( - set, - callback, - this_arg, - this_handle.get_nanbox_f64(), - ); - Some(undefined) - } - // `Set.prototype.keys` is an alias of `values`, and the default - // iterator is `values` — matching the builtin. - "keys" | "values" | "Symbol.iterator" | "@@iterator" => Some(boxed( - crate::collection_iter_object::js_set_values_iter_obj(set), - )), - "entries" => Some(boxed( - crate::collection_iter_object::js_set_entries_iter_obj(set), - )), - _ => None, - }, - } + callback, + this_arg, + this_handle.get_nanbox_f64(), + ); + Some(undefined) + } + "keys" => Some(boxed(crate::collection_iter_object::js_map_keys_iter_obj( + map, + ))), + "values" => Some(boxed( + crate::collection_iter_object::js_map_values_iter_obj(map), + )), + "entries" | "Symbol.iterator" | "@@iterator" => Some(boxed( + crate::collection_iter_object::js_map_entries_iter_obj(map), + )), + _ => None, + }, + CollectionBacking::Set(set) => match name { + "add" => { + crate::set::js_set_add(set, *args.first()?); + Some(this_handle.get_nanbox_f64()) + } + "has" => Some(boolean(crate::set::js_set_has(set, *args.first()?) != 0)), + "delete" => Some(boolean(crate::set::js_set_delete(set, *args.first()?) != 0)), + "clear" => { + crate::set::js_set_clear(set); + Some(undefined) + } + "forEach" => { + let callback = *args.first()?; + let this_arg = args.get(1).copied().unwrap_or(undefined); + crate::set::js_set_foreach_with_collection( + set, + callback, + this_arg, + this_handle.get_nanbox_f64(), + ); + Some(undefined) + } + // `Set.prototype.keys` is an alias of `values`, and the default + // iterator is `values` — matching the builtin. + "keys" | "values" | "Symbol.iterator" | "@@iterator" => Some(boxed( + crate::collection_iter_object::js_set_values_iter_obj(set), + )), + "entries" => Some(boxed( + crate::collection_iter_object::js_set_entries_iter_obj(set), + )), + _ => None, + }, } } diff --git a/crates/perry-runtime/src/object/mod.rs b/crates/perry-runtime/src/object/mod.rs index 083b528b34..ee060e360b 100644 --- a/crates/perry-runtime/src/object/mod.rs +++ b/crates/perry-runtime/src/object/mod.rs @@ -68,7 +68,6 @@ pub(crate) use native_module::{class_prototype_ref_id, SYMBOL_BOUND_METHOD_NAME} mod native_module_crypto_key_object; mod native_module_crypto_random; mod native_module_dispatch; -mod native_module_dispatch_crypto; mod native_module_registry; pub(crate) use native_module_registry::js_nm_enable_install_all; pub(crate) use native_module_registry::nm_ctor_lookup; diff --git a/crates/perry-runtime/src/object/native_call_method/common_methods.rs b/crates/perry-runtime/src/object/native_call_method/common_methods.rs index 6600d9539a..f43833aef7 100644 --- a/crates/perry-runtime/src/object/native_call_method/common_methods.rs +++ b/crates/perry-runtime/src/object/native_call_method/common_methods.rs @@ -248,7 +248,6 @@ pub(super) unsafe fn dispatch_common( let key_value = crate::object::js_to_property_key(key_value); let key_value = root_scope.root_nanbox_f64(key_value).get_nanbox_f64(); let object = object_handle.get_nanbox_f64(); - let jsval = JSValue::from_bits(object.to_bits()); // Symbol keys must not be string-coerced — route through the // canonical entry, which consults the SYMBOL_PROPERTIES side // table (mirrors hasOwnProperty's symbol arm). diff --git a/crates/perry-runtime/src/object/native_module.rs b/crates/perry-runtime/src/object/native_module.rs index 9857ceee76..71a60986c1 100644 --- a/crates/perry-runtime/src/object/native_module.rs +++ b/crates/perry-runtime/src/object/native_module.rs @@ -585,7 +585,6 @@ pub(crate) fn cjs_default_export_value(module_name: &str) -> Option { b"process".as_ptr(), "process".len(), )), - "module" => Some(bound_native_callable_export_value("module", "Module")), "async_hooks" | "child_process" | "constants" | "dns" | "dns/promises" | "node-pty" | "os" | "path" | "path.posix" | "path.win32" | "punycode" | "querystring" | "repl" | "sea" | "url" | "util" | "inspector" | "inspector/promises" => { @@ -1099,7 +1098,8 @@ pub extern "C" fn js_class_method_bind_by_id(instance: f64, method_id: i64) -> f js_class_method_bind(instance, name_ref.ptr, name_ref.len) } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_CLASS_METHOD_BIND_BY_ID: extern "C" fn(f64, i64) -> f64 = js_class_method_bind_by_id; /// Allocate a BOUND_METHOD closure binding `instance` as the receiver for the diff --git a/crates/perry-runtime/src/object/native_module/callable_export_check.rs b/crates/perry-runtime/src/object/native_module/callable_export_check.rs index f762a59e35..e5e86c235a 100644 --- a/crates/perry-runtime/src/object/native_module/callable_export_check.rs +++ b/crates/perry-runtime/src/object/native_module/callable_export_check.rs @@ -156,7 +156,6 @@ pub(crate) fn is_native_module_callable_export_reference(module: &str, prop: &st | ("http", "_connectionListener") | ("module", "Module") | ("module", "createRequire") - | ("module", "Module") | ("module", "findPackageJSON") | ("module", "findSourceMap") | ("module", "flushCompileCache") @@ -494,7 +493,6 @@ pub(crate) fn is_native_module_callable_export_reference(module: &str, prop: &st | ("fs", "unlinkSync") | ("fs", "utimes") | ("fs", "utimesSync") - | ("fs", "_toUnixTimestamp") | ("fs", "watch") | ("fs", "watchFile") | ("fs", "unwatchFile") @@ -908,15 +906,6 @@ pub(crate) fn is_native_module_callable_export_reference(module: &str, prop: &st | ("zlib", "createUnzip") | ("zlib", "createBrotliCompress") | ("zlib", "createBrotliDecompress") - | ("zlib", "Deflate") - | ("zlib", "DeflateRaw") - | ("zlib", "Gzip") - | ("zlib", "Gunzip") - | ("zlib", "Inflate") - | ("zlib", "InflateRaw") - | ("zlib", "Unzip") - | ("zlib", "BrotliCompress") - | ("zlib", "BrotliDecompress") // #2533: node:http/https/http2 server factories read as callable // values so `const createServer = createServerHTTP` (and // `@hono/node-server`'s `options.createServer || createServerHTTP`) diff --git a/crates/perry-runtime/src/object/native_module/callable_exports.rs b/crates/perry-runtime/src/object/native_module/callable_exports.rs index 2b27ad5ab8..3607051746 100644 --- a/crates/perry-runtime/src/object/native_module/callable_exports.rs +++ b/crates/perry-runtime/src/object/native_module/callable_exports.rs @@ -370,22 +370,7 @@ fn native_callable_export_arity_reference(module: &str, prop: &str) -> Option Some(0), ("http", "get") => Some(3), - ( - "stream", - "isDestroyed" - | "isDisturbed" - | "isErrored" - | "isReadable" - | "isWritable" - | "getDefaultHighWaterMark" - | "_isArrayBufferView" - | "_isUint8Array" - | "_uint8ArrayToBuffer", - ) => Some(1), - ("stream", "finished") => Some(3), - ("stream", "addAbortSignal" | "destroy" | "setDefaultHighWaterMark") => Some(2), - ("stream", "compose" | "pipeline") => Some(0), - ("stream", "duplexPair") => Some(1), + ("stream", "destroy") => Some(2), // #3712: node:http module-level helper exports. ("http", "validateHeaderName" | "validateHeaderValue") => Some(2), ("http", "setMaxIdleHTTPParsers" | "setGlobalProxyFromEnv") => Some(1), @@ -431,7 +416,6 @@ fn native_callable_export_arity_reference(module: &str, prop: &str) -> Option Some(18), ("fs", "mkdtempDisposableSync") => Some(2), ("fs", "openAsBlob") => Some(1), - ("fs", "_toUnixTimestamp") => Some(1), ("events", "init") => Some(1), ("repl", "Recoverable") => Some(1), ("repl", "REPLServer" | "start") => Some(6), @@ -450,7 +434,6 @@ fn native_callable_export_arity_reference(module: &str, prop: &str) -> Option Some(0), ("module", "getCompileCacheDir") => Some(0), ("module", "getSourceMapsSupport") => Some(0), - ("module", "Module") => Some(0), ("module", "_findPath") => Some(3), ("module", "_initPaths") => Some(0), ("module", "_load") => Some(3), @@ -1405,21 +1388,6 @@ pub(crate) fn timers_promises_parent_namespace() -> f64 { }) } -extern "C" fn util_debuglog_logger_thunk( - _closure: *const crate::closure::ClosureHeader, - _arg: f64, -) -> f64 { - f64::from_bits(crate::value::TAG_UNDEFINED) -} - -pub(crate) fn util_debuglog_logger_value() -> f64 { - let func_ptr = util_debuglog_logger_thunk as *const u8; - crate::closure::js_register_closure_arity(func_ptr, 1); - let closure = crate::closure::js_closure_alloc_singleton(func_ptr); - set_bound_native_closure_name(closure, "debuglog"); - crate::value::js_nanbox_pointer(closure as i64) -} - fn attach_tty_stream_prototype(constructor_value: f64, name: &str) { crate::tty::attach_tty_constructor_prototype(constructor_value, name); } diff --git a/crates/perry-runtime/src/object/native_module/constants.rs b/crates/perry-runtime/src/object/native_module/constants.rs index 9dca91a65a..8438e0bd9c 100644 --- a/crates/perry-runtime/src/object/native_module/constants.rs +++ b/crates/perry-runtime/src/object/native_module/constants.rs @@ -401,39 +401,6 @@ pub(crate) unsafe fn get_native_module_constant( // Required by axios for its stream wiring. let zlib_const = zlib_const_lookup; - let dns_const = |prop: &str| -> Option { - Some(match prop { - "ADDRCONFIG" => 1024.0, - "V4MAPPED" => 2048.0, - "ALL" => 256.0, - "NODATA" => str_val("ENODATA"), - "FORMERR" => str_val("EFORMERR"), - "SERVFAIL" => str_val("ESERVFAIL"), - "NOTFOUND" => str_val("ENOTFOUND"), - "NOTIMP" => str_val("ENOTIMP"), - "REFUSED" => str_val("EREFUSED"), - "BADQUERY" => str_val("EBADQUERY"), - "BADNAME" => str_val("EBADNAME"), - "BADFAMILY" => str_val("EBADFAMILY"), - "BADRESP" => str_val("EBADRESP"), - "CONNREFUSED" => str_val("ECONNREFUSED"), - "TIMEOUT" => str_val("ETIMEOUT"), - "EOF" => str_val("EOF"), - "FILE" => str_val("EFILE"), - "NOMEM" => str_val("ENOMEM"), - "DESTRUCTION" => str_val("EDESTRUCTION"), - "BADSTR" => str_val("EBADSTR"), - "BADFLAGS" => str_val("EBADFLAGS"), - "NONAME" => str_val("ENONAME"), - "BADHINTS" => str_val("EBADHINTS"), - "NOTINITIALIZED" => str_val("ENOTINITIALIZED"), - "LOADIPHLPAPI" => str_val("ELOADIPHLPAPI"), - "ADDRGETNETWORKPARAMS" => str_val("EADDRGETNETWORKPARAMS"), - "CANCELLED" => str_val("ECANCELLED"), - _ => return None, - }) - }; - let sqlite_const = sqlite_const_lookup; match module_name { @@ -785,10 +752,6 @@ pub(crate) unsafe fn get_native_module_constant( "Stream" => Some(bound_native_callable_export_value("net", "Socket")), _ => None, }, - "timers" => match property { - "promises" => Some(timers_promises_parent_namespace()), - _ => None, - }, "timers/promises" => match property { "setTimeout" | "setImmediate" | "setInterval" => Some(unsafe { crate::node_submodules::js_node_submodule_namespace_member( @@ -1007,7 +970,6 @@ pub(crate) unsafe fn get_native_module_constant( }, #[cfg(feature = "mod-http2-constants")] "http2.constants" => crate::node_http2_constants::constant(property), - "dns" => dns_const(property), // node:cluster — primary-side settings and Worker handles are backed // by `crate::cluster`; scheduling/identity constants remain static. "cluster" => crate::cluster::cluster_property(property), @@ -1019,7 +981,7 @@ pub(crate) unsafe fn get_native_module_constant( "perf_histogram" => match property { "mean" | "min" | "max" | "stddev" | "exceeds" | "count" => Some(0.0), "percentiles" | "percentilesBigInt" => { - let obj = unsafe { js_object_alloc(0, 0) }; + let obj = js_object_alloc(0, 0); Some(f64::from_bits(JSValue::pointer(obj as *const u8).bits())) } _ => None, diff --git a/crates/perry-runtime/src/object/native_module/module_keys.rs b/crates/perry-runtime/src/object/native_module/module_keys.rs index e33a89213a..864a507a60 100644 --- a/crates/perry-runtime/src/object/native_module/module_keys.rs +++ b/crates/perry-runtime/src/object/native_module/module_keys.rs @@ -1083,103 +1083,6 @@ const INSPECTOR_NETWORK_KEYS: &[&[u8]] = &[ b"webSocketHandshakeResponseReceived", ]; -const FS_NAMESPACE_KEYS: &[&[u8]] = &[ - b"_toUnixTimestamp", - b"access", - b"accessSync", - b"appendFile", - b"appendFileSync", - b"chmod", - b"chmodSync", - b"chown", - b"chownSync", - b"close", - b"closeSync", - b"constants", - b"copyFile", - b"copyFileSync", - b"cp", - b"cpSync", - b"createReadStream", - b"createWriteStream", - b"exists", - b"existsSync", - b"fchmod", - b"fchmodSync", - b"fchown", - b"fchownSync", - b"fdatasync", - b"fdatasyncSync", - b"fstat", - b"fstatSync", - b"fsync", - b"fsyncSync", - b"ftruncate", - b"ftruncateSync", - b"futimes", - b"futimesSync", - b"glob", - b"globSync", - b"lchmod", - b"lchmodSync", - b"lchown", - b"lchownSync", - b"link", - b"linkSync", - b"lstat", - b"lstatSync", - b"lutimes", - b"lutimesSync", - b"mkdir", - b"mkdirSync", - b"mkdtemp", - b"mkdtempSync", - b"open", - b"openSync", - b"opendir", - b"opendirSync", - b"promises", - b"read", - b"readFile", - b"readFileSync", - b"readSync", - b"readdir", - b"readdirSync", - b"readlink", - b"readlinkSync", - b"readv", - b"readvSync", - b"realpath", - b"realpathSync", - b"rename", - b"renameSync", - b"rm", - b"rmSync", - b"rmdir", - b"rmdirSync", - b"stat", - b"statSync", - b"statfs", - b"statfsSync", - b"symlink", - b"symlinkSync", - b"truncate", - b"truncateSync", - b"unlink", - b"unlinkSync", - b"unwatchFile", - b"utimes", - b"utimesSync", - b"watch", - b"watchFile", - b"write", - b"writeFile", - b"writeFileSync", - b"writeSync", - b"writev", - b"writevSync", -]; - const URL_DEFAULT_KEYS: &[&[u8]] = &[ b"Url", b"parse", @@ -1705,7 +1608,6 @@ pub(crate) fn native_module_enumerable_keys(module_name: &str) -> Option<&'stati "path" => Some(PATH_NAMESPACE_KEYS), "path.default" | "path.posix.default" | "path.win32.default" => Some(PATH_DEFAULT_KEYS), "path.posix" | "path.win32" => Some(PATH_NAMESPACE_KEYS), - "fs" => Some(FS_NAMESPACE_KEYS), "constants" => Some(deprecated_constants_namespace_keys()), "constants.default" => Some(deprecated_constants_keys()), "dns" => Some(DNS_NAMESPACE_KEYS), @@ -1919,18 +1821,6 @@ pub(crate) fn native_module_enumerable_keys(module_name: &str) -> Option<&'stati VM_NAMESPACE_KEYS }), "vm.constants" => Some(VM_CONSTANTS_KEYS), - // Plain `timers` was missing — `require('node:timers').setImmediate` - // read undefined (Next.js's fast-set-immediate extension reads and - // patches it at module init). - "timers" => Some(&[ - b"setTimeout", - b"clearTimeout", - b"setInterval", - b"clearInterval", - b"setImmediate", - b"clearImmediate", - b"promises", - ]), "timers/promises" => Some(&[b"setTimeout", b"setImmediate", b"setInterval", b"scheduler"]), "readline/promises" => Some(&[b"Interface", b"Readline", b"createInterface"]), "zlib" => Some(&[b"codes"]), diff --git a/crates/perry-runtime/src/object/native_module_dispatch/dispatch_d_i.rs b/crates/perry-runtime/src/object/native_module_dispatch/dispatch_d_i.rs index 4a66e8b3c0..37e4df6884 100644 --- a/crates/perry-runtime/src/object/native_module_dispatch/dispatch_d_i.rs +++ b/crates/perry-runtime/src/object/native_module_dispatch/dispatch_d_i.rs @@ -289,7 +289,6 @@ pub(crate) unsafe fn nm_dispatch_fs(ctx: &NmCtx, module_name: &str, method_name: ("fs", "utimesSync") => crate::fs::js_fs_utimes_sync(arg(0), arg(1), arg(2)) as f64, ("fs", "lutimesSync") => crate::fs::js_fs_lutimes_sync(arg(0), arg(1), arg(2)) as f64, ("fs", "futimesSync") => crate::fs::js_fs_futimes_sync(arg(0), arg(1), arg(2)) as f64, - ("fs", "_toUnixTimestamp") => crate::fs::js_fs_to_unix_timestamp(arg(0)), ("fs", "readvSync") => crate::fs::js_fs_readv_sync(arg(0), arg(1), arg(2)), ("fs", "writevSync") => crate::fs::js_fs_writev_sync(arg(0), arg(1), arg(2)), ("fs", "statfsSync") => crate::fs::js_fs_statfs_sync_options(arg(0), arg(1)), diff --git a/crates/perry-runtime/src/object/native_module_dispatch/dispatch_m_p.rs b/crates/perry-runtime/src/object/native_module_dispatch/dispatch_m_p.rs index 3c3dc26b93..681d1c45b9 100644 --- a/crates/perry-runtime/src/object/native_module_dispatch/dispatch_m_p.rs +++ b/crates/perry-runtime/src/object/native_module_dispatch/dispatch_m_p.rs @@ -808,10 +808,6 @@ pub(crate) unsafe fn nm_dispatch_process(ctx: &NmCtx, module_name: &str, method_ crate::process::js_process_umask_set(mask) } } - ("process", "emitWarning") => { - crate::process::js_process_emit_warning(arg(0), arg(1), arg(2)); - f64::from_bits(crate::value::TAG_UNDEFINED) - } ("process", "hrtime") => crate::os::js_process_hrtime(arg(0)), ("process", "cpuUsage") => crate::process::js_process_cpu_usage(arg(0)), // ── crypto module ── diff --git a/crates/perry-runtime/src/object/native_module_dispatch_crypto.rs b/crates/perry-runtime/src/object/native_module_dispatch_crypto.rs deleted file mode 100644 index 1b2194e07c..0000000000 --- a/crates/perry-runtime/src/object/native_module_dispatch_crypto.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Crypto helpers used by native module method dispatch. - -use crate::JSValue; - -fn crypto_dispatch_value_addr(value: f64) -> usize { - let bits = value.to_bits(); - if (bits >> 48) >= 0x7FF8 { - (bits & 0x0000_FFFF_FFFF_FFFF) as usize - } else { - bits as usize - } -} - -fn crypto_random_fill_number_arg(value: f64, name: &str) -> Option { - let js = JSValue::from_bits(value.to_bits()); - if js.is_undefined() { - return None; - } - if !js.is_number() && !js.is_int32() { - let message = format!( - "The \"{}\" argument must be of type number. Received {}", - name, - crate::fs::validate::describe_received(value) - ); - crate::fs::validate::throw_type_error_with_code(&message, "ERR_INVALID_ARG_TYPE"); - } - Some(if js.is_int32() { - js.as_int32() as f64 - } else { - value - }) -} - -fn crypto_random_fill_range(total: usize, offset_bits: f64, size_bits: f64) -> (usize, usize) { - let offset = match crypto_random_fill_number_arg(offset_bits, "offset") { - Some(n) if n.is_finite() && n >= 0.0 && n <= total as f64 => n as usize, - Some(n) => { - let message = format!( - "The value of \"offset\" is out of range. It must be >= 0 && <= {}. Received {}", - total, n - ); - crate::fs::validate::throw_range_error_with_code(&message); - } - None => 0, - }; - let size = match crypto_random_fill_number_arg(size_bits, "size") { - Some(n) if n.is_finite() && n >= 0.0 && n <= i32::MAX as f64 => n as usize, - Some(n) => { - let message = format!( - "The value of \"size\" is out of range. It must be >= 0 && <= 2147483647. Received {}", - n - ); - crate::fs::validate::throw_range_error_with_code(&message); - } - None => total.saturating_sub(offset), - }; - let end = offset.saturating_add(size); - if end > total { - let message = format!( - "The value of \"size + offset\" is out of range. It must be <= {}. Received {}", - total, end - ); - crate::fs::validate::throw_range_error_with_code(&message); - } - (offset, size) -} - -fn crypto_random_fill_invalid_buf(value: f64) -> ! { - let message = format!( - "The \"buf\" argument must be an instance of Buffer, TypedArray, DataView, or ArrayBuffer. Received {}", - crate::fs::validate::describe_received(value) - ); - crate::fs::validate::throw_type_error_with_code(&message, "ERR_INVALID_ARG_TYPE") -} - -pub(super) unsafe fn crypto_random_fill_sync_dispatch( - target: f64, - offset_bits: f64, - size_bits: f64, -) -> f64 { - use rand::Rng; - - let addr = crypto_dispatch_value_addr(target); - if crate::typedarray::lookup_typed_array_kind(addr).is_some() { - let ta = addr as *mut crate::typedarray::TypedArrayHeader; - if let Some(data) = crate::typedarray::typed_array_bytes_mut(ta) { - let elem_size = (*ta).elem_size as usize; - let len = if elem_size == 0 { - 0 - } else { - data.len() / elem_size - }; - let (start_elem, count_elem) = crypto_random_fill_range(len, offset_bits, size_bits); - let start = start_elem.saturating_mul(elem_size); - let end = start - .saturating_add(count_elem.saturating_mul(elem_size)) - .min(data.len()); - if end > start { - rand::rng().fill_bytes(&mut data[start..end]); - } - return target; - } - crypto_random_fill_invalid_buf(target); - } - if crate::buffer::is_registered_buffer(addr) { - let buf = addr as *mut crate::buffer::BufferHeader; - let total = (*buf).length as usize; - let (start, count) = crypto_random_fill_range(total, offset_bits, size_bits); - if count > 0 { - let data = crate::buffer::buffer_data_mut(buf); - rand::rng().fill_bytes(std::slice::from_raw_parts_mut(data.add(start), count)); - } - return target; - } - crypto_random_fill_invalid_buf(target); -} diff --git a/crates/perry-runtime/src/object/native_this_alias.rs b/crates/perry-runtime/src/object/native_this_alias.rs index 2f8466de5a..db96bda318 100644 --- a/crates/perry-runtime/src/object/native_this_alias.rs +++ b/crates/perry-runtime/src/object/native_this_alias.rs @@ -290,9 +290,11 @@ pub unsafe extern "C" fn js_https_server_construct_with_this( /// Keepalive anchors: the auto-optimize whole-program LLVM rebuild /// dead-strips `#[no_mangle]` fns referenced only from generated `.o` /// files. See the `KEEP_JS_FUNCTION_BIND` precedent in closure/dispatch.rs. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_HTTP_SERVER_CONSTRUCT_WITH_THIS: unsafe extern "C" fn(f64, f64, f64) -> f64 = js_http_server_construct_with_this; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_HTTPS_SERVER_CONSTRUCT_WITH_THIS: unsafe extern "C" fn(f64, f64, f64) -> f64 = js_https_server_construct_with_this; diff --git a/crates/perry-runtime/src/object/object_ops.rs b/crates/perry-runtime/src/object/object_ops.rs index 802fa84fd0..b4ddf8705a 100644 --- a/crates/perry-runtime/src/object/object_ops.rs +++ b/crates/perry-runtime/src/object/object_ops.rs @@ -39,7 +39,7 @@ pub(crate) use descriptor_helpers::{ registered_buffer_index_own_property_present, throw_object_type_error, throw_object_type_error_with_suffix, try_decode_descriptor, validate_nonconfigurable_redefine, validate_property_descriptor, validate_property_descriptor_view, value_is_object_like, - DescView, DESC_CONFIGURABLE, DESC_ENUMERABLE, DESC_GET, DESC_SET, DESC_VALUE, DESC_WRITABLE, + DESC_CONFIGURABLE, DESC_ENUMERABLE, DESC_GET, DESC_SET, DESC_VALUE, DESC_WRITABLE, }; // Module-private `unsafe fn value_is_callable` (descriptor_helpers): used by the // object_ops children (`accessors.rs`, `descriptor_helpers.rs`) but NOT diff --git a/crates/perry-runtime/src/object/object_ops/define_properties.rs b/crates/perry-runtime/src/object/object_ops/define_properties.rs index 052852f0eb..f48a0807b1 100644 --- a/crates/perry-runtime/src/object/object_ops/define_properties.rs +++ b/crates/perry-runtime/src/object/object_ops/define_properties.rs @@ -24,8 +24,7 @@ pub extern "C" fn js_object_define_properties(target: f64, descriptors: f64) -> let target_is_class_ref = super::super::class_ref_id(target).is_some(); let target_is_handle = { let jv = crate::value::JSValue::from_bits(target.to_bits()); - jv.is_pointer() - && crate::value::addr_class::is_small_handle(unsafe { jv.as_pointer::() } as usize) + jv.is_pointer() && crate::value::addr_class::is_small_handle(jv.as_pointer::() as usize) }; if !target_is_class_ref && !target_is_handle && !unsafe { value_is_object_like(target) } { throw_object_type_error(b"Object.defineProperties called on non-object"); @@ -61,9 +60,9 @@ pub extern "C" fn js_object_define_properties(target: f64, descriptors: f64) -> crate::value::js_nanbox_get_pointer(names_value) as *const crate::array::ArrayHeader; let mut keys: Vec = Vec::new(); if !names_arr.is_null() { - let len = unsafe { crate::array::js_array_length(names_arr) } as usize; + let len = crate::array::js_array_length(names_arr) as usize; for i in 0..len { - let k = unsafe { crate::array::js_array_get(names_arr, i as u32) }; + let k = crate::array::js_array_get(names_arr, i as u32); let k_f64 = f64::from_bits(k.bits()); // Skip non-enumerable own keys (spec step: descriptor must be // enumerable). `propertyIsEnumerable` returns false for absent or @@ -104,8 +103,6 @@ pub extern "C" fn js_object_define_properties(target: f64, descriptors: f64) -> target } -const TAG_UNDEFINED_LOCAL: u64 = 0x7FFC_0000_0000_0001; - /// Coerce an arbitrary key value (f64 — usually a STRING_TAG NaN-box) to a /// `*const StringHeader` for use with `js_object_get_field_by_name_f64`. /// Returns null if the value isn't string-like. diff --git a/crates/perry-runtime/src/object/object_ops/has_own.rs b/crates/perry-runtime/src/object/object_ops/has_own.rs index 253b636e25..554066ff51 100644 --- a/crates/perry-runtime/src/object/object_ops/has_own.rs +++ b/crates/perry-runtime/src/object/object_ops/has_own.rs @@ -696,6 +696,7 @@ pub extern "C" fn js_object_property_is_enumerable(obj_value: f64, key_value: f6 } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROPERTY_IS_ENUMERABLE: extern "C" fn(f64, f64) -> f64 = js_object_property_is_enumerable; diff --git a/crates/perry-runtime/src/object/tests.rs b/crates/perry-runtime/src/object/tests.rs index 6fb1e467ad..d48c6bb7a4 100644 --- a/crates/perry-runtime/src/object/tests.rs +++ b/crates/perry-runtime/src/object/tests.rs @@ -268,7 +268,7 @@ fn recorded_prototype_constructor_overrides_plain_object_constructor() { #[test] fn closure_name_and_length_ignore_plain_assignment() { crate::closure::test_clear_closure_side_tables(); - unsafe { + { let closure = crate::closure::js_closure_alloc( crate::object::global_this_builtin_noop_thunk as *const u8, 0, @@ -302,7 +302,7 @@ fn closure_name_and_length_ignore_plain_assignment() { #[test] fn closure_name_can_be_redefined_with_define_property() { crate::closure::test_clear_closure_side_tables(); - unsafe { + { let closure = crate::closure::js_closure_alloc( crate::object::global_this_builtin_noop_thunk as *const u8, 0, @@ -610,7 +610,7 @@ fn text_encoding_stream_globals_construct_readable_writable_shape() { #[test] fn navigator_global_constructor_identity_shape() { - unsafe { + { let ctor_raw = test_global_this_builtin_constructor_value("Navigator"); let ctor = JSValue::from_bits(ctor_raw.to_bits()); assert!(ctor.is_pointer()); @@ -763,7 +763,7 @@ fn transition_cache_lookup_rejects_grown_shared_target() { fn entries_and_values_skip_non_enumerable_descriptor_slots() { // #5046: Object.defineProperty(o, 'hidden', { value: 1 }) defaults to // enumerable: false. Object.keys filtered it; entries/values did not. - unsafe { + { let obj = js_object_alloc(0, 0); let hidden_key = crate::string::js_string_from_bytes(b"hidden".as_ptr(), 6); let shown_key = crate::string::js_string_from_bytes(b"shown".as_ptr(), 5); @@ -809,7 +809,7 @@ fn entries_and_values_skip_non_enumerable_descriptor_slots() { /// the dynamic-write fast path must still respect descriptors installed later. #[test] fn wide_object_index_reads_and_descriptor_writes() { - unsafe { + { let obj = js_object_alloc(0, 0); let n = 600u32; for i in 0..n { diff --git a/crates/perry-runtime/src/object/this_binding.rs b/crates/perry-runtime/src/object/this_binding.rs index 6f3c52d9ff..7f854a9735 100644 --- a/crates/perry-runtime/src/object/this_binding.rs +++ b/crates/perry-runtime/src/object/this_binding.rs @@ -70,7 +70,8 @@ pub(crate) fn static_this_disarm() { /// checks on subclass receivers throw (test262 static-private-method- /// subclass-receiver). // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_STATIC_THIS_ARM_CLASSREF: extern "C" fn(u32) = js_static_this_arm_classref; #[no_mangle] @@ -85,7 +86,8 @@ pub extern "C" fn js_static_this_arm_classref(class_id: u32) { /// class-ref expression and the method resolves on a parent class at compile /// time) right before the direct call. // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_STATIC_THIS_ARM_VALUE: extern "C" fn(f64) = js_static_this_arm_value; #[no_mangle] @@ -96,7 +98,8 @@ pub extern "C" fn js_static_this_arm_value(value: f64) { /// Static-method prologue `this` resolution: take the armed override if any, /// else the lexical class-ref the codegen passes in. // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_STATIC_THIS_RESOLVE: extern "C" fn(f64) -> f64 = js_static_this_resolve; #[no_mangle] diff --git a/crates/perry-runtime/src/object/typed_array_define.rs b/crates/perry-runtime/src/object/typed_array_define.rs index 03db375217..69df6f6b6a 100644 --- a/crates/perry-runtime/src/object/typed_array_define.rs +++ b/crates/perry-runtime/src/object/typed_array_define.rs @@ -219,7 +219,6 @@ pub(crate) unsafe fn typed_array_define_own_property( // Symbol / non-string / non-canonical key → ordinary define handles it. return TypedArrayDefineOutcome::NotTypedArray; }; - let addr = addr_handle.get_raw_mut_ptr::() as usize; let descriptor_value = desc_handle.get_nanbox_f64(); // Canonical numeric index → integer-indexed branch. From here every path diff --git a/crates/perry-runtime/src/object/util_types.rs b/crates/perry-runtime/src/object/util_types.rs index dff5100cb5..aa2237dbee 100644 --- a/crates/perry-runtime/src/object/util_types.rs +++ b/crates/perry-runtime/src/object/util_types.rs @@ -172,11 +172,9 @@ pub extern "C" fn js_util_types_is_promise(value: f64) -> f64 { let v = JSValue::from_bits(value.to_bits()); nanbox_bool( v.is_pointer() - && unsafe { - crate::promise::js_is_promise( - v.as_pointer::() as *mut crate::promise::Promise - ) != 0 - }, + && crate::promise::js_is_promise( + v.as_pointer::() as *mut crate::promise::Promise + ) != 0, ) } diff --git a/crates/perry-runtime/src/object/websocket_global.rs b/crates/perry-runtime/src/object/websocket_global.rs index 0ed2c03e1d..2860bc5cfb 100644 --- a/crates/perry-runtime/src/object/websocket_global.rs +++ b/crates/perry-runtime/src/object/websocket_global.rs @@ -45,6 +45,7 @@ pub(super) fn install_constructor_shape( } } +#[cfg(feature = "global-websocket")] pub(super) fn install_proto_methods(proto_obj: *mut ObjectHeader) { use super::global_this::install_proto_method; install_proto_method( diff --git a/crates/perry-runtime/src/object/with_env.rs b/crates/perry-runtime/src/object/with_env.rs index eccb2c3594..f5e22a9f99 100644 --- a/crates/perry-runtime/src/object/with_env.rs +++ b/crates/perry-runtime/src/object/with_env.rs @@ -124,9 +124,11 @@ pub extern "C" fn js_with_implicit_unset() -> f64 { } // #1561-style force-keep: only generated IR calls these. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_WITH_IMPLICIT_UNSET: extern "C" fn() -> f64 = js_with_implicit_unset; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_WITH_IMPLICIT_READ: extern "C" fn(f64, f64) -> f64 = js_with_implicit_read; /// `name` arrives as a NaN-boxed string (codegen lowers `Expr::String` args @@ -160,7 +162,7 @@ pub extern "C" fn js_with_implicit_read(value: f64, name: f64) -> f64 { let gj = JSValue::from_bits(g_handle.get_heap_word_u64()); let gptr = (gj.bits() & crate::value::POINTER_MASK) as *const ObjectHeader; if !gptr.is_null() && !key.is_null() { - let v = unsafe { crate::object::js_object_get_field_by_name(gptr, key) }; + let v = crate::object::js_object_get_field_by_name(gptr, key); return f64::from_bits(v.bits()); } } diff --git a/crates/perry-runtime/src/os.rs b/crates/perry-runtime/src/os.rs index 0ad783b345..f16bc9e99d 100644 --- a/crates/perry-runtime/src/os.rs +++ b/crates/perry-runtime/src/os.rs @@ -1285,7 +1285,8 @@ fn options_request_buffer(opts_bits: i64) -> bool { read_event_name(enc_ptr).as_deref() == Some("buffer") } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_OS_USER_INFO_OPTIONS: extern "C" fn(i64) -> *mut ObjectHeader = js_os_user_info_options; fn js_os_user_info_impl(buffer_encoding: bool) -> *mut ObjectHeader { diff --git a/crates/perry-runtime/src/path.rs b/crates/perry-runtime/src/path.rs index 80b3dcd644..8c03e41e5e 100644 --- a/crates/perry-runtime/src/path.rs +++ b/crates/perry-runtime/src/path.rs @@ -465,6 +465,10 @@ fn join_win32_paths(base: &str, tail: &str) -> String { } } +pub(crate) fn resolve_win32_str(path_str: &str) -> String { + win32_resolve_inner(path_str) +} + fn win32_resolve_inner(path_str: &str) -> String { let split = split_win32(path_str); if split.is_absolute { @@ -497,10 +501,6 @@ fn win32_resolve_inner(path_str: &str) -> String { normalize_win32_str(&path) } -pub(crate) fn resolve_win32_str(path_str: &str) -> String { - win32_resolve_inner(path_str) -} - /// Get directory name from path — Node's `path.posix.dirname`, which is purely /// lexical. The previous implementation delegated to Rust's `Path::parent()`, /// whose OS path semantics normalize components away and disagree with Node @@ -829,10 +829,12 @@ pub extern "C" fn js_path_win32_relative_checked(from_f64: f64, to_f64: f64) -> /// Keepalive anchors: these are emitted only from generated code, so the /// whole-program auto-optimize bitcode pass would otherwise dead-strip them. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PATH_RELATIVE_CHECKED: extern "C" fn(f64, f64) -> *mut StringHeader = js_path_relative_checked; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PATH_WIN32_RELATIVE_CHECKED: extern "C" fn(f64, f64) -> *mut StringHeader = js_path_win32_relative_checked; diff --git a/crates/perry-runtime/src/perf_hooks.rs b/crates/perry-runtime/src/perf_hooks.rs index c44b58e868..b2a98bc30e 100644 --- a/crates/perry-runtime/src/perf_hooks.rs +++ b/crates/perry-runtime/src/perf_hooks.rs @@ -77,7 +77,7 @@ const UV_METRICS_INFO_SHAPE: u32 = 0x7FFF_FF51; const UV_METRICS_INFO_KEYS: &[u8] = b"loopCount\0events\0eventsWaiting\0"; #[derive(Clone)] -struct PerfEntry { +pub(crate) struct PerfEntry { name: String, entry_type: u8, start_time: f64, @@ -234,8 +234,7 @@ pub fn performance_namespace() -> f64 { return f64::from_bits(cached); } let module = b"perf_hooks"; - let ns = - unsafe { crate::object::js_create_native_module_namespace(module.as_ptr(), module.len()) }; + let ns = crate::object::js_create_native_module_namespace(module.as_ptr(), module.len()); PERFORMANCE_NS.with(|c| c.set(ns.to_bits())); ns } @@ -1246,13 +1245,9 @@ fn entry_type_code(name: &str) -> Option { /// Read the registry index out of a `perf_observer` namespace object value's /// field[1]. pub fn observer_id_from_value(obs_val: f64) -> usize { - unsafe { - match as_object_ptr(obs_val) { - Some(obj) => { - observer_id_from_field(crate::object::js_object_get_field(obj as *mut _, 1)) - } - None => 0, - } + match as_object_ptr(obs_val) { + Some(obj) => observer_id_from_field(crate::object::js_object_get_field(obj as *mut _, 1)), + None => 0, } } @@ -1424,7 +1419,7 @@ fn schedule_flush() { return; } FLUSH_SCHEDULED.with(|f| f.set(true)); - unsafe { + { let closure = crate::closure::js_closure_alloc_singleton(js_perf_observer_flush_all as *const u8); crate::timer::js_set_timeout_callback(closure as i64, 0.0); @@ -1445,7 +1440,7 @@ pub extern "C" fn js_perf_observer_flush_all( .collect() }); for (cb_bits, obj_bits, entries) in work { - unsafe { + { CURRENT_LIST.with(|c| *c.borrow_mut() = entries); let module = b"perf_observer_list"; let list = @@ -1464,7 +1459,7 @@ pub extern "C" fn js_perf_observer_flush_all( /// Build an array from the in-flight observer `list` entries (for the /// `perf_observer_list` namespace methods). -pub unsafe fn current_list_to_array(filter: impl Fn(&PerfEntry) -> bool) -> f64 { +pub(crate) unsafe fn current_list_to_array(filter: impl Fn(&PerfEntry) -> bool) -> f64 { let mut snapshot: Vec = CURRENT_LIST.with(|c| c.borrow().iter().filter(|e| filter(e)).cloned().collect()); sort_entries_by_start_time(&mut snapshot); @@ -1496,7 +1491,7 @@ pub unsafe fn current_list_get_by_name(name_val: f64) -> f64 { /// Build the `PerformanceObserver.supportedEntryTypes` array. #[no_mangle] pub extern "C" fn js_perf_supported_entry_types() -> f64 { - unsafe { + { let mut arr = crate::array::js_array_alloc(4); for t in ["function", "mark", "measure", "resource"] { arr = crate::array::js_array_push(arr, str_value(t)); @@ -1638,7 +1633,7 @@ mod sso_tests_1781 { /// `"mark"` must still filter to the mark entry (site #509). #[test] fn get_entries_by_name_filters_on_sso_type() { - unsafe { + { let undef = f64::from_bits(crate::value::TAG_UNDEFINED); let name = JSValue::string_ptr(crate::string::js_string_from_bytes(b"phase".as_ptr(), 5)); diff --git a/crates/perry-runtime/src/plugin.rs b/crates/perry-runtime/src/plugin.rs index 936fe9362f..7cbf601c68 100644 --- a/crates/perry-runtime/src/plugin.rs +++ b/crates/perry-runtime/src/plugin.rs @@ -641,9 +641,7 @@ pub extern "C" fn perry_plugin_unregister_service(api_handle: i64, name: f64) -> reg.services.remove(idx); } else { drop(reg); - unsafe { - crate::closure::js_closure_call0(stop_ptr); - } + crate::closure::js_closure_call0(stop_ptr); let mut reg = REGISTRY.lock().unwrap(); if let Some(idx2) = reg .services @@ -1316,7 +1314,7 @@ mod unregister_tests { let handle = fresh_api_handle(); let hook_name = unsafe { make_nanboxed_string("beforeSave") }; let target_handler = f64::from_bits(0xAAAA_0001_0001_u64); - let _ = unsafe { perry_plugin_unregister_hook(handle, hook_name, target_handler) }; + let _ = perry_plugin_unregister_hook(handle, hook_name, target_handler); let reg = REGISTRY.lock().unwrap(); let hooks = reg @@ -1330,7 +1328,7 @@ mod unregister_tests { // Second call with the remaining handler — bucket should now be empty // and the parent map entry pruned. let other_handler = f64::from_bits(0xAAAA_0001_0002_u64); - let _ = unsafe { perry_plugin_unregister_hook(handle, hook_name, other_handler) }; + let _ = perry_plugin_unregister_hook(handle, hook_name, other_handler); let reg = REGISTRY.lock().unwrap(); assert!( reg.hooks.get("beforeSave").is_none(), @@ -1348,7 +1346,7 @@ mod unregister_tests { let hook_name = unsafe { make_nanboxed_string("beforeSave") }; let bogus_handler = f64::from_bits(0xDEAD_BEEF_DEAD_BEEF_u64); - let _ = unsafe { perry_plugin_unregister_hook(handle, hook_name, bogus_handler) }; + let _ = perry_plugin_unregister_hook(handle, hook_name, bogus_handler); let reg = REGISTRY.lock().unwrap(); let hooks = reg.hooks.get("beforeSave").unwrap(); @@ -1369,7 +1367,7 @@ mod unregister_tests { let hook_name = unsafe { make_nanboxed_string("beforeSave") }; let target = f64::from_bits(0xAAAA_0001_0001_u64); - let _ = unsafe { perry_plugin_unregister_hook(handle_2, hook_name, target) }; + let _ = perry_plugin_unregister_hook(handle_2, hook_name, target); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.hooks.get("beforeSave").unwrap().len(), 2); @@ -1383,7 +1381,7 @@ mod unregister_tests { seed(); let handle = fresh_api_handle(); let name = unsafe { make_nanboxed_string("formatCode") }; - let _ = unsafe { perry_plugin_unregister_tool(handle, name) }; + let _ = perry_plugin_unregister_tool(handle, name); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.tools.len(), 1, "only the matching tool is removed"); @@ -1398,7 +1396,7 @@ mod unregister_tests { seed(); let handle = fresh_api_handle(); let bogus = unsafe { make_nanboxed_string("doesNotExist") }; - let _ = unsafe { perry_plugin_unregister_tool(handle, bogus) }; + let _ = perry_plugin_unregister_tool(handle, bogus); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.tools.len(), 2); @@ -1412,7 +1410,7 @@ mod unregister_tests { seed(); let handle = fresh_api_handle(); let path = unsafe { make_nanboxed_string("/api/foo") }; - let _ = unsafe { perry_plugin_unregister_route(handle, path) }; + let _ = perry_plugin_unregister_route(handle, path); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.routes.len(), 0); @@ -1436,7 +1434,7 @@ mod unregister_tests { svc.stop_fn = 0; } } - let _ = unsafe { perry_plugin_unregister_service(handle, name) }; + let _ = perry_plugin_unregister_service(handle, name); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.services.len(), 0, "worker service removed"); @@ -1451,7 +1449,7 @@ mod unregister_tests { let handle = fresh_api_handle(); let event = unsafe { make_nanboxed_string("dataUpdated") }; let handler = f64::from_bits(0xEEEE_0001_0001_u64); - let _ = unsafe { perry_plugin_off(handle, event, handler) }; + let _ = perry_plugin_off(handle, event, handler); let reg = REGISTRY.lock().unwrap(); assert!( @@ -1470,14 +1468,14 @@ mod unregister_tests { // plugin_id_for_handle → every unregister path's body is skipped. let ghost_handle: i64 = 9999; let hook_name = unsafe { make_nanboxed_string("beforeSave") }; - let _ = unsafe { perry_plugin_unregister_hook(ghost_handle, hook_name, 0.0) }; + let _ = perry_plugin_unregister_hook(ghost_handle, hook_name, 0.0); let name = unsafe { make_nanboxed_string("formatCode") }; - let _ = unsafe { perry_plugin_unregister_tool(ghost_handle, name) }; + let _ = perry_plugin_unregister_tool(ghost_handle, name); let path = unsafe { make_nanboxed_string("/api/foo") }; - let _ = unsafe { perry_plugin_unregister_route(ghost_handle, path) }; - let _ = unsafe { perry_plugin_unregister_service(ghost_handle, name) }; + let _ = perry_plugin_unregister_route(ghost_handle, path); + let _ = perry_plugin_unregister_service(ghost_handle, name); let event = unsafe { make_nanboxed_string("dataUpdated") }; - let _ = unsafe { perry_plugin_off(ghost_handle, event, 0.0) }; + let _ = perry_plugin_off(ghost_handle, event, 0.0); let reg = REGISTRY.lock().unwrap(); assert_eq!(reg.hooks.get("beforeSave").unwrap().len(), 2); diff --git a/crates/perry-runtime/src/pointer_event.rs b/crates/perry-runtime/src/pointer_event.rs index 8263801684..a2a9d958e9 100644 --- a/crates/perry-runtime/src/pointer_event.rs +++ b/crates/perry-runtime/src/pointer_event.rs @@ -98,12 +98,12 @@ mod tests { assert_eq!(bits & 0xFFFF_0000_0000_0000, POINTER_TAG); let obj_ptr = (bits & 0x0000_FFFF_FFFF_FFFF) as *mut crate::object::ObjectHeader; - let x = unsafe { js_object_get_field(obj_ptr, 0) }; - let y = unsafe { js_object_get_field(obj_ptr, 1) }; - let button = unsafe { js_object_get_field(obj_ptr, 2) }; + let x = js_object_get_field(obj_ptr, 0); + let y = js_object_get_field(obj_ptr, 1); + let button = js_object_get_field(obj_ptr, 2); // Field 3 (pointerType) is a string — exercise only that the // value carries the STRING_TAG so we know we wrote *a* string. - let pt = unsafe { js_object_get_field(obj_ptr, 3) }; + let pt = js_object_get_field(obj_ptr, 3); assert_eq!(f64::from_bits(x.bits()), 10.5); assert_eq!(f64::from_bits(y.bits()), 20.25); @@ -125,7 +125,7 @@ mod tests { let pen = js_pointer_event_new(0.0, 0.0, 0, POINTER_TYPE_PEN); let pt = |nb: f64| { let obj = (nb.to_bits() & 0x0000_FFFF_FFFF_FFFF) as *mut crate::object::ObjectHeader; - unsafe { js_object_get_field(obj, 3) }.bits() + js_object_get_field(obj, 3).bits() }; let m = pt(mouse); let t = pt(touch); diff --git a/crates/perry-runtime/src/process.rs b/crates/perry-runtime/src/process.rs index ba995a4b39..1e59489223 100644 --- a/crates/perry-runtime/src/process.rs +++ b/crates/perry-runtime/src/process.rs @@ -736,12 +736,12 @@ pub(crate) static SOURCE_MAPS_GENERATED_CODE: AtomicBool = AtomicBool::new(false pub(crate) static MODULE_COMPILE_CACHE_DIR: std::sync::Mutex> = std::sync::Mutex::new(None); -/// Thread-local cell holding the process title set via `process.title = X` -/// (#1401). `None` means "not assigned yet, fall back to argv[0]". The -/// setter records the value here; on Linux it also calls `prctl(PR_SET_NAME)` -/// so `/proc//comm` reflects the new value. macOS has no per-process -/// analog — the assignment is still observable via subsequent `process.title` -/// reads, matching Node's best-effort semantics. +// Thread-local cell holding the process title set via `process.title = X` +// (#1401). `None` means "not assigned yet, fall back to argv[0]". The +// setter records the value here; on Linux it also calls `prctl(PR_SET_NAME)` +// so `/proc//comm` reflects the new value. macOS has no per-process +// analog — the assignment is still observable via subsequent `process.title` +// reads, matching Node's best-effort semantics. thread_local! { pub(crate) static PROCESS_TITLE: std::cell::RefCell> = const { std::cell::RefCell::new(None) diff --git a/crates/perry-runtime/src/process/credentials.rs b/crates/perry-runtime/src/process/credentials.rs index 3f6434342b..2502c4d6c0 100644 --- a/crates/perry-runtime/src/process/credentials.rs +++ b/crates/perry-runtime/src/process/credentials.rs @@ -209,12 +209,12 @@ pub extern "C" fn js_process_setgroups(groups: f64) { if arr_ptr.is_null() { return; } - let len = unsafe { crate::array::js_array_length(arr_ptr) }; + let len = crate::array::js_array_length(arr_ptr); #[cfg(unix)] { let mut gids: Vec = Vec::with_capacity(len as usize); for i in 0..len { - let v = unsafe { crate::array::js_array_get_f64(arr_ptr, i) }; + let v = crate::array::js_array_get_f64(arr_ptr, i); if let Some(id) = unix_id_arg(v) { gids.push(id as libc::gid_t); } diff --git a/crates/perry-runtime/src/process/env_misc.rs b/crates/perry-runtime/src/process/env_misc.rs index acc2ddb6e1..68bdb87f32 100644 --- a/crates/perry-runtime/src/process/env_misc.rs +++ b/crates/perry-runtime/src/process/env_misc.rs @@ -880,7 +880,8 @@ pub extern "C" fn js_process_pending_exit_code() -> i32 { // runtime is `js_process_exit`'s nullish fallback. Anchor the symbol so the // auto-optimize whole-program dead-strip cannot drop it (same guard the // unhandled-rejection reporter uses, #4876). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROCESS_PENDING_EXIT_CODE: extern "C" fn() -> i32 = js_process_pending_exit_code; /// Set an environment variable. Backs `process.env.X = v` (#1344). @@ -958,60 +959,80 @@ pub extern "C" fn js_setenv(name_ptr: *const StringHeader, value: f64) { // trips the runtime through whole-program LLVM bitcode and is free to // internalize + dead-strip an unreferenced symbol — leaving the codegen call // dangling (`Undefined symbols: _js_setenv` at final link, which is exactly -// how #1344's acceptance test still failed on main). The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics +// how #1344's acceptance test still failed on main). The `#[used]` statics // below pin a retained reference edge so both survive every link mode. See // the same pattern in `value/dyn_index.rs`. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SETENV: extern "C" fn(*const StringHeader, f64) = js_setenv; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_REMOVEENV: extern "C" fn(*const StringHeader) = js_removeenv; // #3120: codegen emits `js_module_find_package_json` only from generated `.o`, // so pin a retained reference edge for the auto-optimize whole-program build. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_FIND_PACKAGE_JSON: extern "C" fn(f64, f64) -> f64 = js_module_find_package_json; // node:module helper-state APIs are codegen-emitted from generated `.o`, so pin // retained reference edges for the auto-optimize whole-program build. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_ENABLE_COMPILE_CACHE: extern "C" fn(f64) -> f64 = js_module_enable_compile_cache; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_FLUSH_COMPILE_CACHE: extern "C" fn() -> f64 = js_module_flush_compile_cache; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_GET_COMPILE_CACHE_DIR: extern "C" fn() -> f64 = js_module_get_compile_cache_dir; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_GET_SOURCE_MAPS_SUPPORT: extern "C" fn() -> f64 = js_module_get_source_maps_support; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_SET_SOURCE_MAPS_SUPPORT: extern "C" fn(f64, f64) -> f64 = js_module_set_source_maps_support; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_STRIP_TYPESCRIPT_TYPES: extern "C" fn(f64, f64) -> f64 = js_module_strip_typescript_types; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_REGISTER: extern "C" fn(f64, f64, f64) -> f64 = js_module_register; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_REGISTER_HOOKS: extern "C" fn(f64) -> f64 = js_module_register_hooks; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_DYNAMIC_IMPORT_APPLY_HOOKS: extern "C" fn(f64) -> f64 = js_module_dynamic_import_apply_hooks; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_MODULE_NEW: extern "C" fn(f64) -> f64 = js_module_module_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_FIND_PATH: extern "C" fn(f64, f64, f64) -> f64 = js_module_find_path; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_INIT_PATHS: extern "C" fn() -> f64 = js_module_init_paths; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_LOAD: extern "C" fn(f64, f64, f64) -> f64 = js_module_load; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_NODE_MODULE_PATHS: extern "C" fn(f64) -> f64 = js_module_node_module_paths; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_PRELOAD_MODULES: extern "C" fn(f64) -> f64 = js_module_preload_modules; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_RESOLVE_FILENAME: extern "C" fn(f64, f64, f64, f64) -> f64 = js_module_resolve_filename; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_MODULE_RESOLVE_LOOKUP_PATHS: extern "C" fn(f64, f64) -> f64 = js_module_resolve_lookup_paths; @@ -1760,12 +1781,16 @@ fn read_js_string_lossy(value: f64) -> String { // process native table). Pin retained-reference edges so the auto-optimize // whole-program build doesn't internalize + dead-strip them. Same rationale // as KEEP_JS_SETENV above. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PROCESS_SOURCE_MAPS_ENABLED: extern "C" fn() -> f64 = js_process_source_maps_enabled; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PROCESS_SET_SOURCE_MAPS_ENABLED: extern "C" fn(f64) -> f64 = js_process_set_source_maps_enabled; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PROCESS_REF: extern "C" fn(f64) -> f64 = js_process_ref; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PROCESS_UNREF: extern "C" fn(f64) -> f64 = js_process_unref; diff --git a/crates/perry-runtime/src/process/ipc.rs b/crates/perry-runtime/src/process/ipc.rs index f133e83118..b090e40a28 100644 --- a/crates/perry-runtime/src/process/ipc.rs +++ b/crates/perry-runtime/src/process/ipc.rs @@ -4,6 +4,8 @@ //! the same inherited Unix fd convention used by its `child_process.fork()` //! parent side and speaks newline-delimited JSON frames for this cut. +#![cfg_attr(not(feature = "proc-ipc"), allow(dead_code))] + use crate::closure::{ js_closure_alloc, js_closure_get_capture_ptr, js_closure_set_capture_ptr, js_native_call_value, js_register_closure_arity, js_register_closure_length, ClosureHeader, diff --git a/crates/perry-runtime/src/promise/async_step.rs b/crates/perry-runtime/src/promise/async_step.rs index bba35fb537..d1574436d8 100644 --- a/crates/perry-runtime/src/promise/async_step.rs +++ b/crates/perry-runtime/src/promise/async_step.rs @@ -676,7 +676,8 @@ pub extern "C" fn js_async_generator_resume( result } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ASYNC_GENERATOR_RESUME: extern "C" fn(f64, f64, f64) -> f64 = js_async_generator_resume; diff --git a/crates/perry-runtime/src/promise/combinators.rs b/crates/perry-runtime/src/promise/combinators.rs index 7f31bfbd2c..66263f6f58 100644 --- a/crates/perry-runtime/src/promise/combinators.rs +++ b/crates/perry-runtime/src/promise/combinators.rs @@ -13,7 +13,7 @@ use super::assimilate::{ }; #[derive(Clone, Copy)] -pub(super) struct PromiseAllState { +pub(crate) struct PromiseAllState { pub result_promise: *mut Promise, pub results_arr: *mut crate::array::ArrayHeader, pub state_arr: *mut crate::array::ArrayHeader, @@ -500,14 +500,18 @@ pub extern "C" fn js_promise_any_iterable(value: f64) -> *mut Promise { /// #2822/#3320: keepalive anchors so the whole-program LLVM (auto-optimize) /// build does not dead-strip these codegen-only `#[no_mangle]` entry points. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_ALL_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_all_iterable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_RACE_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_race_iterable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_ALL_SETTLED_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_all_settled_iterable; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_ANY_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_any_iterable; // Queue for scheduled promise resolutions diff --git a/crates/perry-runtime/src/promise/keyed_table.rs b/crates/perry-runtime/src/promise/keyed_table.rs index ebf35197f1..9e70bb4906 100644 --- a/crates/perry-runtime/src/promise/keyed_table.rs +++ b/crates/perry-runtime/src/promise/keyed_table.rs @@ -177,6 +177,7 @@ impl PromiseKeyedTable { } #[inline] + #[allow(dead_code)] // used only by #[cfg(test)] assertions in this module pub(super) fn is_empty(&self) -> bool { self.entries.is_empty() } diff --git a/crates/perry-runtime/src/promise/microtasks.rs b/crates/perry-runtime/src/promise/microtasks.rs index 663899f3ad..5a6d8d249b 100644 --- a/crates/perry-runtime/src/promise/microtasks.rs +++ b/crates/perry-runtime/src/promise/microtasks.rs @@ -75,7 +75,8 @@ pub extern "C" fn js_promise_run_microtasks_event_loop() -> i32 { // The entry event loop is generated code, so nothing in the Rust runtime // references this symbol — anchor it like the other codegen-only hooks so the // auto-optimize internalize+dead-strip pass can't drop it (#4876). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_RUN_MICROTASKS_EVENT_LOOP: extern "C" fn() -> i32 = js_promise_run_microtasks_event_loop; diff --git a/crates/perry-runtime/src/promise/mod.rs b/crates/perry-runtime/src/promise/mod.rs index 00808760fc..fb0eca2102 100644 --- a/crates/perry-runtime/src/promise/mod.rs +++ b/crates/perry-runtime/src/promise/mod.rs @@ -96,7 +96,7 @@ pub(crate) use scanners::{ test_async_step_thunk_cache, test_clear_promise_scanner_roots, test_current_microtask_value, test_promise_context_keys, test_promise_scanner_snapshot, test_seed_async_step_thunk_cache, test_seed_many_promise_task_roots, test_seed_promise_context, test_seed_promise_scanner_roots, - test_store_with_resolvers_result_fields, TestPromiseScannerSnapshot, + test_store_with_resolvers_result_fields, }; // Cached `PERRY_MT_PROFILE` flag, populated once at process start. @@ -450,23 +450,32 @@ pub fn scan_iter_result_root_mut(visitor: &mut crate::gc::RuntimeRootVisitor<'_> } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_SET: extern "C" fn(f64, i32) -> f64 = js_iter_result_set; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_SET_F64: extern "C" fn(f64, i32) -> f64 = js_iter_result_set_f64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_SET_I32: extern "C" fn(i32, i32) -> f64 = js_iter_result_set_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_SET_I1: extern "C" fn(i32, i32) -> f64 = js_iter_result_set_i1; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_GET_VALUE: extern "C" fn() -> f64 = js_iter_result_get_value; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_GET_VALUE_F64: extern "C" fn() -> f64 = js_iter_result_get_value_f64; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_GET_VALUE_I32: extern "C" fn() -> i32 = js_iter_result_get_value_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_GET_VALUE_I1: extern "C" fn() -> i32 = js_iter_result_get_value_i1; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITER_RESULT_GET_DONE: extern "C" fn() -> f64 = js_iter_result_get_done; /// Promise state diff --git a/crates/perry-runtime/src/promise/rejection.rs b/crates/perry-runtime/src/promise/rejection.rs index 16dae3886c..e3b89a6afe 100644 --- a/crates/perry-runtime/src/promise/rejection.rs +++ b/crates/perry-runtime/src/promise/rejection.rs @@ -96,7 +96,8 @@ pub extern "C" fn js_promise_mark_internally_handled(promise: *mut Promise) { /// Keep the stdlib-facing marker alive through the dead-strip pass on the /// PERRY_NO_AUTO_OPTIMIZE prebuilt-lib link (same pattern as the checkpoint /// hook anchors below). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_MARK_INTERNALLY_HANDLED: extern "C" fn(*mut Promise) = js_promise_mark_internally_handled; @@ -254,7 +255,8 @@ pub extern "C" fn js_promise_report_unhandled_rejections() { // internalize+dead-strip pass would otherwise drop it and that link mode // fails with "undefined symbol". The classic link needs no anchor (see the // error.rs/combinators.rs anchors for the same pattern). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROMISE_REPORT_UNHANDLED_REJECTIONS: extern "C" fn() = js_promise_report_unhandled_rejections; diff --git a/crates/perry-runtime/src/promise/then.rs b/crates/perry-runtime/src/promise/then.rs index 8549057391..4a345b832c 100644 --- a/crates/perry-runtime/src/promise/then.rs +++ b/crates/perry-runtime/src/promise/then.rs @@ -925,19 +925,6 @@ fn throw_promise_prototype_incompatible_receiver(method: &str, receiver: f64) -> crate::exception::js_throw(f64::from_bits(err_value)) } -fn promise_prototype_receiver(method: &str) -> *mut Promise { - let receiver = crate::object::js_implicit_this_get(); - if js_value_is_promise(receiver) != 0 { - return crate::value::js_nanbox_get_pointer(receiver) as *mut Promise; - } - // `class X extends Promise` instance: unwrap its hidden backing Promise cell - // so `inst.then/catch(...)` dispatches against the real promise state. - if let Some(backing) = super::subclass::subclass_backing_promise(receiver) { - return backing; - } - throw_promise_prototype_incompatible_receiver(method, receiver) -} - /// ECMA-262 Invoke(receiver, "then", args): read the `then` property and call it. /// Unlike `js_native_call_method`, this reads the own/inherited `then` property /// first (invoking accessor getters) and throws TypeError if it is not callable — @@ -1455,35 +1442,6 @@ pub(crate) extern "C" fn promise_prototype_finally_thunk( call_receiver_then(receiver, &args) } -extern "C" fn promise_then_bound( - closure: *const crate::closure::ClosureHeader, - on_fulfilled: f64, - on_rejected: f64, -) -> f64 { - let p = crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut Promise; - box_promise_ptr(js_promise_then( - p, - arg_to_closure(on_fulfilled), - arg_to_closure(on_rejected), - )) -} - -extern "C" fn promise_catch_bound( - closure: *const crate::closure::ClosureHeader, - on_rejected: f64, -) -> f64 { - let p = crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut Promise; - box_promise_ptr(js_promise_catch(p, arg_to_closure(on_rejected))) -} - -extern "C" fn promise_finally_bound( - closure: *const crate::closure::ClosureHeader, - on_finally: f64, -) -> f64 { - let p = crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut Promise; - box_promise_ptr(js_promise_finally(p, arg_to_closure(on_finally))) -} - /// Return the `Promise.prototype[property]` closure for a promise's /// `then`/`catch`/`finally` value-read, or `None` for any other property. /// Returns the shared prototype method so `p.then === Promise.prototype.then` diff --git a/crates/perry-runtime/src/proxy.rs b/crates/perry-runtime/src/proxy.rs index 487ef33d40..733f360c2f 100644 --- a/crates/perry-runtime/src/proxy.rs +++ b/crates/perry-runtime/src/proxy.rs @@ -1845,35 +1845,44 @@ pub extern "C" fn js_proxy_revocable(target: f64, handler: f64) -> f64 { } // #2846: retention anchor for `Proxy.revocable` (codegen-only callsite). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PROXY_REVOCABLE: extern "C" fn(f64, f64) -> f64 = js_proxy_revocable; // #2762: retention anchors for the Reflect-specific extensibility entry points. // These `#[no_mangle]` fns are emitted only by codegen (no Rust caller in the // crate graph), so the auto-optimize whole-program LLVM bitcode rebuild would // otherwise internalize and dead-strip them. See node_stream_keepalive.rs. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_IS_EXTENSIBLE: extern "C" fn(f64) -> f64 = js_reflect_is_extensible; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_PREVENT_EXTENSIONS: extern "C" fn(f64) -> f64 = js_reflect_prevent_extensions; // #2761: retention anchor for `Reflect.setPrototypeOf` (codegen-only callsite). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_SET_PROTOTYPE_OF: extern "C" fn(f64, f64) -> f64 = js_reflect_set_prototype_of; // #2763/#2764/#2766/#2767: retention anchors for the Reflect entry points // whose only callsites are codegen-emitted. `js_reflect_get` gained a third // `receiver` arg (#2766) and must keep its new signature retained. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_GET: extern "C" fn(f64, f64, f64) -> f64 = js_reflect_get; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_GET_OWN_PROPERTY_DESCRIPTOR: extern "C" fn(f64, f64) -> f64 = js_reflect_get_own_property_descriptor; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_HAS: extern "C" fn(f64, f64) -> f64 = js_reflect_has; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_OWN_KEYS: extern "C" fn(f64) -> f64 = js_reflect_own_keys; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REFLECT_APPLY: extern "C" fn(f64, f64, f64) -> f64 = js_reflect_apply; /// Rewrite a `REFLECT_METADATA` key's POINTER-tagged target bits during the diff --git a/crates/perry-runtime/src/proxy/put_value.rs b/crates/perry-runtime/src/proxy/put_value.rs index c2d40bf14d..235ee2c98f 100644 --- a/crates/perry-runtime/src/proxy/put_value.rs +++ b/crates/perry-runtime/src/proxy/put_value.rs @@ -492,7 +492,8 @@ unsafe fn dyn_ic_try_store(target: f64, token: u64, slot: u32, value: f64) -> Op // #6088-style keep: codegen emits the only call; a whole-program bitcode // link would otherwise dead-strip the IC entry. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_PUT_VALUE_SET_DYN_IC: extern "C" fn(*mut [i64; 8], f64, f64, f64, i32) -> f64 = js_put_value_set_dyn_ic; diff --git a/crates/perry-runtime/src/regex.rs b/crates/perry-runtime/src/regex.rs index c52736a530..59ddcf289e 100644 --- a/crates/perry-runtime/src/regex.rs +++ b/crates/perry-runtime/src/regex.rs @@ -1618,7 +1618,7 @@ pub extern "C" fn js_regexp_get_flags(re: *const RegExpHeader) -> *mut StringHea pub extern "C" fn js_regexp_to_string(re: *const RegExpHeader) -> *mut StringHeader { let src = js_regexp_get_source(re); let flg = js_regexp_get_flags(re); - let out = unsafe { format!("/{}/{}", string_as_str(src), string_as_str(flg)) }; + let out = format!("/{}/{}", string_as_str(src), string_as_str(flg)); js_string_from_str(&out) } diff --git a/crates/perry-runtime/src/regex/escape.rs b/crates/perry-runtime/src/regex/escape.rs index a2203e544b..e5a2fb8a51 100644 --- a/crates/perry-runtime/src/regex/escape.rs +++ b/crates/perry-runtime/src/regex/escape.rs @@ -138,6 +138,7 @@ pub extern "C" fn js_regexp_escape(input: f64) -> f64 { /// Keepalive anchor: `js_regexp_escape` is only called from codegen-emitted /// `.o`, so the auto-optimize whole-program LLVM rebuild would dead-strip it -/// without this `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see #3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +/// without this `#[used]` reference (see #3320). +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_REGEXP_ESCAPE: extern "C" fn(f64) -> f64 = js_regexp_escape; diff --git a/crates/perry-runtime/src/regex/tests.rs b/crates/perry-runtime/src/regex/tests.rs index 5a13cf747b..201914bd6b 100644 --- a/crates/perry-runtime/src/regex/tests.rs +++ b/crates/perry-runtime/src/regex/tests.rs @@ -75,7 +75,7 @@ fn fancy_backreference_match() { let re = js_regexp_new(make_string(r"(\w)\1"), make_string("")); let result = js_string_match(make_string("hello"), re); assert!(!result.is_null()); - unsafe { + { let v = crate::array::js_array_get_f64(result, 0); let sp = crate::value::js_get_string_pointer_unified(v) as *const StringHeader; assert_eq!(string_as_str(sp), "ll"); @@ -127,7 +127,7 @@ fn fancy_lookbehind_exec_index() { let result = js_regexp_exec(re, make_string("price: $42")); assert!(!result.is_null()); assert_eq!(js_regexp_exec_get_index(), 8.0); - unsafe { + { let v = crate::array::js_array_get_f64(result, 0); let sp = crate::value::js_get_string_pointer_unified(v) as *const StringHeader; assert_eq!(string_as_str(sp), "42"); diff --git a/crates/perry-runtime/src/regex/utf16.rs b/crates/perry-runtime/src/regex/utf16.rs index c1d2ebdfe1..2c5c0121d4 100644 --- a/crates/perry-runtime/src/regex/utf16.rs +++ b/crates/perry-runtime/src/regex/utf16.rs @@ -13,6 +13,7 @@ //! dependency as #6303.) /// UTF-16 code-unit index -> byte offset. +#[cfg(any(feature = "regex-engine", test))] pub(super) fn utf16_index_to_byte(s: &str, utf16_index: usize) -> usize { if utf16_index == 0 { return 0; diff --git a/crates/perry-runtime/src/safe_area.rs b/crates/perry-runtime/src/safe_area.rs index cd51f3b986..d5094496fe 100644 --- a/crates/perry-runtime/src/safe_area.rs +++ b/crates/perry-runtime/src/safe_area.rs @@ -57,10 +57,10 @@ mod tests { assert_eq!(bits & 0xFFFF_0000_0000_0000, POINTER_TAG); let obj_ptr = (bits & 0x0000_FFFF_FFFF_FFFF) as *mut crate::object::ObjectHeader; - let top = unsafe { js_object_get_field(obj_ptr, 0) }; - let right = unsafe { js_object_get_field(obj_ptr, 1) }; - let bottom = unsafe { js_object_get_field(obj_ptr, 2) }; - let left = unsafe { js_object_get_field(obj_ptr, 3) }; + let top = js_object_get_field(obj_ptr, 0); + let right = js_object_get_field(obj_ptr, 1); + let bottom = js_object_get_field(obj_ptr, 2); + let left = js_object_get_field(obj_ptr, 3); assert_eq!(f64::from_bits(top.bits()), 59.0); assert_eq!(f64::from_bits(right.bits()), 0.0); diff --git a/crates/perry-runtime/src/set.rs b/crates/perry-runtime/src/set.rs index df7412c4e3..c32108d788 100644 --- a/crates/perry-runtime/src/set.rs +++ b/crates/perry-runtime/src/set.rs @@ -716,7 +716,7 @@ fn jsvalue_eq(a: f64, b: f64) -> bool { /// Uses the O(1) hash index side-table. /// C-ABI: current elements-array index of `value` (SameValueZero), or `-1.0` if /// absent. Companion to `js_map_find_key_index` for the delete-safe Set `for-of` -/// fast path (#6075). Only invoked from generated IR, so `#[cfg_attr(feature = "keepalive-anchors", used)]` keeps it. +/// fast path (#6075). Only invoked from generated IR, so `#[used]` keeps it. #[no_mangle] pub extern "C" fn js_set_find_value_index(set_boxed: f64, value: f64) -> f64 { let set = clean_set_ptr(crate::value::js_nanbox_get_pointer(set_boxed) as *const SetHeader); @@ -725,7 +725,8 @@ pub extern "C" fn js_set_find_value_index(set_boxed: f64, value: f64) -> f64 { } unsafe { find_value_index(set, normalize_zero(value)) as f64 } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_FIND_VALUE_INDEX: extern "C" fn(f64, f64) -> f64 = js_set_find_value_index; pub(crate) unsafe fn find_value_index(set: *const SetHeader, value: f64) -> i32 { @@ -1178,47 +1179,65 @@ pub extern "C" fn js_set_delete_bool(set: *mut SetHeader, value: i32) -> i32 { // Codegen emits these string-key typed lowering helpers directly from // generated LLVM IR. Keep roots prevent whole-program LTO/dead-strip from // removing the exported symbols when the Rust crate graph has no caller. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_STRING: extern "C" fn( *mut SetHeader, *const StringHeader, ) -> *mut SetHeader = js_set_add_string; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_NUMBER: extern "C" fn(*mut SetHeader, f64) -> *mut SetHeader = js_set_add_number; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_STRING: extern "C" fn(*const SetHeader, *const StringHeader) -> i32 = js_set_has_string; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_NUMBER: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_has_number; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_STRING: extern "C" fn(*mut SetHeader, *const StringHeader) -> i32 = js_set_delete_string; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_NUMBER: extern "C" fn(*mut SetHeader, f64) -> i32 = js_set_delete_number; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_I32: extern "C" fn(*mut SetHeader, i32) -> *mut SetHeader = js_set_add_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_I32: extern "C" fn(*const SetHeader, i32) -> i32 = js_set_has_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_I32: extern "C" fn(*mut SetHeader, i32) -> i32 = js_set_delete_i32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_U32: extern "C" fn(*mut SetHeader, u32) -> *mut SetHeader = js_set_add_u32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_U32: extern "C" fn(*const SetHeader, u32) -> i32 = js_set_has_u32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_U32: extern "C" fn(*mut SetHeader, u32) -> i32 = js_set_delete_u32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_F32: extern "C" fn(*mut SetHeader, f32) -> *mut SetHeader = js_set_add_f32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_F32: extern "C" fn(*const SetHeader, f32) -> i32 = js_set_has_f32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_F32: extern "C" fn(*mut SetHeader, f32) -> i32 = js_set_delete_f32; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_ADD_BOOL: extern "C" fn(*mut SetHeader, i32) -> *mut SetHeader = js_set_add_bool; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_HAS_BOOL: extern "C" fn(*const SetHeader, i32) -> i32 = js_set_has_bool; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SET_DELETE_BOOL: extern "C" fn(*mut SetHeader, i32) -> i32 = js_set_delete_bool; /// Clear all elements from the set @@ -1803,22 +1822,29 @@ pub extern "C" fn js_set_is_disjoint_from(set: *const SetHeader, other: f64) -> // #2872: keepalive anchors so the auto-optimize whole-program-LLVM-bitcode // rebuild doesn't dead-strip these codegen-only `#[no_mangle]` entry points // (see project_auto_optimize_keepalive_3320 / PR #3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_UNION: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_union; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_INTERSECTION: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_intersection; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_DIFFERENCE: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_difference; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_SYMDIFF: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_symmetric_difference; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_IS_SUBSET: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_subset_of; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_IS_SUPERSET: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_superset_of; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SET_IS_DISJOINT: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_disjoint_from; #[cfg(test)] diff --git a/crates/perry-runtime/src/string/compare.rs b/crates/perry-runtime/src/string/compare.rs index c61a74091c..33af949f7b 100644 --- a/crates/perry-runtime/src/string/compare.rs +++ b/crates/perry-runtime/src/string/compare.rs @@ -666,7 +666,7 @@ pub extern "C" fn js_string_locale_compare_opts( b: *const StringHeader, options: f64, ) -> f64 { - let numeric = unsafe { + let numeric = { let ptr = crate::value::js_nanbox_get_pointer(options) as *const crate::object::ObjectHeader; if ptr.is_null() || (ptr as usize) < 0x10000 { @@ -810,7 +810,7 @@ mod tests_sso_helpers { let bytes = name.as_bytes(); assert!(bytes.len() <= SHORT_STRING_MAX_LEN); - let incoming = unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) }; + let incoming = js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32); let heap_stored = JSValue::string_ptr(incoming); let sso_stored = JSValue::try_short_string(bytes).expect("len<=5 encodes as SSO"); assert!(sso_stored.is_short_string(), "{name:?} should be SSO"); @@ -840,9 +840,9 @@ mod tests_sso_helpers { /// is SSO and the other is heap. #[test] fn key_matches_rejects_different_bytes_across_reps() { - let incoming = unsafe { js_string_from_bytes(b"id".as_ptr(), 2) }; + let incoming = js_string_from_bytes(b"id".as_ptr(), 2); let sso_other = JSValue::try_short_string(b"tag").expect("SSO"); - let heap_other_ptr = unsafe { js_string_from_bytes(b"other".as_ptr(), 5) }; + let heap_other_ptr = js_string_from_bytes(b"other".as_ptr(), 5); let heap_other = JSValue::string_ptr(heap_other_ptr); unsafe { @@ -855,7 +855,7 @@ mod tests_sso_helpers { /// without dereferencing the payload. #[test] fn key_matches_rejects_non_string_stored() { - let incoming = unsafe { js_string_from_bytes(b"id".as_ptr(), 2) }; + let incoming = js_string_from_bytes(b"id".as_ptr(), 2); for stored in [ JSValue::undefined(), JSValue::null(), @@ -874,7 +874,7 @@ mod tests_sso_helpers { #[test] fn key_bytes_round_trips_sso_and_heap() { let sso = JSValue::try_short_string(b"path").expect("SSO"); - let heap = JSValue::string_ptr(unsafe { js_string_from_bytes(b"longish".as_ptr(), 7) }); + let heap = JSValue::string_ptr(js_string_from_bytes(b"longish".as_ptr(), 7)); let mut buf = [0u8; SHORT_STRING_MAX_LEN]; unsafe { assert_eq!(js_string_key_bytes(sso, &mut buf), Some(b"path".as_ref())); diff --git a/crates/perry-runtime/src/string/intern.rs b/crates/perry-runtime/src/string/intern.rs index 988a1f57d6..7b9b8a29dd 100644 --- a/crates/perry-runtime/src/string/intern.rs +++ b/crates/perry-runtime/src/string/intern.rs @@ -17,14 +17,14 @@ pub(crate) const INTERN_TABLE_MASK: usize = INTERN_TABLE_SIZE - 1; /// Maximum byte length for strings eligible for interning. pub(crate) const INTERN_MAX_BYTE_LEN: u32 = 64; -/// Per-thread intern table. -/// -/// Each thread (main + every `perry/thread` worker) has its own arena, so -/// cached `StringHeader*` pointers MUST be per-thread — a string interned -/// from worker A's arena is a use-after-free / cross-arena pointer when -/// read from worker B. The previous design used a single process-wide -/// `static mut`, which both raced under concurrent allocation and risked -/// handing back foreign-arena pointers. +// Per-thread intern table. +// +// Each thread (main + every `perry/thread` worker) has its own arena, so +// cached `StringHeader*` pointers MUST be per-thread — a string interned +// from worker A's arena is a use-after-free / cross-arena pointer when +// read from worker B. The previous design used a single process-wide +// `static mut`, which both raced under concurrent allocation and risked +// handing back foreign-arena pointers. thread_local! { // arm64_32 fix: HEAP-allocate this table instead of inline TLS. // Oversized `#[thread_local]` storage overflows the ILP32 TLS layout and its diff --git a/crates/perry-runtime/src/string/locale.rs b/crates/perry-runtime/src/string/locale.rs index 0cbeb7e54c..45fbe12a19 100644 --- a/crates/perry-runtime/src/string/locale.rs +++ b/crates/perry-runtime/src/string/locale.rs @@ -410,16 +410,19 @@ pub extern "C" fn js_string_validate_collator_args(locales: f64, options: f64) { crate::intl::validate_locale_compare(locales, options); } -// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive anchors: these `#[no_mangle]` entry points are reached +// `#[used]` keepalive anchors: these `#[no_mangle]` entry points are reached // only from generated `.o`, so the whole-program auto-optimize bitcode rebuild // would otherwise dead-strip them (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_LOCALE_LOWER: extern "C" fn(*const StringHeader, f64) -> *mut StringHeader = js_string_to_locale_lower_case; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_LOCALE_UPPER: extern "C" fn(*const StringHeader, f64) -> *mut StringHeader = js_string_to_locale_upper_case; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_COLLATOR_ARGS: extern "C" fn(f64, f64) = js_string_validate_collator_args; #[cfg(test)] diff --git a/crates/perry-runtime/src/string/pad.rs b/crates/perry-runtime/src/string/pad.rs index c7240e298f..4cd20a6325 100644 --- a/crates/perry-runtime/src/string/pad.rs +++ b/crates/perry-runtime/src/string/pad.rs @@ -27,10 +27,11 @@ pub extern "C" fn js_string_pad_fill(value: f64) -> *mut StringHeader { crate::builtins::js_string_coerce(value) } -// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_pad_fill` is reached only from generated +// `#[used]` keepalive: `js_string_pad_fill` is reached only from generated // `.o`, so the whole-program auto-optimize bitcode rebuild would dead-strip it // without an anchor (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_PAD_FILL: extern "C" fn(f64) -> *mut StringHeader = js_string_pad_fill; /// Maximum string length Perry/V8 supports as a single `String`. This diff --git a/crates/perry-runtime/src/string/raw.rs b/crates/perry-runtime/src/string/raw.rs index 8204beb9fb..f35ffc48f2 100644 --- a/crates/perry-runtime/src/string/raw.rs +++ b/crates/perry-runtime/src/string/raw.rs @@ -114,6 +114,7 @@ fn throw_raw_type_error() -> ! { /// Keepalive anchor — `js_string_raw` is emitted only by generated code /// (the `String.raw(...)` call lowering), so the auto-optimize whole-program /// LLVM rebuild would otherwise dead-strip this `#[no_mangle]` symbol and -/// break linking (see PR #3320 / the `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive pattern). -#[cfg_attr(feature = "keepalive-anchors", used)] +/// break linking (see PR #3320 / the `#[used]` keepalive pattern). +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_STRING_RAW: extern "C" fn(f64, f64) -> *mut StringHeader = js_string_raw; diff --git a/crates/perry-runtime/src/string/slice_ops.rs b/crates/perry-runtime/src/string/slice_ops.rs index 960fde3c3a..3463f27127 100644 --- a/crates/perry-runtime/src/string/slice_ops.rs +++ b/crates/perry-runtime/src/string/slice_ops.rs @@ -178,10 +178,11 @@ pub extern "C" fn js_string_substr( ) } -// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_substr` is reached only from generated `.o`, +// `#[used]` keepalive: `js_string_substr` is reached only from generated `.o`, // so the whole-program auto-optimize bitcode rebuild would dead-strip it // without an anchor (see project_auto_optimize_keepalive_3320). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_SUBSTR: extern "C" fn(*const StringHeader, f64, f64) -> *mut StringHeader = js_string_substr; @@ -524,10 +525,11 @@ pub extern "C" fn js_string_position_to_index(pos_f64: f64) -> i32 { } } -// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_position_to_index` is reached only from +// `#[used]` keepalive: `js_string_position_to_index` is reached only from // generated `.o`, so the auto-optimize whole-program bitcode pass would // otherwise dead-strip it. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_POSITION_TO_INDEX: extern "C" fn(f64) -> i32 = js_string_position_to_index; /// Find the last index of a substring (-1 if not found). diff --git a/crates/perry-runtime/src/string/tests_guard_page.rs b/crates/perry-runtime/src/string/tests_guard_page.rs index 977d13529b..914bc4dd37 100644 --- a/crates/perry-runtime/src/string/tests_guard_page.rs +++ b/crates/perry-runtime/src/string/tests_guard_page.rs @@ -291,7 +291,7 @@ fn split_parts_preserve_lone_surrogate_flag() { f0, STRING_FLAG_HAS_LONE_SURROGATES, "the part holding the lone surrogate must stay flagged" ); - let part0 = unsafe { + let part0 = { let v = crate::array::js_array_get_f64(arr, 0); crate::value::js_nanbox_get_pointer(v) as *const StringHeader }; diff --git a/crates/perry-runtime/src/symbol/constructors.rs b/crates/perry-runtime/src/symbol/constructors.rs index adf6b58a82..e1da5b1eb0 100644 --- a/crates/perry-runtime/src/symbol/constructors.rs +++ b/crates/perry-runtime/src/symbol/constructors.rs @@ -179,8 +179,9 @@ fn is_well_known_symbol_member_name(name: &str) -> bool { // #1561-style force-keep: `js_symbol_computed_member` has no internal Rust // callers — only generated IR (perry-hir lowers `Symbol[key]` to a call to it), // so LTO / whole-program-bitcode link modes are free to internalize and -// dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` reference edge keeps the export alive. -#[cfg_attr(feature = "keepalive-anchors", used)] +// dead-strip it. The `#[used]` reference edge keeps the export alive. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SYMBOL_COMPUTED_MEMBER: unsafe extern "C" fn(f64, f64) -> f64 = js_symbol_computed_member; diff --git a/crates/perry-runtime/src/symbol/iterator.rs b/crates/perry-runtime/src/symbol/iterator.rs index 54259d1c67..730fa74919 100644 --- a/crates/perry-runtime/src/symbol/iterator.rs +++ b/crates/perry-runtime/src/symbol/iterator.rs @@ -151,7 +151,8 @@ pub(crate) fn class_ref_resolves_iterator(val_f64: f64) -> bool { /// / guarded `__iter.return()` call in this validator. Returns the result /// unchanged when it is an object. // #1561-style force-keep: only generated IR calls this. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_ITERATOR_RESULT_VALIDATE: extern "C" fn(f64) -> f64 = js_iterator_result_validate; #[no_mangle] diff --git a/crates/perry-runtime/src/temporal/dispatch.rs b/crates/perry-runtime/src/temporal/dispatch.rs index 2ab3828ad7..24b9ac7531 100644 --- a/crates/perry-runtime/src/temporal/dispatch.rs +++ b/crates/perry-runtime/src/temporal/dispatch.rs @@ -13,27 +13,6 @@ use crate::value::JSValue; // ---- argument coercion ---------------------------------------------------- -/// `ToNumber(args[i])`, or `NaN` if the argument is absent. -#[inline] -pub(crate) fn num_arg(args: &[f64], i: usize) -> f64 { - match args.get(i) { - Some(&v) => JSValue::from_bits(v.to_bits()).to_number(), - None => f64::NAN, - } -} - -/// `args[i]` coerced to an integer for a Temporal numeric field. Absent / -/// non-finite → 0 (Temporal treats missing duration/time fields as 0). -#[inline] -pub(crate) fn int_arg(args: &[f64], i: usize) -> i64 { - let n = num_arg(args, i); - if n.is_finite() { - n.trunc() as i64 - } else { - 0 - } -} - /// Saturate an integer Temporal time field into a `u8` slot: any value outside /// `0..=u8::MAX` maps to `u8::MAX`. Every `u8` time field's valid maximum is /// below 255, so `temporal_rs`'s range check then rejects it with a RangeError — diff --git a/crates/perry-runtime/src/text.rs b/crates/perry-runtime/src/text.rs index 62f705c9be..d382dc7cbf 100644 --- a/crates/perry-runtime/src/text.rs +++ b/crates/perry-runtime/src/text.rs @@ -602,18 +602,23 @@ fn throw_invalid_encoded_data(encoding: &str) -> ! { /// Keepalive anchors — these `#[no_mangle]` fns are only called from /// generated `.o`, so the auto-optimize whole-program bitcode rebuild -/// would dead-strip them without `#[cfg_attr(feature = "keepalive-anchors", used)]` retention (see +/// would dead-strip them without `#[used]` retention (see /// [[project_auto_optimize_keepalive_3320]]). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TEXT_DECODER_NEW: extern "C" fn(f64, f64, f64) -> i64 = js_text_decoder_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TEXT_DECODER_DECODE: extern "C" fn(f64, f64) -> i64 = js_text_decoder_decode_llvm; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TEXT_DECODER_ENCODING: extern "C" fn(f64) -> *mut StringHeader = js_text_decoder_encoding; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TEXT_DECODER_FATAL: extern "C" fn(f64) -> f64 = js_text_decoder_fatal; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TEXT_DECODER_IGNORE_BOM: extern "C" fn(f64) -> f64 = js_text_decoder_ignore_bom; /// TextDecoder / TextEncoder registry-handle property surface for VALUE diff --git a/crates/perry-runtime/src/tls.rs b/crates/perry-runtime/src/tls.rs index 43ee85a141..5bd0c4be52 100644 --- a/crates/perry-runtime/src/tls.rs +++ b/crates/perry-runtime/src/tls.rs @@ -567,18 +567,24 @@ pub extern "C" fn js_tls_check_server_identity(hostname: f64, cert: f64) -> f64 // Keep-alive anchors so the auto-optimize bitcode rebuild does not dead-strip // these codegen-emitted `#[no_mangle]` runtime helpers (referenced from the // native dispatch table in perry-codegen). -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_GET_CIPHERS: extern "C" fn() -> f64 = js_tls_get_ciphers; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_GET_CA_CERTIFICATES: extern "C" fn(f64) -> f64 = js_tls_get_ca_certificates; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_SET_DEFAULT_CA_CERTIFICATES: extern "C" fn(f64) -> f64 = js_tls_set_default_ca_certificates; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_CREATE_SECURE_CONTEXT: extern "C" fn(f64) -> f64 = js_tls_create_secure_context; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_SECURE_CONTEXT_NEW: extern "C" fn(f64) -> f64 = js_tls_secure_context_new; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TLS_CHECK_SERVER_IDENTITY: extern "C" fn(f64, f64) -> f64 = js_tls_check_server_identity; diff --git a/crates/perry-runtime/src/tty.rs b/crates/perry-runtime/src/tty.rs index c38d04cf85..a44e80a90a 100644 --- a/crates/perry-runtime/src/tty.rs +++ b/crates/perry-runtime/src/tty.rs @@ -672,20 +672,18 @@ fn ensure_tty_prototypes() { read_keys.as_ptr(), read_keys.len() as u32, ); - unsafe { - crate::object::js_object_set_field( - read_proto, - 1, - JSValue::from_bits( - closure_value( - js_tty_read_stream_set_raw_mode as *const u8, - "setRawMode", - 1, - ) - .to_bits(), - ), - ); - } + crate::object::js_object_set_field( + read_proto, + 1, + JSValue::from_bits( + closure_value( + js_tty_read_stream_set_raw_mode as *const u8, + "setRawMode", + 1, + ) + .to_bits(), + ), + ); crate::object::class_prototype_object_root_store(CLASS_ID_TTY_READ_STREAM, read_proto); let write_keys = b"constructor\0isTTY\0getColorDepth\0hasColors\0_refreshSize\0cursorTo\0moveCursor\0clearLine\0clearScreenDown\0getWindowSize\0"; @@ -695,92 +693,88 @@ fn ensure_tty_prototypes() { write_keys.as_ptr(), write_keys.len() as u32, ); - unsafe { - crate::object::js_object_set_field(write_proto, 1, JSValue::from_bits(TAG_TRUE)); - crate::object::js_object_set_field( - write_proto, - 2, - JSValue::from_bits( - closure_value( - js_tty_write_stream_get_color_depth as *const u8, - "getColorDepth", - 1, - ) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 3, - JSValue::from_bits( - closure_value(js_tty_write_stream_has_colors as *const u8, "hasColors", 2) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 4, - JSValue::from_bits( - closure_value( - js_tty_write_stream_refresh_size as *const u8, - "_refreshSize", - 0, - ) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 5, - JSValue::from_bits( - closure_value(js_tty_write_stream_cursor_to as *const u8, "cursorTo", 3).to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 6, - JSValue::from_bits( - closure_value( - js_tty_write_stream_move_cursor as *const u8, - "moveCursor", - 3, - ) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 7, - JSValue::from_bits( - closure_value(js_tty_write_stream_clear_line as *const u8, "clearLine", 2) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 8, - JSValue::from_bits( - closure_value( - js_tty_write_stream_clear_screen_down as *const u8, - "clearScreenDown", - 1, - ) - .to_bits(), - ), - ); - crate::object::js_object_set_field( - write_proto, - 9, - JSValue::from_bits( - closure_value( - js_tty_write_stream_get_window_size as *const u8, - "getWindowSize", - 0, - ) - .to_bits(), - ), - ); - } + crate::object::js_object_set_field(write_proto, 1, JSValue::from_bits(TAG_TRUE)); + crate::object::js_object_set_field( + write_proto, + 2, + JSValue::from_bits( + closure_value( + js_tty_write_stream_get_color_depth as *const u8, + "getColorDepth", + 1, + ) + .to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 3, + JSValue::from_bits( + closure_value(js_tty_write_stream_has_colors as *const u8, "hasColors", 2).to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 4, + JSValue::from_bits( + closure_value( + js_tty_write_stream_refresh_size as *const u8, + "_refreshSize", + 0, + ) + .to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 5, + JSValue::from_bits( + closure_value(js_tty_write_stream_cursor_to as *const u8, "cursorTo", 3).to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 6, + JSValue::from_bits( + closure_value( + js_tty_write_stream_move_cursor as *const u8, + "moveCursor", + 3, + ) + .to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 7, + JSValue::from_bits( + closure_value(js_tty_write_stream_clear_line as *const u8, "clearLine", 2).to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 8, + JSValue::from_bits( + closure_value( + js_tty_write_stream_clear_screen_down as *const u8, + "clearScreenDown", + 1, + ) + .to_bits(), + ), + ); + crate::object::js_object_set_field( + write_proto, + 9, + JSValue::from_bits( + closure_value( + js_tty_write_stream_get_window_size as *const u8, + "getWindowSize", + 0, + ) + .to_bits(), + ), + ); crate::object::class_prototype_object_root_store(CLASS_ID_TTY_WRITE_STREAM, write_proto); } @@ -803,7 +797,7 @@ pub(crate) fn attach_tty_constructor_prototype(constructor_value: f64, name: &st if proto.is_null() { return; } - unsafe { + { crate::object::js_object_set_field( proto, 0, @@ -864,10 +858,8 @@ pub extern "C" fn js_tty_read_stream_new(fd: f64) -> f64 { keys.as_ptr(), keys.len() as u32, ); - unsafe { - crate::object::js_object_set_field(obj, 0, JSValue::from_bits(TAG_FALSE)); - crate::object::js_object_set_field(obj, 1, JSValue::from_bits(TAG_TRUE)); - } + crate::object::js_object_set_field(obj, 0, JSValue::from_bits(TAG_FALSE)); + crate::object::js_object_set_field(obj, 1, JSValue::from_bits(TAG_TRUE)); ptr_value(obj) } @@ -1115,7 +1107,7 @@ pub extern "C" fn js_tty_write_stream_remove_all_listeners( pub fn throw_invalid_fd(fd: f64) -> ! { let obj = crate::object::js_object_alloc(crate::error::CLASS_ID_RANGE_ERROR, 4); - unsafe { + { crate::object::js_register_class_extends_error(crate::error::CLASS_ID_RANGE_ERROR); let str_val = |s: &str| -> f64 { let ptr = crate::string::js_string_from_bytes(s.as_ptr(), s.len() as u32); diff --git a/crates/perry-runtime/src/tui/layout.rs b/crates/perry-runtime/src/tui/layout.rs index 0bd1f11f29..c0ef8712e8 100644 --- a/crates/perry-runtime/src/tui/layout.rs +++ b/crates/perry-runtime/src/tui/layout.rs @@ -74,7 +74,7 @@ fn build_taffy_tree( let style = Style { size: Size { width: length(cols), - height: length(1.0), + height: length(1.0_f32), }, ..Default::default() }; diff --git a/crates/perry-runtime/src/typed_feedback.rs b/crates/perry-runtime/src/typed_feedback.rs index eadb460c23..a2a8d83bc4 100644 --- a/crates/perry-runtime/src/typed_feedback.rs +++ b/crates/perry-runtime/src/typed_feedback.rs @@ -1994,7 +1994,8 @@ pub extern "C" fn js_typed_feedback_packed_f64_range_loop_guard( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_FEEDBACK_PACKED_F64_RANGE_LOOP_GUARD: extern "C" fn( u64, f64, @@ -2035,7 +2036,8 @@ pub extern "C" fn js_typed_feedback_packed_i32_array_loop_guard( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_FEEDBACK_PACKED_I32_ARRAY_LOOP_GUARD: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_i32_array_loop_guard; @@ -2072,7 +2074,8 @@ pub extern "C" fn js_typed_feedback_packed_u32_array_loop_guard( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_FEEDBACK_PACKED_U32_ARRAY_LOOP_GUARD: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; diff --git a/crates/perry-runtime/src/typed_feedback/guards.rs b/crates/perry-runtime/src/typed_feedback/guards.rs index 39bbbae1e9..dc757e4694 100644 --- a/crates/perry-runtime/src/typed_feedback/guards.rs +++ b/crates/perry-runtime/src/typed_feedback/guards.rs @@ -1036,21 +1036,30 @@ pub extern "C" fn js_typed_feedback_closure_direct_call_guard( // whole-program thin-LTO + `strip=true` build internalizes + dead-strips them // — dangling the codegen call at final link (`Undefined symbols: // _js_typed_feedback_class_field_set_guard` for any class-field program). -// `typed_feedback.rs`'s `#[cfg_attr(feature = "keepalive-anchors", used)]` block covers the helpers defined there; +// `typed_feedback.rs`'s `#[used]` block covers the helpers defined there; // these typed fn-pointer statics extend the same `@llvm.used` retention to the // guard helpers defined here. (A `usize`/`*const()` cast does NOT survive // thin-LTO — only individual typed fn-pointer statics keep the symbol // external.) The statics must mirror each guard's exact signature, so keep // them in sync if a guard's parameter list changes. #[rustfmt::skip] +#[cfg(feature = "keepalive-anchors")] mod keep_guard_symbols { use super::*; - #[cfg_attr(feature = "keepalive-anchors", used)] static G0: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> i32 = js_typed_feedback_class_field_get_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static G1: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) -> i32 = js_typed_feedback_class_field_set_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static G1C: extern "C" fn(u64, u64, u64, f64) = js_class_field_set_fallback; - #[cfg_attr(feature = "keepalive-anchors", used)] static G1D: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) = js_class_field_set_ic; - #[cfg_attr(feature = "keepalive-anchors", used)] static G1E: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> f64 = js_class_field_get_ic; - #[cfg_attr(feature = "keepalive-anchors", used)] static G2: unsafe extern "C" fn(u64, f64, u32, *const ArrayHeader, *const i8, usize, *const u8) -> i32 = js_typed_feedback_method_direct_call_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static G3: extern "C" fn(u64, f64, *const u8, u32, u32) -> i32 = js_typed_feedback_closure_direct_call_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static G4: unsafe extern "C" fn(f64, u32, *const ArrayHeader) -> i32 = js_method_direct_shape_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static G0: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> i32 = js_typed_feedback_class_field_get_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static G1: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) -> i32 = js_typed_feedback_class_field_set_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static G1C: extern "C" fn(u64, u64, u64, f64) = js_class_field_set_fallback; + #[cfg(feature = "keepalive-anchors")] +#[used] static G1D: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) = js_class_field_set_ic; + #[cfg(feature = "keepalive-anchors")] +#[used] static G1E: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> f64 = js_class_field_get_ic; + #[cfg(feature = "keepalive-anchors")] +#[used] static G2: unsafe extern "C" fn(u64, f64, u32, *const ArrayHeader, *const i8, usize, *const u8) -> i32 = js_typed_feedback_method_direct_call_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static G3: extern "C" fn(u64, f64, *const u8, u32, u32) -> i32 = js_typed_feedback_closure_direct_call_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static G4: unsafe extern "C" fn(f64, u32, *const ArrayHeader) -> i32 = js_method_direct_shape_guard; } diff --git a/crates/perry-runtime/src/typed_feedback/tests.rs b/crates/perry-runtime/src/typed_feedback/tests.rs index dee670913c..cddc80e03d 100644 --- a/crates/perry-runtime/src/typed_feedback/tests.rs +++ b/crates/perry-runtime/src/typed_feedback/tests.rs @@ -995,15 +995,15 @@ fn assert_lto_keepalive_anchor(src: &str, static_name: &str, signature: &str, ta let static_pos = src .find(static_name) .unwrap_or_else(|| panic!("missing keepalive static {static_name} for {target}")); - // Lookback must cover the full gated attribute line above the static - // (`#[cfg_attr(feature = "keepalive-anchors", used)]` + newline). + // Lookback must cover both gated keepalive attributes above the static. let start = static_pos.saturating_sub(96); let end = (static_pos + 512).min(src.len()); let window = &src[start..end]; assert!( - window.contains(r#"#[cfg_attr(feature = "keepalive-anchors", used)]"#), - "keepalive static {static_name} for {target} lacks the feature-gated #[used] \ - (cfg_attr keepalive-anchors) attribute" + window.contains(r#"#[cfg(feature = "keepalive-anchors")]"#) + && window.contains(r#"#[used]"#), + "keepalive static {static_name} for {target} lacks the keepalive-anchors \ + gate and #[used] attribute" ); assert!( window.contains(signature), diff --git a/crates/perry-runtime/src/typed_feedback/trace.rs b/crates/perry-runtime/src/typed_feedback/trace.rs index 4597834d1a..3e4a07661b 100644 --- a/crates/perry-runtime/src/typed_feedback/trace.rs +++ b/crates/perry-runtime/src/typed_feedback/trace.rs @@ -352,7 +352,7 @@ pub extern "C" fn js_typed_feedback_maybe_dump_trace() { // symbol, leaving the codegen call dangling (`Undefined symbols: // _js_typed_feedback_native_call_method` etc. at final link — which is exactly // how an instrumented async program failed to link under auto-optimize). The -// `#[cfg_attr(feature = "keepalive-anchors", used)]` typed fn-pointer statics below take the address of each helper, +// `#[used]` typed fn-pointer statics below take the address of each helper, // landing the functions themselves in `@llvm.used` so thin-LTO keeps them // external (not internalized) and the linker's `-dead_strip` honors them — the // same proven retention mechanism as `value/dyn_index.rs` / `process.rs` @@ -362,42 +362,75 @@ pub extern "C" fn js_typed_feedback_maybe_dump_trace() { // array forms were verified failing under auto-optimize). Function-pointer // types are `Sync`, so no wrapper is needed. #[rustfmt::skip] +#[cfg(feature = "keepalive-anchors")] mod keep_typed_feedback { use super::*; use crate::typed_feedback::guards::{ js_typed_feedback_native_call_method_apply_by_id, js_typed_feedback_native_call_method_by_id, }; - #[cfg_attr(feature = "keepalive-anchors", used)] static K00: extern "C" fn(u64, u32, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize) = js_typed_feedback_register_site; - #[cfg_attr(feature = "keepalive-anchors", used)] static K01: extern "C" fn(u64) = js_typed_feedback_record_guard_pass; - #[cfg_attr(feature = "keepalive-anchors", used)] static K02: extern "C" fn(u64) = js_typed_feedback_record_guard_fail; - #[cfg_attr(feature = "keepalive-anchors", used)] static K03: extern "C" fn(u64) = js_typed_feedback_record_fallback_call; - #[cfg_attr(feature = "keepalive-anchors", used)] static K04: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_get; - #[cfg_attr(feature = "keepalive-anchors", used)] static K05: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_set; - #[cfg_attr(feature = "keepalive-anchors", used)] static K06: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) -> f64 = js_typed_feedback_object_get_field_by_name_f64; - #[cfg_attr(feature = "keepalive-anchors", used)] static K07: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name; - #[cfg_attr(feature = "keepalive-anchors", used)] static K08: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name_fast; - #[cfg_attr(feature = "keepalive-anchors", used)] static K09: unsafe extern "C" fn(u64, f64, *const i8, usize, *const f64, usize) -> f64 = js_typed_feedback_native_call_method; - #[cfg_attr(feature = "keepalive-anchors", used)] static K10: unsafe extern "C" fn(u64, f64, *const i8, usize, i64) -> f64 = js_typed_feedback_native_call_method_apply; - #[cfg_attr(feature = "keepalive-anchors", used)] static K11: extern "C" fn(u64, *const ArrayHeader, u32) -> f64 = js_typed_feedback_array_get_f64; - #[cfg_attr(feature = "keepalive-anchors", used)] static K12: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_plain_array_index_get_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K13: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_numeric_array_index_get_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K14: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_f64_array_loop_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K15: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K16: extern "C" fn(u64, f64, f64) -> f64 = js_typed_feedback_array_index_get_fallback_boxed; - #[cfg_attr(feature = "keepalive-anchors", used)] static K17: extern "C" fn(u64, *mut ArrayHeader, u32, f64) = js_typed_feedback_array_set_f64; - #[cfg_attr(feature = "keepalive-anchors", used)] static K18: extern "C" fn(u64, *mut ArrayHeader, u32, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_f64_extend; - #[cfg_attr(feature = "keepalive-anchors", used)] static K19: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_plain_array_index_set_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K20: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_numeric_array_index_set_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K21: extern "C" fn(u64, f64, f64) -> i32 = js_typed_feedback_numeric_array_push_guard; - #[cfg_attr(feature = "keepalive-anchors", used)] static K22: extern "C" fn(u64, f64, f64, f64) -> f64 = js_typed_feedback_array_index_set_fallback_boxed; - #[cfg_attr(feature = "keepalive-anchors", used)] static K23: extern "C" fn(u64, *const ArrayHeader, u32) = js_typed_feedback_observe_array_element; - #[cfg_attr(feature = "keepalive-anchors", used)] static K24: extern "C" fn(u64, *mut ArrayHeader, *const crate::StringHeader, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_string_key; - #[cfg_attr(feature = "keepalive-anchors", used)] static K25: extern "C" fn(u64, *mut ArrayHeader, f64, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_index_or_string; - #[cfg_attr(feature = "keepalive-anchors", used)] static K26: extern "C" fn(u64, i64, f64, f64) = js_typed_feedback_object_set_index_polymorphic; - #[cfg_attr(feature = "keepalive-anchors", used)] static K27: extern "C" fn(u64, *mut ObjectHeader, u32, *const crate::StringHeader, f64) = js_typed_feedback_object_set_unboxed_f64_field; - #[cfg_attr(feature = "keepalive-anchors", used)] static K28: extern "C" fn(u64, f64) -> f64 = js_typed_feedback_observe_helper_return; - #[cfg_attr(feature = "keepalive-anchors", used)] static K29: extern "C" fn() = js_typed_feedback_maybe_dump_trace; - #[cfg_attr(feature = "keepalive-anchors", used)] static K30: unsafe extern "C" fn(u64, f64, i64, *const f64, usize) -> f64 = js_typed_feedback_native_call_method_by_id; - #[cfg_attr(feature = "keepalive-anchors", used)] static K31: unsafe extern "C" fn(u64, f64, i64, i64) -> f64 = js_typed_feedback_native_call_method_apply_by_id; + #[cfg(feature = "keepalive-anchors")] +#[used] static K00: extern "C" fn(u64, u32, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize) = js_typed_feedback_register_site; + #[cfg(feature = "keepalive-anchors")] +#[used] static K01: extern "C" fn(u64) = js_typed_feedback_record_guard_pass; + #[cfg(feature = "keepalive-anchors")] +#[used] static K02: extern "C" fn(u64) = js_typed_feedback_record_guard_fail; + #[cfg(feature = "keepalive-anchors")] +#[used] static K03: extern "C" fn(u64) = js_typed_feedback_record_fallback_call; + #[cfg(feature = "keepalive-anchors")] +#[used] static K04: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_get; + #[cfg(feature = "keepalive-anchors")] +#[used] static K05: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_set; + #[cfg(feature = "keepalive-anchors")] +#[used] static K06: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) -> f64 = js_typed_feedback_object_get_field_by_name_f64; + #[cfg(feature = "keepalive-anchors")] +#[used] static K07: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name; + #[cfg(feature = "keepalive-anchors")] +#[used] static K08: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name_fast; + #[cfg(feature = "keepalive-anchors")] +#[used] static K09: unsafe extern "C" fn(u64, f64, *const i8, usize, *const f64, usize) -> f64 = js_typed_feedback_native_call_method; + #[cfg(feature = "keepalive-anchors")] +#[used] static K10: unsafe extern "C" fn(u64, f64, *const i8, usize, i64) -> f64 = js_typed_feedback_native_call_method_apply; + #[cfg(feature = "keepalive-anchors")] +#[used] static K11: extern "C" fn(u64, *const ArrayHeader, u32) -> f64 = js_typed_feedback_array_get_f64; + #[cfg(feature = "keepalive-anchors")] +#[used] static K12: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_plain_array_index_get_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K13: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_numeric_array_index_get_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K14: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_f64_array_loop_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K15: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K16: extern "C" fn(u64, f64, f64) -> f64 = js_typed_feedback_array_index_get_fallback_boxed; + #[cfg(feature = "keepalive-anchors")] +#[used] static K17: extern "C" fn(u64, *mut ArrayHeader, u32, f64) = js_typed_feedback_array_set_f64; + #[cfg(feature = "keepalive-anchors")] +#[used] static K18: extern "C" fn(u64, *mut ArrayHeader, u32, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_f64_extend; + #[cfg(feature = "keepalive-anchors")] +#[used] static K19: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_plain_array_index_set_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K20: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_numeric_array_index_set_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K21: extern "C" fn(u64, f64, f64) -> i32 = js_typed_feedback_numeric_array_push_guard; + #[cfg(feature = "keepalive-anchors")] +#[used] static K22: extern "C" fn(u64, f64, f64, f64) -> f64 = js_typed_feedback_array_index_set_fallback_boxed; + #[cfg(feature = "keepalive-anchors")] +#[used] static K23: extern "C" fn(u64, *const ArrayHeader, u32) = js_typed_feedback_observe_array_element; + #[cfg(feature = "keepalive-anchors")] +#[used] static K24: extern "C" fn(u64, *mut ArrayHeader, *const crate::StringHeader, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_string_key; + #[cfg(feature = "keepalive-anchors")] +#[used] static K25: extern "C" fn(u64, *mut ArrayHeader, f64, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_index_or_string; + #[cfg(feature = "keepalive-anchors")] +#[used] static K26: extern "C" fn(u64, i64, f64, f64) = js_typed_feedback_object_set_index_polymorphic; + #[cfg(feature = "keepalive-anchors")] +#[used] static K27: extern "C" fn(u64, *mut ObjectHeader, u32, *const crate::StringHeader, f64) = js_typed_feedback_object_set_unboxed_f64_field; + #[cfg(feature = "keepalive-anchors")] +#[used] static K28: extern "C" fn(u64, f64) -> f64 = js_typed_feedback_observe_helper_return; + #[cfg(feature = "keepalive-anchors")] +#[used] static K29: extern "C" fn() = js_typed_feedback_maybe_dump_trace; + #[cfg(feature = "keepalive-anchors")] +#[used] static K30: unsafe extern "C" fn(u64, f64, i64, *const f64, usize) -> f64 = js_typed_feedback_native_call_method_by_id; + #[cfg(feature = "keepalive-anchors")] +#[used] static K31: unsafe extern "C" fn(u64, f64, i64, i64) -> f64 = js_typed_feedback_native_call_method_apply_by_id; } diff --git a/crates/perry-runtime/src/typedarray/access.rs b/crates/perry-runtime/src/typedarray/access.rs index d46968c323..4d2af8d797 100644 --- a/crates/perry-runtime/src/typedarray/access.rs +++ b/crates/perry-runtime/src/typedarray/access.rs @@ -86,8 +86,9 @@ pub extern "C" fn js_typed_array_read_int32(ta: *const TypedArrayHeader, index: // Codegen-only export: the inline checked-i32 read emits the call in // `perry-codegen/src/expr/i32_fast_path.rs`; a whole-program bitcode link is // otherwise free to internalize and dead-strip it (it has no internal Rust -// caller). The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it, mirroring the getter above. -#[cfg_attr(feature = "keepalive-anchors", used)] +// caller). The `#[used]` anchor pins it, mirroring the getter above. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_ARRAY_READ_INT32: extern "C" fn(*const TypedArrayHeader, i32) -> i32 = js_typed_array_read_int32; @@ -120,7 +121,8 @@ pub extern "C" fn js_typed_array_read_f64(ta: *const TypedArrayHeader, index: i3 } // Codegen-only export (see the i32 sibling above): pin under whole-program LTO. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_ARRAY_READ_F64: extern "C" fn(*const TypedArrayHeader, i32) -> f64 = js_typed_array_read_f64; @@ -149,8 +151,9 @@ pub extern "C" fn js_typed_array_index_get_dynamic(ta: *const TypedArrayHeader, // `js_dyn_index_get`, this export has zero internal Rust callers — it is only // invoked from generated LLVM IR (codegen emits the call in // `perry-codegen/src/expr/index_get.rs`), so a whole-program bitcode link is -// free to internalize and dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it. -#[cfg_attr(feature = "keepalive-anchors", used)] +// free to internalize and dead-strip it. The `#[used]` anchor pins it. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_ARRAY_INDEX_GET_DYNAMIC: extern "C" fn(*const TypedArrayHeader, f64) -> f64 = js_typed_array_index_get_dynamic; @@ -561,7 +564,8 @@ pub extern "C" fn js_uint8array_index_get_value( // #6088: force-keep the JS-value Uint8Array index getter under LTO / // auto-optimize — it has zero internal Rust callers (codegen emits the only // call), so a whole-program bitcode link is otherwise free to dead-strip it. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_UINT8ARRAY_INDEX_GET_VALUE: extern "C" fn(*const TypedArrayHeader, i32) -> f64 = js_uint8array_index_get_value; diff --git a/crates/perry-runtime/src/typedarray/mod.rs b/crates/perry-runtime/src/typedarray/mod.rs index ab04256e01..c7d191117b 100644 --- a/crates/perry-runtime/src/typedarray/mod.rs +++ b/crates/perry-runtime/src/typedarray/mod.rs @@ -9,7 +9,6 @@ //! Pointers are NaN-boxed with POINTER_TAG (0x7FFD) and tracked in //! TYPED_ARRAY_REGISTRY for `instanceof` and console.log formatting. -use std::alloc::Layout; use std::cell::RefCell; use std::ptr; use std::sync::atomic::{AtomicU64, Ordering}; @@ -697,24 +696,12 @@ unsafe fn native_memory_copy_accepts_buffer(addr: usize) -> bool { true } -fn ta_layout(capacity: u32, elem_size: usize) -> Layout { - let total = std::mem::size_of::() + (capacity as usize) * elem_size; - let total = total.max(std::mem::size_of::() + elem_size); - Layout::from_size_align(total, 8).unwrap() -} - #[inline] fn typed_array_payload_size(capacity: u32, elem_size: usize) -> usize { let total = std::mem::size_of::() + (capacity as usize) * elem_size; total.max(std::mem::size_of::() + elem_size) } -#[inline] -fn typed_array_gc_total_size(capacity: u32, elem_size: usize) -> usize { - let payload = typed_array_payload_size(capacity, elem_size); - (crate::gc::GC_HEADER_SIZE + payload + 7) & !7 -} - /// Allocate a zero-filled typed array of `length` elements. pub fn typed_array_alloc(kind: u8, length: u32) -> *mut TypedArrayHeader { let elem_size = elem_size_for_kind(kind); diff --git a/crates/perry-runtime/src/typedarray_props.rs b/crates/perry-runtime/src/typedarray_props.rs index ddbea5d240..d15f041761 100644 --- a/crates/perry-runtime/src/typedarray_props.rs +++ b/crates/perry-runtime/src/typedarray_props.rs @@ -747,7 +747,8 @@ pub extern "C" fn js_typed_array_index_set_dynamic( } } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_TYPED_ARRAY_INDEX_SET_DYNAMIC: extern "C" fn( *mut TypedArrayHeader, f64, diff --git a/crates/perry-runtime/src/url/abort.rs b/crates/perry-runtime/src/url/abort.rs index c5952d7946..7fd7c364c9 100644 --- a/crates/perry-runtime/src/url/abort.rs +++ b/crates/perry-runtime/src/url/abort.rs @@ -635,13 +635,16 @@ pub extern "C" fn js_abort_signal_any( // #2582: keepalive anchors so the auto-optimize whole-program LLVM bitcode // rebuild doesn't internalize + dead-strip these codegen-only `#[no_mangle]` // entry points (see project_auto_optimize_keepalive_3320). These are only -// referenced from generated `.o`, so without `#[cfg_attr(feature = "keepalive-anchors", used)]` they vanish. -#[cfg_attr(feature = "keepalive-anchors", used)] +// referenced from generated `.o`, so without `#[used]` they vanish. +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ABORT_SIGNAL_ABORT: extern "C" fn(f64) -> *mut ObjectHeader = js_abort_signal_abort; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ABORT_SIGNAL_ANY: extern "C" fn(*mut crate::array::ArrayHeader) -> *mut ObjectHeader = js_abort_signal_any; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_ABORT_SIGNAL_THROW_IF_ABORTED: extern "C" fn(*mut ObjectHeader) -> f64 = js_abort_signal_throw_if_aborted; diff --git a/crates/perry-runtime/src/url/mod.rs b/crates/perry-runtime/src/url/mod.rs index dc266dd774..0a4c033d48 100644 --- a/crates/perry-runtime/src/url/mod.rs +++ b/crates/perry-runtime/src/url/mod.rs @@ -146,19 +146,6 @@ pub(crate) fn object_prop_f64(obj: *mut ObjectHeader, key: &str) -> f64 { crate::object::js_object_get_field_by_name_f64(obj, key_ptr) } -/// Read a `*mut StringHeader` (NULL → empty) into a Rust `String`. -pub(crate) fn string_header_to_string(value: *mut crate::StringHeader) -> String { - if value.is_null() { - return String::new(); - } - unsafe { - let len = (*value).byte_len as usize; - let data_ptr = (value as *const u8).add(std::mem::size_of::()); - let slice = std::slice::from_raw_parts(data_ptr, len); - String::from_utf8_lossy(slice).into_owned() - } -} - /// WHATWG host canonicalization via the `url` crate (#3056, #3059). /// /// Perry's URL uses a custom hostname parser that runs only IDNA diff --git a/crates/perry-runtime/src/url/node_compat.rs b/crates/perry-runtime/src/url/node_compat.rs index 7a37cf7b29..57320c16d7 100644 --- a/crates/perry-runtime/src/url/node_compat.rs +++ b/crates/perry-runtime/src/url/node_compat.rs @@ -839,21 +839,22 @@ const LEGACY_URL_KEYS: [&str; 12] = [ "pathname", "path", "href", ]; -fn string_or_null(value: String) -> f64 { - if value.is_empty() { - null_f64() - } else { - create_string_f64(&value) - } -} - fn create_legacy_url_object(values: [f64; 12]) -> *mut ObjectHeader { - let obj = js_object_alloc(0, LEGACY_URL_KEYS.len() as u32); - let mut keys = js_array_alloc(LEGACY_URL_KEYS.len() as u32); + let scope = crate::gc::RuntimeHandleScope::new(); + let value_handles = scope.root_nanbox_f64_slice(&values); + let obj_handle = scope.root_raw_mut_ptr(js_object_alloc(0, LEGACY_URL_KEYS.len() as u32)); + let keys_handle = scope.root_raw_mut_ptr(js_array_alloc(LEGACY_URL_KEYS.len() as u32)); for (index, key) in LEGACY_URL_KEYS.iter().enumerate() { - keys = js_array_push_f64(keys, create_string_f64(key)); - js_object_set_field_f64(obj, index as u32, values[index]); + let keys = keys_handle.get_raw_mut_ptr::(); + keys_handle.set_raw_mut_ptr(js_array_push_f64(keys, create_string_f64(key))); + js_object_set_field_f64( + obj_handle.get_raw_mut_ptr::(), + index as u32, + value_handles[index].get_nanbox_f64(), + ); } + let obj = obj_handle.get_raw_mut_ptr::(); + let keys = keys_handle.get_raw_mut_ptr::(); js_object_set_keys(obj, keys); obj } @@ -941,8 +942,8 @@ pub extern "C" fn js_url_legacy_parse( crate::value::js_nanbox_pointer(obj as i64) } -/// `Some("")` is a legitimate value (`file:///a` has `host === ""`), so this is -/// NOT `string_or_null`, which collapses the empty string to null. +/// `Some("")` is a legitimate value (`file:///a` has `host === ""`), so the +/// empty string is preserved rather than collapsed to null. fn opt_string_f64(v: Option) -> f64 { match v { Some(s) => create_string_f64(&s), @@ -950,10 +951,6 @@ fn opt_string_f64(v: Option) -> f64 { } } -fn protocol_null_or_slashes(input: &str, protocol_is_null: bool, host: &str) -> bool { - protocol_is_null || input.starts_with("//") || input.contains("://") || !host.is_empty() -} - /// WHATWG `URL.join` of `to` onto base `from`, or `None` when `from` isn't a /// parseable absolute URL. Cfg-paired: the off twin returns `None` (no `url` /// crate), so the caller falls back to the hand-rolled `resolve_url`. diff --git a/crates/perry-runtime/src/url/search_params.rs b/crates/perry-runtime/src/url/search_params.rs index 193372df58..dcad2ff909 100644 --- a/crates/perry-runtime/src/url/search_params.rs +++ b/crates/perry-runtime/src/url/search_params.rs @@ -46,7 +46,7 @@ pub(crate) fn resolve_search_params_receiver(params: *mut ObjectHeader) -> *mut URL_SEARCH_PARAMS_BACKING_KEY.len() as u32, ); let params = params_handle.get_raw_mut_ptr::(); - let backing = unsafe { crate::object::js_object_get_field_by_name(params, key) }; + let backing = crate::object::js_object_get_field_by_name(params, key); if backing.is_pointer() { let ptr = backing.as_pointer::() as *mut ObjectHeader; if !ptr.is_null() { @@ -79,7 +79,7 @@ pub(crate) fn url_search_params_backing_of(object: f64) -> Option<*mut ObjectHea URL_SEARCH_PARAMS_BACKING_KEY.len() as u32, ); let obj = obj_handle.get_raw_mut_ptr::(); - let backing = unsafe { crate::object::js_object_get_field_by_name(obj, key) }; + let backing = crate::object::js_object_get_field_by_name(obj, key); if backing.is_pointer() { let ptr = backing.as_pointer::() as *mut ObjectHeader; if !ptr.is_null() { @@ -121,13 +121,14 @@ pub extern "C" fn js_url_search_params_subclass_init(this: f64, init: f64) -> f6 let key = key_handle.get_raw_const_ptr::(); let backing_ptr = backing_handle.get_raw_mut_ptr::(); let backing_f64 = crate::value::js_nanbox_pointer(backing_ptr as i64); - unsafe { crate::object::js_object_set_field_by_name(this_obj, key, backing_f64) }; + crate::object::js_object_set_field_by_name(this_obj, key, backing_f64); undef } /// Reached only from codegen-emitted IR (the `Expr::SuperCall` URLSearchParams /// arm); pin it so the auto-optimize bitcode rebuild's dead-strip can't drop it. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_URL_SEARCH_PARAMS_SUBCLASS_INIT: extern "C" fn(f64, f64) -> f64 = js_url_search_params_subclass_init; diff --git a/crates/perry-runtime/src/util_promisify.rs b/crates/perry-runtime/src/util_promisify.rs index 00a882b1ac..322caeae16 100644 --- a/crates/perry-runtime/src/util_promisify.rs +++ b/crates/perry-runtime/src/util_promisify.rs @@ -763,9 +763,8 @@ fn callable_then_field(value: f64) -> Option { } let obj = addr as *const crate::object::ObjectHeader; let key = js_string_from_bytes(b"then".as_ptr(), 4); - let then_value = unsafe { - crate::object::js_object_get_field_by_name_f64(obj, key as *const crate::StringHeader) - }; + let then_value = + crate::object::js_object_get_field_by_name_f64(obj, key as *const crate::StringHeader); if is_callable_closure(then_value) { Some(then_value) } else { diff --git a/crates/perry-runtime/src/validators.rs b/crates/perry-runtime/src/validators.rs index 9bb5cb3ff6..b4f3ccb698 100644 --- a/crates/perry-runtime/src/validators.rs +++ b/crates/perry-runtime/src/validators.rs @@ -338,7 +338,7 @@ pub fn validate_port(value: f64, name: &str) -> u16 { // hard segfault rather than node's catchable `TypeError`. These helpers take // the *original* NaN-boxed value (as `f64`) plus the argument name, so codegen // can emit a `call void` validation BEFORE the unbox, throwing node's typed -// error instead of crashing. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors below keep them alive +// error instead of crashing. The `#[used]` anchors below keep them alive // through the auto-optimize whole-program rebuild (the bitcode internalizer // drops `#[no_mangle]` symbols only referenced from generated `.o`). // ============================================================================ @@ -423,12 +423,15 @@ pub unsafe extern "C" fn js_runtime_validate_integer_arg( validate_integer(value, &name, min, max); } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_STRING_ARG: unsafe extern "C" fn(f64, *const u8, u32) = js_runtime_validate_string_arg; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_CRYPTO_KEY_ARG: unsafe extern "C" fn(f64, *const u8, u32) = js_runtime_validate_crypto_key_arg; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_VALIDATE_INTEGER_ARG: unsafe extern "C" fn(f64, *const u8, u32, f64, f64) = js_runtime_validate_integer_arg; diff --git a/crates/perry-runtime/src/value/dyn_index.rs b/crates/perry-runtime/src/value/dyn_index.rs index e93413b77e..58544327fe 100644 --- a/crates/perry-runtime/src/value/dyn_index.rs +++ b/crates/perry-runtime/src/value/dyn_index.rs @@ -647,9 +647,12 @@ pub extern "C" fn js_is_undefined_or_bare_nan(value: f64) -> i32 { // link keeps these exports via the program's own undefined references, so // the anchors compile out there. Function-pointer types are `Sync`, so no // wrapper is needed. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DYN_INDEX_GET: extern "C" fn(f64, f64) -> f64 = js_dyn_index_get; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_DYN_INDEX_SET: extern "C" fn(f64, f64, f64) -> f64 = js_dyn_index_set; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_IS_UNDEFINED_OR_BARE_NAN: extern "C" fn(f64) -> i32 = js_is_undefined_or_bare_nan; diff --git a/crates/perry-runtime/src/value/dynamic_arith.rs b/crates/perry-runtime/src/value/dynamic_arith.rs index 90cde483be..061d39f8d2 100644 --- a/crates/perry-runtime/src/value/dynamic_arith.rs +++ b/crates/perry-runtime/src/value/dynamic_arith.rs @@ -913,15 +913,20 @@ pub unsafe extern "C" fn js_dynamic_ushr(a: f64, b: f64) -> f64 { (ai >> bi) as f64 } -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DYNAMIC_POW: unsafe extern "C" fn(f64, f64) -> f64 = js_dynamic_pow; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DYNAMIC_USHR: unsafe extern "C" fn(f64, f64) -> f64 = js_dynamic_ushr; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_DYNAMIC_BITNOT: unsafe extern "C" fn(f64) -> f64 = js_dynamic_bitnot; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_TO_NUMERIC: unsafe extern "C" fn(f64) -> f64 = js_to_numeric; -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_NUMERIC_STEP: unsafe extern "C" fn(f64, i32) -> f64 = js_numeric_step; #[cfg(test)] diff --git a/crates/perry-runtime/src/value/nanbox.rs b/crates/perry-runtime/src/value/nanbox.rs index 95e873c6b4..38e9828b34 100644 --- a/crates/perry-runtime/src/value/nanbox.rs +++ b/crates/perry-runtime/src/value/nanbox.rs @@ -351,7 +351,8 @@ pub extern "C" fn js_switch_strict_equals(a: f64, b: f64) -> i32 { // #1561-style force-keep: only generated IR calls this — see // value/dyn_index.rs for the rationale. -#[cfg_attr(feature = "keepalive-anchors", used)] +#[cfg(feature = "keepalive-anchors")] +#[used] static KEEP_JS_SWITCH_STRICT_EQUALS: extern "C" fn(f64, f64) -> i32 = js_switch_strict_equals; /// Check if a NaN-boxed f64 value represents a string. diff --git a/crates/perry-runtime/src/weakref.rs b/crates/perry-runtime/src/weakref.rs index c565b31234..d92af6ea00 100644 --- a/crates/perry-runtime/src/weakref.rs +++ b/crates/perry-runtime/src/weakref.rs @@ -1414,9 +1414,9 @@ pub extern "C" fn js_weakmap_init_iterable(map: f64, iterable: f64) -> f64 { let arr_handle = scope.root_nanbox_f64(arr_value); let arr_ptr = js_nanbox_get_pointer(arr_handle.get_nanbox_f64()) as *mut ArrayHeader; if !arr_ptr.is_null() { - let len = unsafe { js_array_length(arr_ptr) as usize }; + let len = js_array_length(arr_ptr) as usize; for i in 0..len { - let entry = unsafe { + let entry = { let arr = js_nanbox_get_pointer(arr_handle.get_nanbox_f64()) as *const ArrayHeader; js_array_get_f64(arr, i as u32) @@ -1723,9 +1723,9 @@ pub extern "C" fn js_weakset_init_iterable(set: f64, iterable: f64) -> f64 { let arr_handle = scope.root_nanbox_f64(arr_value); let arr_ptr = js_nanbox_get_pointer(arr_handle.get_nanbox_f64()) as *mut ArrayHeader; if !arr_ptr.is_null() { - let len = unsafe { js_array_length(arr_ptr) as usize }; + let len = js_array_length(arr_ptr) as usize; for i in 0..len { - let element = unsafe { + let element = { let arr = js_nanbox_get_pointer(arr_handle.get_nanbox_f64()) as *const ArrayHeader; js_array_get_f64(arr, i as u32) diff --git a/crates/perry-runtime/src/yoga.rs b/crates/perry-runtime/src/yoga.rs index 52b48cd4cc..78dde43e57 100644 --- a/crates/perry-runtime/src/yoga.rs +++ b/crates/perry-runtime/src/yoga.rs @@ -719,7 +719,8 @@ pub extern "C" fn js_yoga_get_computed_edge(id: f64, kind: f64, edge: f64) -> f6 // Typed statics (coercion, not a const ptr→int cast). macro_rules! keep { ($n:ident : $t:ty = $f:ident) => { - #[cfg_attr(feature = "keepalive-anchors", used)] + #[cfg(feature = "keepalive-anchors")] + #[used] static $n: $t = $f; }; } diff --git a/crates/perry-stdlib/src/commander.rs b/crates/perry-stdlib/src/commander.rs index d6152cacee..eb373fa5a7 100644 --- a/crates/perry-stdlib/src/commander.rs +++ b/crates/perry-stdlib/src/commander.rs @@ -598,7 +598,7 @@ pub extern "C" fn js_commander_args_array(handle: Handle) -> Handle { Some(cmd) => cmd.args.clone(), None => Vec::new(), }; - unsafe { + { let boxed: Vec = args .iter() .map(|a| { diff --git a/crates/perry-stdlib/src/common/dispatch.rs b/crates/perry-stdlib/src/common/dispatch.rs index 39c2c254c8..62c9efe736 100644 --- a/crates/perry-stdlib/src/common/dispatch.rs +++ b/crates/perry-stdlib/src/common/dispatch.rs @@ -72,17 +72,17 @@ pub(crate) unsafe fn pack_args_array(args: &[f64]) -> *mut perry_runtime::ArrayH arr_handle.get_raw_mut_ptr::() } -/// Shared `extern "C"` surface of the EventEmitter implementation. Both -/// perry-stdlib (`bundled-events`) and perry-ext-events export these exact -/// symbols, kept byte-identical per #3072. The dispatch arms below call -/// through the linker-resolved symbol instead of `crate::events::*` so that -/// when the well-known flip links perry-ext-events, dynamic dispatch -/// consults the SAME handle registry the constructors used. An in-crate call -/// always hit perry-stdlib's registry and returned `None` for ext-events -/// handles — every dynamic `.on`/`.emit`/`.setMaxListeners` on an emitter -/// silently no-op'd and method-value reads came back `undefined` (#4995). -/// Mirrors the sqlite duplicate-symbol contract noted in -/// `compile/optimized_libs.rs` (#643). +// Shared `extern "C"` surface of the EventEmitter implementation. Both +// perry-stdlib (`bundled-events`) and perry-ext-events export these exact +// symbols, kept byte-identical per #3072. The dispatch arms below call +// through the linker-resolved symbol instead of `crate::events::*` so that +// when the well-known flip links perry-ext-events, dynamic dispatch +// consults the SAME handle registry the constructors used. An in-crate call +// always hit perry-stdlib's registry and returned `None` for ext-events +// handles — every dynamic `.on`/`.emit`/`.setMaxListeners` on an emitter +// silently no-op'd and method-value reads came back `undefined` (#4995). +// Mirrors the sqlite duplicate-symbol contract noted in +// `compile/optimized_libs.rs` (#643). #[cfg(any(feature = "bundled-events", feature = "external-events-construct"))] extern "C" { pub(crate) fn js_event_emitter_is_handle(handle: i64) -> bool; diff --git a/crates/perry-stdlib/src/crypto/sign.rs b/crates/perry-stdlib/src/crypto/sign.rs index 362db90270..2248d5e62f 100644 --- a/crates/perry-stdlib/src/crypto/sign.rs +++ b/crates/perry-stdlib/src/crypto/sign.rs @@ -1,21 +1,5 @@ use super::*; -/// Resolve `(start, end)` byte indices from Node-style `offset` / `size` -/// arguments against a buffer of `total` bytes. Out-of-range values are -/// clamped to `[0, total]`. -pub(super) fn resolve_range( - total: usize, - offset: Option, - size: Option, -) -> (usize, usize) { - let start = offset.unwrap_or(0).min(total); - let end = match size { - Some(s) => start.saturating_add(s).min(total), - None => total, - }; - (start, end) -} - /// Create HMAC-SHA256 /// crypto.createHmac('sha256', key).update(data).digest('hex') -> string #[no_mangle] diff --git a/crates/perry-stdlib/src/fetch/dispatch.rs b/crates/perry-stdlib/src/fetch/dispatch.rs index 9d55f160d7..ef824cc38b 100644 --- a/crates/perry-stdlib/src/fetch/dispatch.rs +++ b/crates/perry-stdlib/src/fetch/dispatch.rs @@ -37,7 +37,7 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 { // payload came back all zeroes (#5435). Materialize the bytes into a heap // StringHeader so `js_response_new`'s lossless byte read recovers them. if let Some(bytes) = unsafe { body_value_buffer_bytes(value) } { - return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } as i64; + return js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) as i64; } // A Blob / File body contributes its raw bytes. Blob handles are handle-band // ids (>= FETCH_HANDLE_BAND_START, 0x40000), NOT real pointers, so they must @@ -50,8 +50,7 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 { let addr = jsval.as_pointer::() as usize; if perry_runtime::value::addr_class::is_handle_band(addr) { if let Some(bytes) = crate::fetch::blob_bytes_clone(addr) { - return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } - as i64; + return js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) as i64; } } } @@ -68,7 +67,7 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 { // kind == 1 ⇒ live ReadableStream. if crate::streams::js_stream_handle_kind(id) == 1 { let bytes = crate::streams::drain_readable_into_bytes(id); - return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } as i64; + return js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) as i64; } } // #5437: a Node `IncomingMessage` body — the request-body bridge Next.js's @@ -80,7 +79,7 @@ pub extern "C" fn js_response_body_init_ptr(value: f64) -> i64 { if let Some(bytes) = unsafe { incoming_message_raw_body_bytes(jsval.as_pointer::() as usize) } { - return unsafe { js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) } as i64; + return js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32) as i64; } } } diff --git a/crates/perry-stdlib/src/querystring.rs b/crates/perry-stdlib/src/querystring.rs index 30deb75f62..ace744b458 100644 --- a/crates/perry-stdlib/src/querystring.rs +++ b/crates/perry-stdlib/src/querystring.rs @@ -87,7 +87,7 @@ unsafe fn nanboxed_to_string(value: f64) -> Option { /// Allocate a heap StringHeader from a Rust `&str`. fn intern_string(s: &str) -> *mut StringHeader { - unsafe { js_string_from_bytes(s.as_ptr(), s.len() as u32) } + js_string_from_bytes(s.as_ptr(), s.len() as u32) } /// NaN-box a `*mut StringHeader` with STRING_TAG so it returns through @@ -405,7 +405,7 @@ fn resolve_max_keys(options: f64) -> Option { return Some(1000); } let key = intern_string("maxKeys"); - let max_keys = unsafe { js_object_get_field_by_name(obj, key) }; + let max_keys = js_object_get_field_by_name(obj, key); if max_keys.is_undefined() || max_keys.is_null() { return Some(1000); } @@ -628,7 +628,7 @@ fn querystring_scalar_to_string(value_bits: u64) -> String { if ptr.is_null() { return String::new(); } - let hdr = unsafe { perry_runtime::bigint::js_bigint_to_string(ptr) }; + let hdr = perry_runtime::bigint::js_bigint_to_string(ptr); if hdr.is_null() { return String::new(); } diff --git a/crates/perry-stdlib/src/readline/mod.rs b/crates/perry-stdlib/src/readline/mod.rs index a5b0da2eca..3268937961 100644 --- a/crates/perry-stdlib/src/readline/mod.rs +++ b/crates/perry-stdlib/src/readline/mod.rs @@ -27,7 +27,9 @@ //! independent of this file. use std::cell::RefCell; -use std::io::{self, Read, Write}; +#[cfg(not(test))] +use std::io::Read; +use std::io::{self, Write}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; diff --git a/crates/perry-stdlib/src/sqlite/connection.rs b/crates/perry-stdlib/src/sqlite/connection.rs index 9e29b25531..f774a3c898 100644 --- a/crates/perry-stdlib/src/sqlite/connection.rs +++ b/crates/perry-stdlib/src/sqlite/connection.rs @@ -46,7 +46,7 @@ pub(crate) fn open_node_sqlite_connection(db: &NodeSqliteDbHandle) -> rusqlite:: ] .get(idx) { - conn.set_limit(*limit, *value); + conn.set_limit(*limit, *value)?; } } } diff --git a/crates/perry-stdlib/src/sqlite/dispatch.rs b/crates/perry-stdlib/src/sqlite/dispatch.rs index d4fa097d50..5cf757911b 100644 --- a/crates/perry-stdlib/src/sqlite/dispatch.rs +++ b/crates/perry-stdlib/src/sqlite/dispatch.rs @@ -391,7 +391,9 @@ pub unsafe fn dispatch_node_sqlite_limits_set( }; let new_value = non_negative_i32_value(value_from_f64(value), property_name, true); with_open_node_connection(limits.db_handle, |conn| { - conn.set_limit(limit, new_value); + // limit id is pre-validated by node_sqlite_limit above; deliberately + // discard set_limit's prior-value Result. + let _ = conn.set_limit(limit, new_value); }); true } diff --git a/crates/perry-stdlib/src/streams/transform.rs b/crates/perry-stdlib/src/streams/transform.rs index a0ffd0926e..e20851b4a7 100644 --- a/crates/perry-stdlib/src/streams/transform.rs +++ b/crates/perry-stdlib/src/streams/transform.rs @@ -267,7 +267,7 @@ pub(super) unsafe fn transform_write(writable_id: usize, chunk: f64) -> *mut Pro /// teepipe2.js shows the transform output landing one tick later than a /// single-job deferral produces. Re-queue once, then run the transform. extern "C" fn transform_write_job(closure: *const ClosureHeader) -> f64 { - unsafe { + { let job_fn = transform_write_job2 as *const u8; perry_runtime::closure::js_register_closure_arity(job_fn, 0); let job = perry_runtime::closure::js_closure_alloc(job_fn, 5); diff --git a/crates/perry-stdlib/src/streams/writable.rs b/crates/perry-stdlib/src/streams/writable.rs index 9ddcdaab30..7cd185bd6c 100644 --- a/crates/perry-stdlib/src/streams/writable.rs +++ b/crates/perry-stdlib/src/streams/writable.rs @@ -258,24 +258,6 @@ fn writable_capture_promise(closure: *const ClosureHeader, idx: u32) -> *mut Pro perry_runtime::closure::js_closure_get_capture_ptr(closure, idx) as *mut Promise } -extern "C" fn writable_write_start_microtask(closure: *const ClosureHeader) -> f64 { - unsafe { - let stream_id = writable_capture_usize(closure, 0); - let writer_id = writable_capture_usize(closure, 1); - let cb = perry_runtime::closure::js_closure_get_capture_ptr(closure, 2); - let chunk_bits = perry_runtime::closure::js_closure_get_capture_ptr(closure, 3) as u64; - let write_promise = writable_capture_promise(closure, 4); - run_writable_write( - stream_id, - writer_id, - cb, - f64::from_bits(chunk_bits), - write_promise, - ); - } - f64::from_bits(TAG_UNDEFINED) -} - extern "C" fn writable_write_fulfilled(closure: *const ClosureHeader, _value: f64) -> f64 { unsafe { let stream_id = writable_capture_usize(closure, 0); diff --git a/crates/perry-stdlib/src/tls.rs b/crates/perry-stdlib/src/tls.rs index 1951fc57d5..7ee9681f0a 100644 --- a/crates/perry-stdlib/src/tls.rs +++ b/crates/perry-stdlib/src/tls.rs @@ -142,7 +142,9 @@ struct TlsServerState { struct TlsSocketState { cmd_tx: Option>, + #[allow(dead_code)] // captured socket local address for future localAddress exposure local_addr: Option, + #[allow(dead_code)] // captured socket peer address for future remoteAddress exposure peer_addr: Option, authorized: bool, server_side: bool, @@ -200,7 +202,7 @@ fn undefined() -> f64 { } fn nanbox_str(s: &str) -> f64 { - unsafe { + { let ptr = js_string_from_bytes(s.as_ptr(), s.len() as u32); f64::from_bits(JSValue::string_ptr(ptr).bits()) } @@ -297,7 +299,7 @@ fn throw_type_error(message: &str, code: &'static str) -> ! { } fn throw_error(message: &str, code: &'static str) -> ! { - unsafe { + { let msg = js_string_from_bytes(message.as_ptr(), message.len() as u32); perry_runtime::node_submodules::register_error_code_pub(msg, code); let err = perry_runtime::error::js_error_new_with_message(msg); diff --git a/crates/perry-stdlib/src/zlib.rs b/crates/perry-stdlib/src/zlib.rs index c6987f9307..9601e52ff5 100644 --- a/crates/perry-stdlib/src/zlib.rs +++ b/crates/perry-stdlib/src/zlib.rs @@ -25,7 +25,7 @@ use std::sync::Mutex; /// reports invalid input so callers see a Node-shaped exception instead /// of a sentinel null return. fn throw_zlib_error(message: &str) -> ! { - unsafe { + { let msg = js_string_from_bytes(message.as_ptr(), message.len() as u32); let err = perry_runtime::error::js_error_new_with_message(msg); perry_runtime::exception::js_throw(perry_runtime::value::js_nanbox_pointer(err as i64)) @@ -212,9 +212,9 @@ pub unsafe extern "C" fn js_zlib_deflate_sync(data_bits: i64, opts: f64) -> *mut // dead-strips them from the stdlib archive, breaking the link of any program // that uses zlib (surfaced by Next.js's resume-data-cache `inflateSync` once the // #5437 live-import fix makes that module reachable). `#[used]` keeps them. -struct KeepZlibFfi([*const (); 32]); -// SAFETY: a link-time keepalive anchor only — the pointers are never read or -// dereferenced, so cross-thread sharing of the raw pointers is sound. +struct KeepZlibFfi(#[allow(dead_code)] [*const (); 32]); // link-time keepalive anchor; field never read, only #[used] to retain FFI symbols + // SAFETY: a link-time keepalive anchor only — the pointers are never read or + // dereferenced, so cross-thread sharing of the raw pointers is sound. unsafe impl Sync for KeepZlibFfi {} #[used] static KEEP_ZLIB_FFI: KeepZlibFfi = KeepZlibFfi([ diff --git a/crates/perry-transform/src/generator/lower.rs b/crates/perry-transform/src/generator/lower.rs index 79d548f35c..45a1f8d0cf 100644 --- a/crates/perry-transform/src/generator/lower.rs +++ b/crates/perry-transform/src/generator/lower.rs @@ -15,24 +15,13 @@ mod yield_await; // `use super::*`) reference. Globs do not propagate transitively in this // repo, so spell every cross-module symbol explicitly. pub(crate) use abrupt::{ - build_abrupt_routing, build_async_catch_route_body, build_async_throw_body, - build_completion_resume_stmts, build_dispatch_catch_handler, build_finally_run_stmts, - build_yield_star_return_routes, build_yield_star_throw_routes, catch_route_condition, - finally_abrupt_condition, finally_route_condition, rewrite_dispatch_continue_to_suspend, - wrap_dispatch_loop, -}; -pub(crate) use async_step::{ - build_async_catch_route_body_direct, build_async_step_driver_direct, - build_async_throw_body_direct, -}; -pub(crate) use call_this::{ - generator_body_uses_call_this, generator_expr_uses_call_this, generator_stmt_uses_call_this, -}; -pub(crate) use resume::{ - generator_executing_guard, generator_executing_type_error, generator_resume_rethrow, - prepend_executing_clear_before_returns, promise_reject, wrap_async_gen_step_body, - wrap_generator_resume_body, + build_abrupt_routing, build_async_throw_body, build_completion_resume_stmts, + build_finally_run_stmts, build_yield_star_return_routes, build_yield_star_throw_routes, + catch_route_condition, wrap_dispatch_loop, }; +pub(crate) use async_step::build_async_step_driver_direct; +pub(crate) use call_this::generator_body_uses_call_this; +pub(crate) use resume::{wrap_async_gen_step_body, wrap_generator_resume_body}; pub(crate) use yield_await::await_async_generator_yield_operands; /// #6709: read the currently-running step closure (`Expr::CurrentStepClosure`). diff --git a/crates/perry-ui-geisterhand/src/server.rs b/crates/perry-ui-geisterhand/src/server.rs index f765d43b6d..e894b40f1e 100644 --- a/crates/perry-ui-geisterhand/src/server.rs +++ b/crates/perry-ui-geisterhand/src/server.rs @@ -31,6 +31,7 @@ extern "C" { // Callback kind constants (must match perry-runtime/src/geisterhand_registry.rs) const CB_ON_CLICK: u8 = 0; const CB_ON_CHANGE: u8 = 1; +#[allow(dead_code)] // kept in lockstep with perry-runtime geisterhand_registry callback-kind enumeration const CB_ON_SUBMIT: u8 = 2; const CB_ON_HOVER: u8 = 3; const CB_ON_DOUBLE_CLICK: u8 = 4; diff --git a/crates/perry-ui-macos/src/app.rs b/crates/perry-ui-macos/src/app.rs index 3676c5099b..b66ad219d4 100644 --- a/crates/perry-ui-macos/src/app.rs +++ b/crates/perry-ui-macos/src/app.rs @@ -1123,7 +1123,7 @@ pub fn add_keyboard_shortcut(key_ptr: *const u8, modifiers: f64, callback: f64) let app = NSApplication::sharedApplication(mtm); // If the menu bar exists, install immediately; otherwise buffer for later. - let has_menu = unsafe { app.mainMenu().is_some() }; + let has_menu = app.mainMenu().is_some(); if has_menu { install_keyboard_shortcut(key_ptr, modifiers, callback, mtm); } else { diff --git a/crates/perry-ui-macos/src/audio.rs b/crates/perry-ui-macos/src/audio.rs index f5513f3495..50f13a4b83 100644 --- a/crates/perry-ui-macos/src/audio.rs +++ b/crates/perry-ui-macos/src/audio.rs @@ -58,10 +58,6 @@ impl AWeightState { sections: [[0.0; 4]; 3], } } - - fn reset(&mut self) { - self.sections = [[0.0; 4]; 3]; - } } // A-weighting filter coefficients for 48000 Hz sample rate. @@ -149,14 +145,6 @@ extern "C" { fn js_array_create() -> i64; } -/// ObjC type encoding for AVAudioPCMBuffer block: -/// void (^)(AVAudioPCMBuffer *, AVAudioTime *) -/// We use raw pointers since we access through msg_send! anyway. -#[repr(C)] -struct AudioBufferList { - _opaque: [u8; 0], -} - // ============================================================================= // Public API // ============================================================================= @@ -213,9 +201,9 @@ pub fn start() -> i64 { let buffer_size: u32 = 1024; let _: () = msg_send![ input_node, - installTapOnBus: 0u32 - bufferSize: buffer_size - format: format + installTapOnBus: 0u32, + bufferSize: buffer_size, + format: format, block: &*tap_block ]; @@ -407,8 +395,8 @@ fn write_wav_samples(writer: &mut File, samples: &[f32]) -> std::io::Result<()> // Internal: Audio processing // ============================================================================= -/// EMA (exponential moving average) state. Uses thread_local because the -/// tap block always runs on the same audio thread. +// EMA (exponential moving average) state. Uses thread_local because the +// tap block always runs on the same audio thread. thread_local! { static FILTER_STATE: RefCell = RefCell::new(AWeightState::new()); static EMA_DB: RefCell = const { RefCell::new(0.0) }; diff --git a/crates/perry-ui-macos/src/audio_playback.rs b/crates/perry-ui-macos/src/audio_playback.rs index 8eee40de27..4896f7fea3 100644 --- a/crates/perry-ui-macos/src/audio_playback.rs +++ b/crates/perry-ui-macos/src/audio_playback.rs @@ -102,6 +102,8 @@ struct VoiceEntry { player_node: Retained, // AVAudioPlayerNode varispeed: Option>, // AVAudioUnitVarispeed (None if fallback) sound_idx: usize, + #[allow(dead_code)] + // carries the sound's bus handle (read from SoundEntry.bus_handle) into the voice; kept so the value chain stays intact bus_handle: f64, is_playing: bool, is_paused: bool, @@ -270,13 +272,13 @@ pub extern "C" fn perry_audio_load_sound(path_ptr: i64, bus: f64, stream: f64) - let ns_name = objc2_foundation::NSString::from_str(name); let ns_ext = objc2_foundation::NSString::from_str(ext); let mut url: *mut AnyObject = - msg_send![bundle, URLForResource: &*ns_name withExtension: &*ns_ext]; + msg_send![bundle, URLForResource: &*ns_name, withExtension: &*ns_ext]; if url.is_null() { let ns_subdir = objc2_foundation::NSString::from_str("sounds"); url = msg_send![ bundle, - URLForResource: &*ns_name - withExtension: &*ns_ext + URLForResource: &*ns_name, + withExtension: &*ns_ext, subdirectory: &*ns_subdir ]; } @@ -330,7 +332,7 @@ pub extern "C" fn perry_audio_load_sound(path_ptr: i64, bus: f64, stream: f64) - let format_ptr: *mut AnyObject = &*format as *const AnyObject as *mut AnyObject; let buffer_raw: *mut AnyObject = msg_send![ buf_alloc, - initWithPCMFormat: format_ptr + initWithPCMFormat: format_ptr, frameCapacity: capacity ]; if buffer_raw.is_null() { @@ -339,7 +341,7 @@ pub extern "C" fn perry_audio_load_sound(path_ptr: i64, bus: f64, stream: f64) - let buffer = Retained::retain(buffer_raw).unwrap(); error = std::ptr::null_mut(); - let read_ok: bool = msg_send![&*file, readIntoBuffer: &*buffer error: &mut error]; + let read_ok: bool = msg_send![&*file, readIntoBuffer: &*buffer, error: &mut error]; if !read_ok || !error.is_null() { eprintln!("[perry/audio] loadSound: read failed: {}", filename); return 0; @@ -492,14 +494,14 @@ pub extern "C" fn perry_audio_play( let _: () = msg_send![&**engine, attachNode: &**vs]; let _: () = msg_send![ &**engine, - connect: &*player_node - to: &**vs + connect: &*player_node, + to: &**vs, format: buf_format ]; let _: () = msg_send![ &**engine, - connect: &**vs - to: bus_node + connect: &**vs, + to: bus_node, format: buf_format ]; } else { @@ -513,8 +515,8 @@ pub extern "C" fn perry_audio_play( }); let _: () = msg_send![ &**engine, - connect: &*player_node - to: bus_node + connect: &*player_node, + to: bus_node, format: buf_format ]; } @@ -546,8 +548,8 @@ pub extern "C" fn perry_audio_play( let cb = make_ended_block(voice_idx); let _: () = msg_send![ &*player_node, - scheduleFile: &**file - atTime: std::ptr::null::() + scheduleFile: &**file, + atTime: std::ptr::null::(), completionHandler: &*cb ]; std::mem::forget(cb); @@ -563,9 +565,9 @@ pub extern "C" fn perry_audio_play( }; let _: () = msg_send![ &*player_node, - scheduleBuffer: &**buffer_ret - atTime: std::ptr::null::() - options: options + scheduleBuffer: &**buffer_ret, + atTime: std::ptr::null::(), + options: options, completionHandler: &*cb ]; std::mem::forget(cb); @@ -672,8 +674,8 @@ fn drain_pending_callbacks() { let cb = make_ended_block(idx); let _: () = msg_send![ &*player_node, - scheduleFile: &*file - atTime: std::ptr::null::() + scheduleFile: &*file, + atTime: std::ptr::null::(), completionHandler: &*cb ]; std::mem::forget(cb); @@ -1049,8 +1051,8 @@ pub extern "C" fn perry_audio_create_bus(name_ptr: i64, parent: f64) -> i64 { let null_fmt: *const AnyObject = std::ptr::null(); let _: () = msg_send![ &**engine, - connect: &*mixer - to: parent_node + connect: &*mixer, + to: parent_node, format: null_fmt ]; true diff --git a/crates/perry-ui-macos/src/crash_log.rs b/crates/perry-ui-macos/src/crash_log.rs index 6bfdcefcc0..c238adcee9 100644 --- a/crates/perry-ui-macos/src/crash_log.rs +++ b/crates/perry-ui-macos/src/crash_log.rs @@ -115,7 +115,7 @@ extern "C" fn signal_handler(sig: libc::c_int) { unsafe { if CRASH_LOG_PATH_LEN > 0 { let fd = libc::open( - CRASH_LOG_PATH_BUF.as_ptr() as *const libc::c_char, + (&raw const CRASH_LOG_PATH_BUF) as *const libc::c_char, libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC, 0o644, ); diff --git a/crates/perry-ui-macos/src/geolocation.rs b/crates/perry-ui-macos/src/geolocation.rs index d9fa6decb0..2b856e0c69 100644 --- a/crates/perry-ui-macos/src/geolocation.rs +++ b/crates/perry-ui-macos/src/geolocation.rs @@ -62,7 +62,11 @@ struct WatchEntry { struct PermissionRequest { callback: f64, + #[allow(dead_code)] + // keep-alive owner: retains the CLLocationManager while the async permission request is pending manager: Retained, + #[allow(dead_code)] + // keep-alive owner: retains the delegate so authorization callbacks still fire delegate: Retained, } diff --git a/crates/perry-ui-macos/src/network.rs b/crates/perry-ui-macos/src/network.rs index 0983965a3f..3e24610e38 100644 --- a/crates/perry-ui-macos/src/network.rs +++ b/crates/perry-ui-macos/src/network.rs @@ -52,7 +52,6 @@ const TAG_TRUE: u64 = 0x7FFC_0000_0000_0004; struct Status { connected: bool, kind: &'static str, - initialized: bool, } impl Status { @@ -60,7 +59,6 @@ impl Status { Self { connected: false, kind: "unknown", - initialized: false, } } } @@ -100,7 +98,6 @@ fn classify(path: *mut c_void) -> Status { return Status { connected: false, kind: "none", - initialized: true, }; } let status_code = unsafe { nw_path_get_status(path) }; @@ -108,7 +105,6 @@ fn classify(path: *mut c_void) -> Status { return Status { connected: false, kind: "none", - initialized: true, }; } let kind = if unsafe { nw_path_uses_interface_type(path, NW_INTERFACE_TYPE_WIFI) } { @@ -123,7 +119,6 @@ fn classify(path: *mut c_void) -> Status { Status { connected: true, kind, - initialized: true, } } diff --git a/crates/perry-ui-macos/src/widgets/adbanner.rs b/crates/perry-ui-macos/src/widgets/adbanner.rs index 9f4cf318d4..2326fe93cf 100644 --- a/crates/perry-ui-macos/src/widgets/adbanner.rs +++ b/crates/perry-ui-macos/src/widgets/adbanner.rs @@ -10,7 +10,7 @@ use objc2::msg_send; use objc2::rc::Retained; -use objc2::{AnyThread, MainThreadOnly}; +use objc2::MainThreadOnly; use objc2_app_kit::NSView; use objc2_core_foundation::{CGPoint, CGRect, CGSize}; use objc2_foundation::MainThreadMarker; diff --git a/crates/perry-ui-macos/src/widgets/canvas.rs b/crates/perry-ui-macos/src/widgets/canvas.rs index 16a2715df3..92be12f0b1 100644 --- a/crates/perry-ui-macos/src/widgets/canvas.rs +++ b/crates/perry-ui-macos/src/widgets/canvas.rs @@ -17,7 +17,7 @@ use crate::ffi::CGContextStrokePath; use crate::ffi::CGContextStrokeRect; use objc2::rc::Retained; use objc2::runtime::AnyObject; -use objc2::{define_class, msg_send, AnyThread, DefinedClass, MainThreadOnly}; +use objc2::{define_class, msg_send, DefinedClass, MainThreadOnly}; use objc2_app_kit::NSView; use objc2_core_foundation::{CGPoint, CGRect, CGSize}; use objc2_foundation::MainThreadMarker; @@ -564,7 +564,7 @@ pub fn create(width: f64, height: f64) -> i64 { }); // Cast to NSView for registration - let ns_view: Retained = unsafe { Retained::cast(view) }; + let ns_view: Retained = unsafe { Retained::cast_unchecked(view) }; register_widget(ns_view) } diff --git a/crates/perry-ui-macos/src/widgets/chart.rs b/crates/perry-ui-macos/src/widgets/chart.rs index 54738f8b25..f87d9a5b2d 100644 --- a/crates/perry-ui-macos/src/widgets/chart.rs +++ b/crates/perry-ui-macos/src/widgets/chart.rs @@ -31,7 +31,7 @@ use crate::ffi::CGContextStrokePath; use objc2::msg_send; use objc2::rc::Retained; use objc2::runtime::{AnyClass, AnyObject}; -use objc2::{define_class, AnyThread, DefinedClass, MainThreadOnly}; +use objc2::{define_class, DefinedClass, MainThreadOnly}; use objc2_app_kit::NSView; use objc2_core_foundation::CGFloat; use objc2_foundation::MainThreadMarker; diff --git a/crates/perry-ui-macos/src/widgets/combobox.rs b/crates/perry-ui-macos/src/widgets/combobox.rs index cb04824432..5cd4f84f71 100644 --- a/crates/perry-ui-macos/src/widgets/combobox.rs +++ b/crates/perry-ui-macos/src/widgets/combobox.rs @@ -12,7 +12,7 @@ use crate::ffi::js_string_from_bytes; use objc2::rc::Retained; use objc2::runtime::{AnyClass, AnyObject, Sel}; -use objc2::{define_class, msg_send, AnyThread, DefinedClass, MainThreadOnly}; +use objc2::{define_class, msg_send, AnyThread, DefinedClass}; use objc2_app_kit::NSView; use objc2_foundation::{MainThreadMarker, NSObject, NSString}; use std::cell::RefCell; diff --git a/crates/perry-ui-macos/src/widgets/hstack.rs b/crates/perry-ui-macos/src/widgets/hstack.rs index 5cae2afc59..95059ad78e 100644 --- a/crates/perry-ui-macos/src/widgets/hstack.rs +++ b/crates/perry-ui-macos/src/widgets/hstack.rs @@ -31,14 +31,12 @@ pub fn create_with_insets(spacing: f64, top: f64, left: f64, bottom: f64, right: stack.setSpacing(spacing); stack.setAlignment(NSLayoutAttribute::CenterY); set_gravity_distribution(&stack); - unsafe { - stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { - top, - left, - bottom, - right, - }); - } + stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { + top, + left, + bottom, + right, + }); let view: Retained = unsafe { Retained::cast_unchecked(stack) }; super::register_widget(view) } diff --git a/crates/perry-ui-macos/src/widgets/image.rs b/crates/perry-ui-macos/src/widgets/image.rs index 9dcedadb35..005a114298 100644 --- a/crates/perry-ui-macos/src/widgets/image.rs +++ b/crates/perry-ui-macos/src/widgets/image.rs @@ -1,6 +1,6 @@ use objc2::msg_send; use objc2::rc::Retained; -use objc2::{AnyThread, MainThreadOnly}; +use objc2::MainThreadOnly; use objc2_app_kit::{NSImage, NSImageView, NSView}; use objc2_foundation::{MainThreadMarker, NSString}; diff --git a/crates/perry-ui-macos/src/widgets/mod.rs b/crates/perry-ui-macos/src/widgets/mod.rs index 4da3a9a75d..e11093d8d4 100644 --- a/crates/perry-ui-macos/src/widgets/mod.rs +++ b/crates/perry-ui-macos/src/widgets/mod.rs @@ -211,9 +211,9 @@ pub fn register_external_nsview(nsview_ptr: i64) -> i64 { // Set low content hugging so it stretches in both axes. unsafe { let _: () = - msg_send![&*nsview, setContentHuggingPriority: 1.0f32 forOrientation: 0i64]; // horizontal + msg_send![&*nsview, setContentHuggingPriority: 1.0f32, forOrientation: 0i64]; // horizontal let _: () = - msg_send![&*nsview, setContentHuggingPriority: 1.0f32 forOrientation: 1i64]; + msg_send![&*nsview, setContentHuggingPriority: 1.0f32, forOrientation: 1i64]; // vertical } // Clip to bounds — prevent the view from drawing outside its frame @@ -590,7 +590,7 @@ pub fn add_overlay(parent_handle: i64, child_handle: i64) { /// x/y are relative to the parent's coordinate system. pub fn set_overlay_frame(handle: i64, x: f64, y: f64, w: f64, h: f64) { if let Some(view) = get_widget(handle) { - unsafe { + { view.setTranslatesAutoresizingMaskIntoConstraints(true); let frame = objc2_core_foundation::CGRect::new( objc2_core_foundation::CGPoint::new(x, y), @@ -851,14 +851,12 @@ pub fn set_edge_insets(handle: i64, top: f64, left: f64, bottom: f64, right: f64 }; if is_stack { let stack: &NSStackView = unsafe { &*(Retained::as_ptr(&view) as *const NSStackView) }; - unsafe { - stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { - top, - left, - bottom, - right, - }); - } + stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { + top, + left, + bottom, + right, + }); } } } @@ -1052,7 +1050,6 @@ pub fn match_parent_width(child_handle: i64) { extern "C" { fn js_closure_call0(closure: *const u8) -> f64; - fn js_closure_call1(closure: *const u8, arg: f64) -> f64; fn js_nanbox_get_pointer(value: f64) -> i64; } diff --git a/crates/perry-ui-macos/src/widgets/qrcode.rs b/crates/perry-ui-macos/src/widgets/qrcode.rs index 156bd3696b..5e8a874046 100644 --- a/crates/perry-ui-macos/src/widgets/qrcode.rs +++ b/crates/perry-ui-macos/src/widgets/qrcode.rs @@ -1,7 +1,7 @@ use objc2::msg_send; use objc2::rc::Retained; use objc2::runtime::{AnyClass, AnyObject}; -use objc2::{AnyThread, MainThreadOnly}; +use objc2::MainThreadOnly; use objc2_app_kit::{NSImage, NSImageView, NSView}; use objc2_foundation::{MainThreadMarker, NSString}; @@ -120,7 +120,7 @@ unsafe fn generate_qr_image(text: &str, display_size: f64) -> Option Option Option = Retained::retain(ns_image_raw as *mut NSImage)?; - // Balance the retain (init already gives us +1) - std::mem::forget(Retained::retain(ns_image_raw as *mut NSImage)); + // Adopt the +1 result returned by the alloc/init pair. + let ns_image: Retained = Retained::from_raw(ns_image_raw as *mut NSImage)?; Some(ns_image) } diff --git a/crates/perry-ui-macos/src/widgets/table.rs b/crates/perry-ui-macos/src/widgets/table.rs index 30b75cc224..41219ad214 100644 --- a/crates/perry-ui-macos/src/widgets/table.rs +++ b/crates/perry-ui-macos/src/widgets/table.rs @@ -14,6 +14,8 @@ extern "C" { } struct TableEntry { + #[allow(dead_code)] + // owning handle: keeps the AppKit scroll view retained for the entry's lifetime scroll_view: Retained, table_view: Retained, handle: i64, diff --git a/crates/perry-ui-macos/src/widgets/tree_view.rs b/crates/perry-ui-macos/src/widgets/tree_view.rs index eeb91fe1af..e4fe46498e 100644 --- a/crates/perry-ui-macos/src/widgets/tree_view.rs +++ b/crates/perry-ui-macos/src/widgets/tree_view.rs @@ -36,6 +36,7 @@ struct TreeNode { } struct TreeEntry { + #[allow(dead_code)] // keep-alive owner: retains the NSScrollView container for this tree entry scroll_view: Retained, outline_view: Retained, handle: i64, diff --git a/crates/perry-ui-macos/src/widgets/vstack.rs b/crates/perry-ui-macos/src/widgets/vstack.rs index 13d257fe19..0e368ab786 100644 --- a/crates/perry-ui-macos/src/widgets/vstack.rs +++ b/crates/perry-ui-macos/src/widgets/vstack.rs @@ -38,14 +38,12 @@ pub fn create_with_insets(spacing: f64, top: f64, left: f64, bottom: f64, right: stack.setSpacing(spacing); stack.setAlignment(NSLayoutAttribute::Leading); set_gravity_distribution(&stack); - unsafe { - stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { - top, - left, - bottom, - right, - }); - } + stack.setEdgeInsets(objc2_foundation::NSEdgeInsets { + top, + left, + bottom, + right, + }); let view: Retained = unsafe { Retained::cast_unchecked(stack) }; super::register_widget(view) } diff --git a/crates/perry-ui-macos/src/widgets/webview.rs b/crates/perry-ui-macos/src/widgets/webview.rs index 769a254f10..f16d896e71 100644 --- a/crates/perry-ui-macos/src/widgets/webview.rs +++ b/crates/perry-ui-macos/src/widgets/webview.rs @@ -186,7 +186,7 @@ fn request_av_capture_access_once() { } ); }); - let _: () = msg_send![device_cls, requestAccessForMediaType: &*media_type completionHandler: &*permission_block]; + let _: () = msg_send![device_cls, requestAccessForMediaType: &*media_type, completionHandler: &*permission_block]; } else { // Do not show the Settings alert here: WebView widgets are // constructed while building the App body, before the window is diff --git a/crates/perry/src/commands/compile.rs b/crates/perry/src/commands/compile.rs index 1dee0a0a8c..ffb08948ae 100644 --- a/crates/perry/src/commands/compile.rs +++ b/crates/perry/src/commands/compile.rs @@ -1,12 +1,7 @@ //! Compile command - compiles TypeScript to native executable -use anyhow::{anyhow, Result}; +use anyhow::Result; use rayon::prelude::*; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::sync::atomic::{AtomicUsize, Ordering}; use crate::OutputFormat; @@ -142,9 +137,8 @@ mod run_pipeline; #[cfg(windows)] pub(crate) use helpers::is_windows_reserved_file_stem; pub(crate) use helpers::{ - apply_libc_to_target, backend_disabled_msg, canonical_class_source_prefix, - native_object_file_stem, object_cache_project_root, print_deferred_eval_notice, - NativeObjectArtifact, + apply_libc_to_target, canonical_class_source_prefix, native_object_file_stem, + object_cache_project_root, print_deferred_eval_notice, NativeObjectArtifact, }; pub use run_pipeline::run_with_parse_cache; diff --git a/crates/perry/src/commands/compile/cjs_wrap/detect.rs b/crates/perry/src/commands/compile/cjs_wrap/detect.rs index 7ba381fe8e..694cb44780 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/detect.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/detect.rs @@ -1,7 +1,5 @@ //! CommonJS-vs-ESM heuristic detection plus reserved-word filtering. -use super::*; - /// Heuristic CJS detection. Same shape as /// `perry-jsruntime/src/modules.rs::is_commonjs`. False negatives are /// acceptable (the file just falls through to the existing ESM-only diff --git a/crates/perry/src/commands/compile/cjs_wrap/extract_requires.rs b/crates/perry/src/commands/compile/cjs_wrap/extract_requires.rs index 2b52b4f635..da9806c03b 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/extract_requires.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/extract_requires.rs @@ -1,7 +1,5 @@ //! `require(...)` specifier extraction and alias detection. -use super::*; - /// Extract `require('X')` / `require("X")` specifiers, preserving order and /// deduping. Only matches static string literal arguments — dynamic /// `require(someVar)` is unrepresentable as ESM and the bound `require` diff --git a/crates/perry/src/commands/compile/cjs_wrap/hoist_classes.rs b/crates/perry/src/commands/compile/cjs_wrap/hoist_classes.rs index 494c04bc35..7ee02260e2 100644 --- a/crates/perry/src/commands/compile/cjs_wrap/hoist_classes.rs +++ b/crates/perry/src/commands/compile/cjs_wrap/hoist_classes.rs @@ -1,7 +1,5 @@ //! Top-level `class` hoisting and `module.exports = class …` rewrite passes. -use super::*; - /// Issue #665 (fifth pass): rewrite the leaf-file shape /// `module.exports = class Name { ... };` into declaration form /// `class Name { ... }\nmodule.exports = Name;` so the existing @@ -605,13 +603,6 @@ fn collect_pattern_binding_names(bytes: &[u8], start: usize, names: &mut Vec { - // Unreachable in practice (handled above), kept for clarity. - i += 1; - } c if c.is_ascii_alphanumeric() || c == b'_' || c == b'$' || c == b'.' => { // Identifier (or `...rest` after the dots). Read it. let name_start = i; diff --git a/crates/perry/src/commands/compile/collect_modules.rs b/crates/perry/src/commands/compile/collect_modules.rs index c408dbb28c..5b1d2b4d23 100644 --- a/crates/perry/src/commands/compile/collect_modules.rs +++ b/crates/perry/src/commands/compile/collect_modules.rs @@ -227,7 +227,7 @@ fn collect_module_one( target: Option<&str>, next_class_id: &mut perry_hir::ClassId, progress: &VerboseProgress, - mut parse_cache: Option<&mut ParseCache>, + parse_cache: Option<&mut ParseCache>, ) -> Result { let mut pending = Vec::new(); diff --git a/crates/perry/src/commands/compile/helpers.rs b/crates/perry/src/commands/compile/helpers.rs index fd75538639..5c844a154d 100644 --- a/crates/perry/src/commands/compile/helpers.rs +++ b/crates/perry/src/commands/compile/helpers.rs @@ -7,13 +7,9 @@ use super::*; -use anyhow::{anyhow, Result}; -use rayon::prelude::*; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; -use std::fs; +use anyhow::Result; +use std::collections::HashMap; use std::path::{Path, PathBuf}; -use std::process::Command; -use std::sync::atomic::{AtomicUsize, Ordering}; use crate::OutputFormat; diff --git a/crates/perry/src/commands/compile/link/platform_cmd.rs b/crates/perry/src/commands/compile/link/platform_cmd.rs index fc47379d53..1acbdce425 100644 --- a/crates/perry/src/commands/compile/link/platform_cmd.rs +++ b/crates/perry/src/commands/compile/link/platform_cmd.rs @@ -727,6 +727,9 @@ pub fn select_linker_command( // When cross-compiling from macOS, use clang + ld.lld + a glibc // sysroot pointed to by PERRY_LINUX_SYSROOT (matching the // PERRY_IOS_SYSROOT/PERRY_WINDOWS_SYSROOT builder pattern). + // Only the cross-compile block below mutates `c`; building on Linux + // hands back a bare `cc`. + #[cfg_attr(target_os = "linux", allow(unused_mut))] let mut c = Command::new("cc"); #[cfg(not(target_os = "linux"))] { diff --git a/crates/perry/src/commands/compile/object_cache.rs b/crates/perry/src/commands/compile/object_cache.rs index 7131f87493..424c29c907 100644 --- a/crates/perry/src/commands/compile/object_cache.rs +++ b/crates/perry/src/commands/compile/object_cache.rs @@ -1173,6 +1173,7 @@ impl ObjectCache { /// reading the object bytes into memory. We still open the file once so /// unreadable cache entries fall back to a fresh compile just like /// `lookup` would. + #[allow(dead_code)] // exercised by #[cfg(test)] object_cache_tests; prod uses lookup_path_with_ffi pub fn lookup_path(&self, key: u64) -> Option { let path = self.path_for(key)?; match fs::File::open(&path).and_then(|f| f.metadata()) { diff --git a/crates/perry/src/commands/compile/optimized_libs.rs b/crates/perry/src/commands/compile/optimized_libs.rs index ea197d7556..60b2942e1a 100644 --- a/crates/perry/src/commands/compile/optimized_libs.rs +++ b/crates/perry/src/commands/compile/optimized_libs.rs @@ -12,16 +12,9 @@ //! profile are no-ops after the first build. use std::collections::BTreeSet; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::time::SystemTime; +use std::path::PathBuf; -use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; -use crate::OutputFormat; - -use super::library_search::{find_harmonyos_sdk, harmonyos_cross_env}; -use super::{find_perry_workspace_root, rust_target_triple, CompilationContext}; +use super::CompilationContext; mod driver; mod freshness; @@ -35,9 +28,7 @@ pub(crate) use freshness::{ effective_size_panic_immediate_abort, resolve_auto_well_known_libs, retain_workspace_declared_features, size_lto_fat, size_opt_level, }; -pub(crate) use no_auto::{ - build_missing_prebuilt_ext_lib, resolve_no_auto_optimized_libs, resolve_prebuilt_ext_libs, -}; +pub(crate) use no_auto::{resolve_no_auto_optimized_libs, resolve_prebuilt_ext_libs}; pub(crate) use paths::{ android_global_dynamic_tls_rustflag, auto_target_dir_paths, cargo_target_dir_path, }; diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index ac686314a9..9f853a26f5 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -1,10 +1,7 @@ use super::*; -use std::collections::BTreeSet; -use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; -use std::time::SystemTime; use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; use crate::OutputFormat; diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index 95a1d4ffdc..e4227fa3df 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -1,16 +1,11 @@ -use super::*; - use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; use std::time::SystemTime; -use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; use crate::OutputFormat; -use super::super::library_search::{find_harmonyos_sdk, harmonyos_cross_env}; -use super::super::{find_perry_workspace_root, rust_target_triple, CompilationContext}; +use super::super::{rust_target_triple, CompilationContext}; fn needs_http2_constants(ctx: &CompilationContext) -> bool { ctx.native_module_imports.contains("http2") || ctx.uses_get_builtin_module diff --git a/crates/perry/src/commands/compile/optimized_libs/no_auto.rs b/crates/perry/src/commands/compile/optimized_libs/no_auto.rs index c821ca30bc..73c608bb3a 100644 --- a/crates/perry/src/commands/compile/optimized_libs/no_auto.rs +++ b/crates/perry/src/commands/compile/optimized_libs/no_auto.rs @@ -1,15 +1,10 @@ use super::*; -use std::collections::BTreeSet; -use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use std::time::SystemTime; -use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; use crate::OutputFormat; -use super::super::library_search::{find_harmonyos_sdk, harmonyos_cross_env}; use super::super::{find_perry_workspace_root, rust_target_triple, CompilationContext}; /// Resolve well-known wrapper archives without rebuilding runtime/stdlib. diff --git a/crates/perry/src/commands/compile/optimized_libs/paths.rs b/crates/perry/src/commands/compile/optimized_libs/paths.rs index 5fdc1b26bf..601a965596 100644 --- a/crates/perry/src/commands/compile/optimized_libs/paths.rs +++ b/crates/perry/src/commands/compile/optimized_libs/paths.rs @@ -1,16 +1,5 @@ -use super::*; - -use std::collections::BTreeSet; -use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use std::time::SystemTime; - -use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; -use crate::OutputFormat; - -use super::super::library_search::{find_harmonyos_sdk, harmonyos_cross_env}; -use super::super::{find_perry_workspace_root, rust_target_triple, CompilationContext}; /// (#1529) Android's `libperry_app.so` is loaded via `dlopen`, so its TLS /// relocations must use the global-dynamic model — the aarch64-linux-android diff --git a/crates/perry/src/commands/compile/optimized_libs/tests.rs b/crates/perry/src/commands/compile/optimized_libs/tests.rs index fb743e431b..57b10917dc 100644 --- a/crates/perry/src/commands/compile/optimized_libs/tests.rs +++ b/crates/perry/src/commands/compile/optimized_libs/tests.rs @@ -1,6 +1,6 @@ +use super::no_auto::build_missing_prebuilt_ext_lib; use super::*; -use std::path::{Path, PathBuf}; -use std::sync::{Mutex, OnceLock}; +use std::path::Path; use crate::commands::stdlib_features::{compute_required_features, features_to_cargo_arg}; use crate::OutputFormat; diff --git a/crates/perry/src/commands/compile/resolve.rs b/crates/perry/src/commands/compile/resolve.rs index cb9dd5df4f..725c859895 100644 --- a/crates/perry/src/commands/compile/resolve.rs +++ b/crates/perry/src/commands/compile/resolve.rs @@ -857,26 +857,6 @@ fn resolve_exports_with_conditions( } } -/// Resolve exports field from package.json for executable module entries. -/// -/// `node` is ranked ABOVE `default` (and above `import`/`module`) because perry -/// compiles a Node target: a package whose `exports` offers a `{ node, default }` -/// conditional pair ships its full Node API under `node` and a reduced -/// browser/edge build under `default`. Picking `default` drops Node-only -/// exports — e.g. `unicorn-magic` exposes `toPath`/`traversePathUp` only in its -/// `node` entry (`./node.js`); resolving `./default.js` left them undefined, so -/// `npm-run-path` (→ execa) failed to LINK (`undefined symbol -/// perry_fn_…unicorn_magic…__toPath`). Matches the `resolve_subpath_import` -/// condition order (chalk's `#supports-color` `{ node, default }`); the two -/// resolvers must agree. -pub(super) fn resolve_exports(exports: &serde_json::Value, subpath: &str) -> Option { - resolve_exports_with_conditions( - exports, - subpath, - &["perry", "node", "import", "module", "default", "require"], - ) -} - /// Node subpath imports (#5039): resolve a `#`-prefixed specifier through the /// importing package's own `package.json` `"imports"` map /// (https://nodejs.org/api/packages.html#imports). chalk 5 loads its vendored @@ -910,7 +890,7 @@ fn resolve_subpath_import(import_source: &str, importer_path: &Path) -> Option

= key.splitn(2, '*').collect(); - if parts.len() == 2 { - let (prefix, suffix) = (parts[0], parts[1]); - if subpath.starts_with(prefix) && subpath.ends_with(suffix) { - let matched = &subpath[prefix.len()..subpath.len() - suffix.len()]; - let mut templates = Vec::new(); - collect(entry, subpath, &mut templates); - for template in templates { - let resolved = template.replace('*', matched); - if !out.contains(&resolved) { - out.push(resolved); - } - } - } + let mut patterns: Vec<_> = map + .iter() + .filter_map(|(key, entry)| { + let (prefix, suffix) = key.split_once('*')?; + let end = subpath.len().checked_sub(suffix.len())?; + (subpath.starts_with(prefix) + && subpath.ends_with(suffix) + && prefix.len() <= end) + .then_some((prefix, suffix, end, entry)) + }) + .collect(); + patterns.sort_by(|a, b| { + b.0.len() + .cmp(&a.0.len()) + .then_with(|| (b.0.len() + b.1.len()).cmp(&(a.0.len() + a.1.len()))) + }); + for (prefix, _, end, entry) in patterns { + let matched = &subpath[prefix.len()..end]; + let mut templates = Vec::new(); + collect(entry, subpath, &mut templates); + for template in templates { + let resolved = template.replace('*', matched); + if !out.contains(&resolved) { + out.push(resolved); } } } diff --git a/crates/perry/src/commands/compile/resolve/tests.rs b/crates/perry/src/commands/compile/resolve/tests.rs index d173421609..bfd18d7c77 100644 --- a/crates/perry/src/commands/compile/resolve/tests.rs +++ b/crates/perry/src/commands/compile/resolve/tests.rs @@ -1820,6 +1820,30 @@ mod exports_candidates_tests { ); } + #[test] + fn wildcard_candidates_reject_overlapping_bounds() { + let exports: serde_json::Value = serde_json::json!({ + "./long*long": "./wrong/*.js" + }); + assert!(resolve_exports_candidates(&exports, "./long").is_empty()); + } + + #[test] + fn wildcard_candidates_prefer_the_most_specific_pattern() { + let exports: serde_json::Value = serde_json::json!({ + "./*": "./broad/*.js", + "./features/*": "./specific/*.js" + }); + let candidates = resolve_exports_candidates(&exports, "./features/a"); + assert_eq!( + candidates, + vec![ + "./specific/a.js".to_string(), + "./broad/features/a.js".to_string() + ] + ); + } + /// #5237 — a Node "exports" *fallback array* (an ordered list of targets, /// here a conditions-object followed by a plain string) must expand to its /// inner targets. Before the fix the array form produced no candidates at diff --git a/crates/perry/src/commands/compile/run_pipeline.rs b/crates/perry/src/commands/compile/run_pipeline.rs index 68be249833..542d8a21e9 100644 --- a/crates/perry/src/commands/compile/run_pipeline.rs +++ b/crates/perry/src/commands/compile/run_pipeline.rs @@ -7,8 +7,15 @@ use super::*; +#[cfg(any( + not(feature = "backend-wasm"), + not(feature = "backend-swiftui"), + not(feature = "backend-glance"), + not(feature = "backend-wear-tiles") +))] +use super::helpers::backend_disabled_msg; + use anyhow::{anyhow, bail, Context, Result}; -use rayon::prelude::*; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs; use std::path::{Path, PathBuf}; diff --git a/crates/perry/src/commands/compile/types.rs b/crates/perry/src/commands/compile/types.rs index 59910b5092..c9cf7b027d 100644 --- a/crates/perry/src/commands/compile/types.rs +++ b/crates/perry/src/commands/compile/types.rs @@ -596,6 +596,8 @@ pub struct CompilationContext { /// expanding globs/directories. The embed-relative name (e.g. /// `dist/index.html`) is the runtime registry key and `$perryfs/` virtual /// path suffix. + #[allow(dead_code)] + // #5731 embed-assets context contract; pub field populated on the embed path, not read here pub embedded_assets: Vec<(String, PathBuf)>, /// #1681 (Phase 3 of #1677): true when this is the build-time capture /// stage (the `current_exe` subprocess), so `precompile(EXPR)` sites @@ -1015,6 +1017,8 @@ pub enum SideEffects { Unknown, /// `"sideEffects": ["glob", ...]` — only files matching a glob have side /// effects; others are droppable. Globs are relative to the package dir. + #[allow(dead_code)] + // #2309 PR1: glob list captured for future per-file matching, not yet read Globs(Vec), } diff --git a/crates/perry/src/commands/mod.rs b/crates/perry/src/commands/mod.rs index fb506401c7..e1c0e75366 100644 --- a/crates/perry/src/commands/mod.rs +++ b/crates/perry/src/commands/mod.rs @@ -31,6 +31,10 @@ pub mod perry_lock; pub(crate) mod progress; pub mod publish; pub mod run; +// #506 MVP is macOS-only (sandbox-exec profiles); the seccomp and AppContainer +// equivalents are follow-ups. Its only caller is `#[cfg(target_os = "macos")]`, +// so off macOS the whole module is dead. +#[cfg(target_os = "macos")] pub mod sandbox_profile; pub mod sanitize; pub mod setup; diff --git a/crates/perry/src/commands/publish/mod.rs b/crates/perry/src/commands/publish/mod.rs index 42733c5fdf..dfa651ca1e 100644 --- a/crates/perry/src/commands/publish/mod.rs +++ b/crates/perry/src/commands/publish/mod.rs @@ -77,6 +77,7 @@ pub fn run(args: PublishArgs, format: OutputFormat, use_color: bool, _verbose: u result } +#[allow(unused_assignments)] // see the `done` declaration below async fn run_async(args: PublishArgs, format: OutputFormat, _use_color: bool) -> Result<()> { let project_dir = args.project.canonicalize().unwrap_or(args.project.clone()); @@ -1604,6 +1605,10 @@ async fn run_async(args: PublishArgs, format: OutputFormat, _use_color: bool) -> // closed WebSocket (the hub drops connections while a job sits in the queue) // must RECONNECT + re-subscribe rather than silently end the publish — else // the command exits without ever downloading the artifact (#flaky-publish). + // rustc reports the `done = true` below as never read: the Complete arm + // breaks immediately, so both `if done { break }` guards only ever see + // `false`. Whether the flag still buys anything is a protocol question, + // not a lint one, so the store stays. let mut done = false; // `published` = the hub confirmed a server-side publish (TestFlight / App // Store etc.), where no local artifact is downloaded. diff --git a/crates/perry/tests/issue_4914_cluster_port_sharing.rs b/crates/perry/tests/issue_4914_cluster_port_sharing.rs index aeb854f962..8eea8fb5ca 100644 --- a/crates/perry/tests/issue_4914_cluster_port_sharing.rs +++ b/crates/perry/tests/issue_4914_cluster_port_sharing.rs @@ -25,13 +25,17 @@ //! The primary discovers a free port by binding port 0 once and closing it //! (the `listen(0)` shared-ephemeral-port round-trip itself is #4962). +#[cfg(all(unix, not(target_os = "macos")))] use std::path::PathBuf; +#[cfg(all(unix, not(target_os = "macos")))] use std::process::Command; +#[cfg(all(unix, not(target_os = "macos")))] fn perry_bin() -> PathBuf { PathBuf::from(env!("CARGO_BIN_EXE_perry")) } +#[cfg(all(unix, not(target_os = "macos")))] fn compile_and_run(dir: &std::path::Path, source: &str) -> String { let entry = dir.join("main.ts"); let output = dir.join("main_bin");