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
19 changes: 19 additions & 0 deletions crates/perry-runtime/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,25 @@ fn ordinary_set_with_receiver(target: f64, key: f64, value: f64, receiver: f64)
) {
return true;
}
// `Object.preventExtensions(fn)` / `Object.seal(fn)` set
// NO_EXTEND / SEALED in the closure's GcHeader (functions are
// GcHeader-backed closures). A [[Set]] that would ADD a new own
// property must fail (OrdinaryDefineOwnProperty returns false,
// silent in non-strict, TypeError in strict); an existing own
// key can still be updated. (test262
// Object/preventExtensions/15.2.3.10-3-{3,13}.)
if !crate::closure::closure_has_own_dynamic_prop(cur_ptr, &name) {
let non_extensible = unsafe {
let gc = (cur_ptr as *const u8).sub(crate::gc::GC_HEADER_SIZE)
as *const crate::gc::GcHeader;
(*gc)._reserved
& (crate::gc::OBJ_FLAG_NO_EXTEND | crate::gc::OBJ_FLAG_SEALED)
!= 0
};
if non_extensible {
return false;
}
}
Comment on lines +1388 to +1406

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and surrounding symbols.
ast-grep outline crates/perry-runtime/src/proxy.rs --view expanded

# Read the relevant region with line numbers.
sed -n '1240,1435p' crates/perry-runtime/src/proxy.rs | cat -n

# Find the helper and related extensibility checks.
rg -n "closure_has_own_dynamic_prop|try_read_gc_header|OBJ_FLAG_NO_EXTEND|OBJ_FLAG_SEALED|prototype_of_for_set|create_or_update_receiver_property|ordinary_set_with_receiver" crates/perry-runtime/src -A 3 -B 3

# Search for APIs or tests suggesting functions can be used as prototypes.
rg -n "Object::create|Object.create|setPrototypeOf|prototype.*closure|closure.*prototype|is_closure_ptr" crates/perry-runtime crates -A 2 -B 2

Repository: PerryTS/perry

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact set-path logic around the closure branch.
sed -n '1300,1415p' crates/perry-runtime/src/proxy.rs | cat -n

# Inspect the helper that actually creates/updates the receiver property.
rg -n "fn create_or_update_receiver_property|create_or_update_receiver_property\(" crates/perry-runtime/src/proxy.rs crates/perry-runtime/src -A 20 -B 10

# Inspect how closure prototypes are recorded and used, to see whether closures can be ordinary prototypes.
sed -n '1,140p' crates/perry-runtime/src/closure/dynamic_props.rs | cat -n
sed -n '380,430p' crates/perry-runtime/src/closure/dynamic_props.rs | cat -n

# Narrow evidence for prototype usage with functions.
rg -n "Object\.setPrototypeOf\([A-Za-z_][A-Za-z0-9_]*, [A-Za-z_][A-Za-z0-9_]*\)|Object\.create\([A-Za-z_][A-Za-z0-9_]*\)|function.*prototype" crates/perry/tests crates/perry-runtime/src -A 2 -B 2

Repository: PerryTS/perry

Length of output: 50370


Guard the non-extensibility check on the receiver.
This reads SEALED/NO_EXTEND from current, so if a function sits on the prototype chain it can incorrectly block obj.prop = value even when obj is extensible. Let the receiver’s own CreateDataProperty path enforce extensibility, or only apply this check when current == receiver.

🤖 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/proxy.rs` around lines 1388 - 1406, Guard the
non-extensibility check in proxy.rs inside the property-set path so it applies
to the receiver, not the prototype `current` object. The current
`closure_has_own_dynamic_prop` / `GcHeader` flag check can wrongly reject writes
when a function on the prototype chain is sealed or non-extensible; update the
`Set` handling to only return false for adding a new property when `current ==
receiver`, or otherwise rely on the receiver’s `CreateDataProperty` /
extensibility checks to enforce the rule.

}
return create_or_update_receiver_property(receiver, key, value);
}
Expand Down
Loading