Repository navigation
A rejected STRICT ordinary-object write never throws — the mirror image of #9394 on the object path #9422
Description
Activity
This issue does not reproduce as filed. Both its stated symptom and its stated root cause are wrong on current
main. Investigated under PR #9458.The repro in this issue passes today:
"use strict"; const o={x:1}; Object.freeze(o); o.x=9; // node TypeError, perry TypeError
So do all seven shapes listed here, plus computed-key, class-field,
+=,++and frozen-array-index — 34 of 36 lines of the verification fixture are byte-identical to node before any change.The stated root cause, settled from IR
I filed this citing
js_put_value_set(..., strict = 0)at every property-set site (a line from #9426's changelog). That is wrong. Strict and sloppy twins of the exact program above, dumped withPERRY_SAVE_LL:f() [strict]: invoke void @js_class_field_set_fallback(...) ← throws g() [sloppy]: invoke double @js_put_value_set(..., i32 0) ← correct for sloppyThe two
strict = 0literals atexpr/property_set.rs:348,479are insidetry_lower_sloppy_class_field_store/…_boxed_store, whichexpr/proxy_reflect.rsreaches only underif !*strict.0is the right constant there, and the strict arm never enters that lane.This resolves the contradiction I flagged during review: #9426's commit message is right ("already passes to the ordinary-object
[[Set]]") and its changelog line is wrong ("every property-set site"). Two documents by the same author disagreeing is not two witnesses — the IR was the tiebreaker.What was actually found and fixed
One genuine strict under-throw, one lane over:
js_array_set_length_strict(array/push_pop.rs:1269) testedOBJ_FLAG_FROZENonly, soObject.defineProperty(arr, "length", {writable:false})— which records the attribute in the descriptor side table without freezing — fell through to the sloppy body, whose non-writable arm is a silentreturnannotated "strict-mode throw is handled by the caller's PutValue". That entry is the caller. Fixed in PR #9458 by reusingarray_length_is_non_writable, the predicatepush/pop/shift/unshifthave used since test262'sset-length-*-non-writable.The real remaining gap is the opposite direction
Sloppy
o.x += 1on a frozen object over-throws (node silent, perry TypeError), becauseExpr::PropertySetcarries no strictness field at all. Filed as #9459. That is the object-path analogue of #9394, and it is a hard failure rather than a wrong value — worth more than this issue was.Closing this as not-reproducing once #9458 lands, unless someone can produce a shape that still under-throws.
- added a commit that references this issue
on Sep 2, 2026 Closing as not-reproducing-as-filed, per the investigation on PR #9458: the issue's own repro and all seven listed shapes already passed on unfixed main (34/36 fixture lines), and the stated root cause was disproven from IR — the
strict = 0literals live in a lane reached only underif !*strict. The one genuine strict under-throw found nearby (non-writablearr.lengthvia the descriptor side table) is fixed in #9458.test_gap_9422_strict_object_store_strictness.cts. Verified on main0a1c137d90with a fresh build: fixture byte-identical tonode --experimental-strip-types(stdout, stderr, and exit code).
The real remaining gap is the OPPOSITE direction and has its own issue: #9459 (sloppyo.x += 1on a frozen object over-throws;Expr::PropertySetcarries no strictness field).- added a commit that references this issue
on Sep 2, 2026
Per ES §6.2.5.7, a failed
[[Set]]must throw in strict mode. Perry is silent.Root cause, already located
Codegen emits
js_put_value_set(..., strict = 0)at every property-set site (crates/perry-codegen/src/expr/property_set.rs:348and:479). The strictness flag exists and is threaded elsewhere — it simply is not passed here.Relationship to #9394
Exact mirror image. #9394 was the array element path throwing in sloppy mode where node is silent; this is the ordinary-object path staying silent in strict mode where node throws. #9418 fixed the array side by carrying the assignment's own
Throwflag, which codegen already had — the same flag is what this needs.Deliberately kept out of #9418's scope so a correctness fix for one path did not silently change the other; the fixture there asserts the object control in the sloppy arm only, with a comment pointing here.
Test guidance
Assert both arms — strict throws and sloppy stays silent — for frozen, sealed, non-extensible and non-writable-descriptor receivers. Asserting only one arm is precisely what let #9394 through.
Found while fixing #9394.