fix: 顶层 var 全局属性求值前创建(声明定时语义)+ 无初始化 var 声明不再抹槽值

This commit is contained in:
2026-09-09 22:00:29 +08:00
parent bd1427b116
commit 2457f283fe
6 changed files with 115 additions and 16 deletions
@@ -98,3 +98,47 @@ fn descriptor_attributes_match_spec() {
let d = eval_string("var x = 1; JSON.stringify(Object.getOwnPropertyDescriptor(globalThis, 'x'))");
assert_eq!(d, r#"{"value":1,"writable":true,"enumerable":true,"configurable":false}"#);
}
#[test]
fn var_property_exists_before_declaration_statement() {
// 全局声明实例化:属性在脚本求值开始即创建,声明语句执行前的
// 反射/读取即可见绑定(值 undefined、脚本模式 configurable:false)。
// 断言值经临时全局承载——尾部 var 声明语句会覆盖程序完成值。
eval_truthy(
"(function(){ seen = Object.prototype.hasOwnProperty.call(globalThis, 'gv'); })(); var gv; seen === true",
);
eval_truthy("(function(){ seen = (typeof gv === 'undefined'); })(); var gv; seen === true");
eval_truthy("(function(){ var p = Object.getOwnPropertyDescriptor(globalThis, 'gv'); seen = (p.configurable === false && p.value === undefined); })(); var gv; seen === true");
}
#[test]
fn var_no_init_preserves_prior_write() {
eval_truthy("x = 1; var x; globalThis.x === 1");
eval_truthy("x = 1; var x; x === 1");
}
#[test]
fn var_no_init_preserves_prior_write_captured() {
// 被捕获 var 的值在 cell:声明语句从 cell 同步全局属性,不抹掉先前写入。
eval_truthy("x = 1; var x; (function(){ return x; })() === 1");
eval_truthy("x = 1; var x; globalThis.x === 1");
}
#[test]
fn var_self_reference_reads_binding_value() {
eval_truthy("var x = x; typeof x === 'undefined'");
eval_truthy("x = 7; var x = x; x === 7");
}
#[test]
fn function_callable_before_its_declaration() {
eval_truthy("(function(){ return f() === 42; })(); function f(){ return 42; }");
}
#[test]
fn var_no_init_does_not_clobber_function_binding() {
eval_truthy("function f(){ return 1; } var f; typeof f === 'function' && f() === 1");
// 不比较 globalThis.f === f 同一性:属性读路径存在预存 codegen 缺陷,
// 行为契约是声明后属性仍为原函数对象(可调用、返回值正确)。
eval_truthy("function f(){ return 1; } var f; typeof globalThis.f === 'function' && globalThis.f() === 1");
}