Skip to content

fix(proxy): #5905 — forward setPrototypeOf/deleteProperty through proxy targets with correct throw semantics#5967

Merged
proggeramlug merged 1 commit into
mainfrom
fix/t262-5905-proxy-target-proxy
Jul 4, 2026
Merged

fix(proxy): #5905 — forward setPrototypeOf/deleteProperty through proxy targets with correct throw semantics#5967
proggeramlug merged 1 commit into
mainfrom
fix/t262-5905-proxy-target-proxy

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Part of #5905 (built-ins/Proxy worklist).

Root cause

When a Proxy's target is itself a Proxy and the outer trap is missing/undefined/null, the operation forwards down the chain. Several forwarding paths dropped the spec's throw-on-false behavior or mis-reported non-configurable exotic properties as deletable.

Fixes

  1. Object.setPrototypeOf(proxy, x) discarded the internal [[SetPrototypeOf]] boolean. A false result (a non-extensible target reached through the chain, or an inner trap returning false) must throw a TypeError, while Reflect.setPrototypeOf keeps returning the boolean. (crates/perry-runtime/src/object/object_ops/define_properties.rs)

  2. Reflect.deleteProperty / delete never rejected an Array's non-configurable length or a plain (non-arrow, non-bound) function's non-configurable prototype. Both are exotic own properties with no descriptor-table entry, so obj_value_attrs/get_property_attrs returned None and the delete silently reported success. Now rejected in reflect_ordinary_delete_property_key and js_object_delete_field. (crates/perry-runtime/src/proxy.rs, crates/perry-runtime/src/object/delete_rest.rs)

  3. delete proxy.key lowers to the ProxyDelete HIR node, whose codegen returned js_proxy_delete's boolean directly and ignored strict mode. A strict-mode delete that resolves to false must throw, exactly like the ordinary member-delete path — now routed through js_delete_result with the strict flag. (crates/perry-codegen/src/expr/proxy_reflect.rs)

Before/after

built-ins/Proxy: 218/240 → 223/240 (parity 90.8% → 92.9%).

Newly passing (5):

  • setPrototypeOf/trap-is-missing-target-is-proxy.js
  • setPrototypeOf/trap-is-undefined-target-is-proxy.js
  • deleteProperty/trap-is-missing-target-is-proxy.js
  • deleteProperty/trap-is-null-target-is-proxy.js
  • deleteProperty/trap-is-undefined-target-is-proxy.js

Zero regressions: verified the built-ins/Proxy slice (no new failures) plus built-ins/Reflect, built-ins/Object{,/defineProperty,/getPrototypeOf,/setPrototypeOf}, and language/expressions/delete (all unchanged).

cargo fmt --all -- --check and scripts/check_file_size.sh pass.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed delete to correctly report failure (and thus strict-mode throwing) for special non-configurable properties like Array length and function prototype.
    • Corrected Proxy deletion behavior so strict-mode observable semantics match ordinary delete, rather than incorrectly succeeding.
    • Fixed Proxy-backed Object.setPrototypeOf to fail reliably when the operation is not permitted (instead of appearing to succeed).

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b411bcf-e88a-456d-b63e-fd406cbaf032

📥 Commits

Reviewing files that changed from the base of the PR and between 174f531 and 7d52ea4.

📒 Files selected for processing (5)
  • crates/perry-codegen/src/expr/proxy_reflect.rs
  • crates/perry-runtime/src/object/delete_rest.rs
  • crates/perry-runtime/src/object/object_ops/define_properties.rs
  • crates/perry-runtime/src/proxy.rs
  • crates/perry-runtime/src/proxy/has_delete.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/perry-codegen/src/expr/proxy_reflect.rs
  • crates/perry-runtime/src/object/object_ops/define_properties.rs
  • crates/perry-runtime/src/object/delete_rest.rs

📝 Walkthrough

Walkthrough

Changes add proxy has/delete handling, strict-mode-aware proxy delete lowering, delete guards for Array length and function prototype, and TypeError propagation for failed Proxy setPrototypeOf.

Changes

Proxy and delete semantics

