fix: for-in/for-of 左侧未声明写改读写登记集并集判定——读侧登记的全局槽漏全局对象属性写与 strict 漏抛

左侧标识符先读后写时全局槽由读侧登记,写侧单集判定不命中:sloppy 写穿透漏发(循环后读陈旧值)、strict 漏抛 ReferenceError。与其余 8 写点统一为 is_implicit_global_reg;补 3 枚判别性钉(sloppy for-in / for-of 穿透 + strict for-in 漏抛,回退单集判定必红)。
This commit is contained in:
2026-09-11 12:44:24 +00:00
parent cb0168faa1
commit ef3b95f6d3
3 changed files with 21 additions and 2 deletions
@@ -188,9 +188,28 @@ fn undeclared_read_then_destructuring_write_reaches_global() {
assert!(r.is_bool() && r.as_bool(), "先读后解构写应穿透全局对象,实际 {:?}", r);
}
// ── for-in/for-of 左侧先读后写:全局槽由读侧登记,LHS 写同样须穿透全局对象 ──
#[test]
fn undeclared_read_then_for_in_write_reaches_global() {
let r = eval_many(&["globalThis.k = 0", "var seen = k", "for (k in { a: 1 }) {}", "globalThis.k === 'a'"]).unwrap();
assert!(r.is_bool() && r.as_bool(), "先读后 for-in 左侧写应穿透全局对象,实际 {:?}", r);
}
#[test]
fn undeclared_read_then_for_of_write_reaches_global() {
let r = eval_many(&["globalThis.v = 0", "var seen = v", "for (v of [1, 2]) {}", "globalThis.v === 2"]).unwrap();
assert!(r.is_bool() && r.as_bool(), "先读后 for-of 左侧写应穿透全局对象,实际 {:?}", r);
}
// ── 严格模式:读侧已登记的全局槽同属未解析引用,后写抛 ReferenceError ──
#[test]
fn strict_undeclared_read_then_write_throws_reference() {
let result = eval("'use strict'; globalThis.s = 0; var seen = s; s = 5");
assert_err_contains(result, "s is not defined");
}
#[test]
fn strict_undeclared_read_then_for_in_write_throws_reference() {
let result = eval("'use strict'; globalThis.k = 0; var seen = k; for (k in { a: 1 }) {}");
assert_err_contains(result, "k is not defined");
}