You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A rejected STRICT ordinary-object write never throws — the mirror image of #9394 on the object path #9422
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:348 and :479). The strictness flag exists and is threaded elsewhere — it simply is not passed here.
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 Throw flag, 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.
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";consto={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 with PERRY_SAVE_LL:
The two strict = 0 literals at expr/property_set.rs:348,479 are inside try_lower_sloppy_class_field_store / …_boxed_store, which expr/proxy_reflect.rs reaches only under if !*strict. 0 is 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) tested OBJ_FLAG_FROZEN only, so Object.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 silent return annotated "strict-mode throw is handled by the caller's PutValue". That entry is the caller. Fixed in PR #9458 by reusing array_length_is_non_writable, the predicate push/pop/shift/unshift have used since test262's set-length-*-non-writable.
The real remaining gap is the opposite direction
Sloppy o.x += 1 on a frozen object over-throws (node silent, perry TypeError), because Expr::PropertySet carries 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.
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 = 0 literals live in a lane reached only under if !*strict. The one genuine strict under-throw found nearby (non-writable arr.length via the descriptor side table) is fixed in #9458. test_gap_9422_strict_object_store_strictness.cts. Verified on main 0a1c137d90 with a fresh build: fixture byte-identical to node --experimental-strip-types (stdout, stderr, and exit code).
The real remaining gap is the OPPOSITE direction and has its own issue: #9459 (sloppy o.x += 1 on a frozen object over-throws; Expr::PropertySet carries no strictness field).
Activity
proggeramlug commentedon Sep 1, 2026
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:
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:The 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.
fix: ES module init is lowered strict (#9423); a rejected strict `arr…
proggeramlug commentedon 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 (sloppy
o.x += 1on a frozen object over-throws;Expr::PropertySetcarries no strictness field).fix(codegen): PropertySet rejections honour sloppy mode (#9459); a wr…