Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions crates/perry-runtime/src/object/native_call_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,30 @@ pub unsafe extern "C" fn js_native_call_method(
}
}
}
// A BARE heap pointer — a real object whose value was never NaN-boxed, so its
// top 16 bits are zero and it decodes as a denormal double. Classifying it as a
// "number" here throws `<method> is not a function` on a perfectly good object.
// Validate deref-free (above the handle band + a genuinely tracked allocation),
// rebox as a POINTER_TAG value and dispatch on the object it actually is.
// Perry already recovers bare pointers on other dispatch paths; this one threw
// before it ever got the chance.
if jsval.is_number() && (jsval.bits() >> 48) == 0 {
let raw = jsval.bits() as usize;
if crate::value::addr_class::is_above_handle_band(raw)
&& crate::value::addr_class::is_valid_obj_ptr(raw as *const u8)
{
let reboxed = crate::value::JSValue::pointer(raw as *const u8);
if reboxed.bits() != jsval.bits() {
return js_native_call_method(
f64::from_bits(reboxed.bits()),
method_name_ptr,
method_name_len,
args_ptr,
args_len,
);
}
}
}
Comment on lines +1612 to +1635

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that crate::object::is_valid_obj_ptr contains the necessary page-map checks 
# that are absent in crate::value::addr_class::is_valid_obj_ptr.
ast-grep outline crates/perry-runtime/src/object -m is_valid_obj_ptr
rg -n -A 10 "pub.*fn is_valid_obj_ptr" crates/perry-runtime/src/object/

Repository: PerryTS/perry

Length of output: 317


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and inspect the touched function/body around the reported lines.
ast-grep outline crates/perry-runtime/src/object/native_call_method.rs
wc -l crates/perry-runtime/src/object/native_call_method.rs
sed -n '700,780p' crates/perry-runtime/src/object/native_call_method.rs
sed -n '1590,1665p' crates/perry-runtime/src/object/native_call_method.rs

Repository: PerryTS/perry

Length of output: 9080


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the two validation helpers the comment compares.
rg -n -A 20 -B 5 "pub.*fn is_valid_obj_ptr|fn is_valid_obj_ptr" crates/perry-runtime/src/object crates/perry-runtime/src/value

Repository: PerryTS/perry

Length of output: 2319


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the GC rooting helper used here and any object-level pointer validator.
rg -n -A 25 -B 10 "fn root_nanbox_f64|root_nanbox_f64\(|pub.*fn is_valid_obj_ptr|fn is_valid_obj_ptr" crates/perry-runtime/src

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any object-level pointer validator and inspect the GC rooting helper.
rg -n -A 20 -B 5 "crate::object::is_valid_obj_ptr|pub.*fn is_valid_obj_ptr|fn is_valid_obj_ptr|root_nanbox_f64" crates/perry-runtime/src

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether an object-level validator exists and inspect the exact control flow around the recovery block.
rg -n -A 8 -B 8 "is_valid_obj_ptr" crates/perry-runtime/src/object crates/perry-runtime/src/value
sed -n '703,840p' crates/perry-runtime/src/object/native_call_method.rs

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact validator implementations and the rooting helper used by this function.
sed -n '161,220p' crates/perry-runtime/src/value/addr_class.rs
sed -n '1,220p' crates/perry-runtime/src/object/to_string.rs 2>/dev/null || true
rg -n -A 20 -B 5 "pub.*fn is_valid_obj_ptr|fn is_valid_obj_ptr|root_nanbox_f64" crates/perry-runtime/src/gc crates/perry-runtime/src/object crates/perry-runtime/src/value

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the primitive-dispatch entry and the rooting helper implementation.
sed -n '1,220p' crates/perry-runtime/src/object/native_call_method/primitive_methods.rs
rg -n -A 40 -B 10 "fn root_nanbox_f64|root_nanbox_f64\(" crates/perry-runtime/src/gc crates/perry-runtime/src

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '637,700p' crates/perry-runtime/src/object/native_call_method.rs

Repository: PerryTS/perry

Length of output: 3587


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the root_nanbox_f64 implementation and the exact ordering around the recovery block.
rg -n -A 30 -B 10 "fn root_nanbox_f64|pub fn root_nanbox_f64|root_nanbox_f64\(" crates/perry-runtime/src/gc.rs crates/perry-runtime/src/gc crates/perry-runtime/src/object/native_call_method.rs

Repository: PerryTS/perry

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the GC runtime handle scope implementation and inspect root_nanbox_f64.
fd -a "RuntimeHandleScope" crates/perry-runtime/src
fd -a "gc" crates/perry-runtime/src
rg -n -A 30 -B 10 "fn root_nanbox_f64|pub fn root_nanbox_f64|impl RuntimeHandleScope" crates/perry-runtime/src

Repository: PerryTS/perry

Length of output: 6263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -A 10 -B 5 "pub.*fn is_valid_obj_ptr|pub use .*is_valid_obj_ptr|mod addr_class" crates/perry-runtime/src/object.rs crates/perry-runtime/src/object/mod.rs crates/perry-runtime/src/object/**/*.rs crates/perry-runtime/src/value/addr_class.rs

Repository: PerryTS/perry

Length of output: 1633


Move bare-pointer recovery before primitive dispatch

A bare heap pointer can still flow into primitive_methods::dispatch_primitive as a number and take the primitive method path before this fallback runs. Normalize/rebox it at the top of js_native_call_method so the receiver is treated as an object first.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/native_call_method.rs` around lines 1612 -
1635, Move the bare-pointer detection and reboxing logic from its current
fallback location to the beginning of js_native_call_method, before
primitive_methods::dispatch_primitive or any receiver classification. Preserve
the existing validation through is_above_handle_band and is_valid_obj_ptr, then
redispatch using the reboxed pointer so valid bare heap pointers are handled as
objects.

let primitive_kind: Option<&'static str> = if jsval.is_any_string() {
Some("string")
} else if jsval.is_int32() || jsval.is_number() {
Expand Down
Loading