Layer / File(s) Summary
Shared exotic own-property predicate
crates/perry-runtime/src/proxy.rs
Adds key byte comparison helpers and an exotic-own-property predicate for Array length and plain function prototype properties.
Proxy has/delete helpers
crates/perry-runtime/src/proxy.rs, crates/perry-runtime/src/proxy/has_delete.rs
Implements proxy has and deleteProperty handling, ordinary delete fallback, and the new delete helper pipeline.
Ordinary delete guards
crates/perry-runtime/src/object/delete_rest.rs
Adds early failure checks for deleting Array length and plain function prototype from ordinary object paths.
Strict proxy delete lowering
crates/perry-codegen/src/expr/proxy_reflect.rs
Routes Expr::ProxyDelete through strict-mode-aware delete-result handling instead of returning the raw proxy delete boolean.
Proxy setPrototypeOf failure propagation
crates/perry-runtime/src/object/object_ops/define_properties.rs
Checks the reflected Proxy setPrototypeOf result and throws TypeError when it fails.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

  • PerryTS/perry#5146: Both PRs adjust js_object_delete_field behavior for non-configurable array-related deletion cases.
  • PerryTS/perry#5396: Both PRs enforce TypeError "#<Object> is not extensible" when setPrototypeOf fails on a Proxy path.
  • PerryTS/perry#5882: Both PRs touch proxy dispatch and invariant-aware proxy has/delete handling in the same runtime area.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main proxy forwarding and throw-semantics fix.
Description check ✅ Passed The description covers the root cause, fixes, affected files, related issue, and verification results, though it doesn't use the template headings verbatim.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/t262-5905-proxy-target-proxy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@crates/perry-runtime/src/proxy.rs`:
- Around line 819-831: The prototype-delete guard in the proxy/object delete
paths still treats async functions like plain functions, so `delete
fn.prototype` can incorrectly return false. Update the logic in `proxy.rs` and
mirror the same change in `object/delete_rest.rs` so the `prototype`
special-case also excludes async functions alongside the existing
`closure_is_arrow` and `closure_is_bound_method` checks, using the relevant
closure inspection helpers around the `ClosureHeader` path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 728ad0a7-fc25-4927-a249-cba6ee3e3336

📥 Commits

Reviewing files that changed from the base of the PR and between 17062a8 and 174f531.

📒 Files selected for processing (4)
  • crates/perry-codegen/src/expr/proxy_reflect.rs
  • crates/perry-runtime/src/object/delete_rest.rs
  • crates/perry-runtime/src/object/object_ops/define_properties.rs
  • crates/perry-runtime/src/proxy.rs

Comment thread crates/perry-runtime/src/proxy.rs
…xy targets with correct throw semantics

When a Proxy's target is itself a Proxy and the outer trap is missing/undefined/null,
the operation forwards down the chain. Several forwarding paths dropped the spec's
throw-on-false behavior or mis-reported non-configurable exotic properties as deletable:

- Object.setPrototypeOf(proxy, x) discarded the internal [[SetPrototypeOf]] boolean;
  a false result (non-extensible target reached through the chain) must throw a
  TypeError (Reflect.setPrototypeOf keeps returning the boolean). Fixes
  setPrototypeOf/trap-is-{missing,undefined}-target-is-proxy.

- Reflect.deleteProperty / delete never rejected an Array's non-configurable
  `length` or a plain function's non-configurable `prototype` — both are exotic
  own properties with no descriptor-table entry, so the delete silently reported
  success. Reject them in reflect_ordinary_delete_property_key and js_object_delete_field.

- delete proxy.key is lowered to the ProxyDelete HIR node, whose codegen returned
  js_proxy_delete's boolean directly and ignored strict mode; a strict-mode delete
  that resolves to false must throw. Route it through js_delete_result like the
  ordinary member-delete path. Fixes deleteProperty/trap-is-{missing,null,undefined}-target-is-proxy.

built-ins/Proxy 218/240 -> 223/240; no regressions in Reflect/Object/delete slices.
@proggeramlug
proggeramlug force-pushed the fix/t262-5905-proxy-target-proxy branch from 174f531 to 7d52ea4 Compare July 4, 2026 14:07
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Rebased on main and split crates/perry-runtime/src/proxy.rs (was 2011 lines, over the 2000-line gate) — extracted the coherent [[HasProperty]]/[[Delete]] cluster (js_proxy_has, js_proxy_delete, reflect_ordinary_delete{,_property_key}) into a new sibling module crates/perry-runtime/src/proxy/has_delete.rs, matching the existing proxy/*.rs submodule layout. Pure code motion, no behavior change; proxy.rs is now 1867 lines. cargo fmt --all -- --check and scripts/check_file_size.sh pass, and the built-ins/Proxy slice is unchanged at 223/240.

@proggeramlug
proggeramlug merged commit 408a19c into main Jul 4, 2026
16 of 17 checks passed
@proggeramlug
proggeramlug deleted the fix/t262-5905-proxy-target-proxy branch July 4, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant