fix: 捕获块函数名入口实例化为 undefined

This commit is contained in:
2026-09-18 01:29:53 +00:00
parent 4236290b45
commit 46464efede
2 changed files with 30 additions and 7 deletions
+22 -6
View File
@@ -743,12 +743,26 @@ impl Emitter {
));
}
// var 绑定函数入口实例化:被捕获的 var 名统一 MAKE_CELL(undefined)。
// 规范上 var 在函数入口即初始化为 undefined(HoistDeclaration),声明语句
// 只是赋值;否则声明语句前创建的闭包读取占位 cell → TDZ 误报。参数与
// arguments 已在上方初始化(跳过以免覆盖参数值);let/const/class 保持
// TDZ 语义不动。声明语句的 MAKE_CELL 按占位更新语义覆盖此初值。
let mut var_names: Vec<String> = collect_var_binding_names(body_stmts)
// var 与块级函数外层 var 绑定函数入口实例化:被捕获名统一
// MAKE_CELL(undefined)。规范上 var 在函数入口即初始化为 undefined
// (HoistDeclaration),sloppy 下块级函数名同建外层 var 绑定;声明语句只是
// 赋值。缺此实例化时,声明点前创建的闭包读取占位 cell → TDZ 误报,非捕获面
// 则取调用方寄存器残留。参数与 arguments 已在上方初始化(跳过以免覆盖参数
// 值);let/const/class 保持 TDZ 语义不动。声明语句的 MAKE_CELL 按占位更新
// 语义覆盖此初值。
let mut captured_entry_names = collect_var_binding_names(body_stmts);
// 块级函数外层 var 名与 instantiate_var_bindings 同源并入;抑制名(形参/
// 词法声明同名)无外层绑定,不并入。抑制集在此独立计算:本循环早于函数体
// 的 block_fn_suppressed 构建。
if !ctx.is_strict {
let suppressed = self.collect_block_fn_suppressed_names(body_stmts, &ctx.param_names);
for name in self.collect_block_function_names(body_stmts) {
if !suppressed.contains(&name) {
captured_entry_names.insert(name);
}
}
}
let mut var_names: Vec<String> = captured_entry_names
.into_iter()
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
.filter(|n| ctx.captured_bindings.contains_key(n))
@@ -792,6 +806,8 @@ impl Emitter {
// 块级函数外层 var 绑定同样只预声明不发射定义指令,缺失入口写会让块前读取
// 取到调用方残留;入口写须并入(strict 与抑制名除外)。
if !ctx.is_strict {
// 直接子标签函数声明在函数入口首 sub-pass 物化闭包,声明点前读即为函数;
// 此处按名排除只省一次冗余的非捕获 undefined 写,非正确性必需。
let labeled_hoisted: HashSet<&str> = body_stmts
.iter()
.filter_map(|stmt| Self::labeled_function_decl(stmt))
@@ -3,7 +3,7 @@
//!
//! 覆盖:显式块主形(块前读/块后读/调用)、if 支臂形、显式块内 if 支臂形、
//! 嵌套块形、strict 不泄漏守卫、裸读形、形参同名抑制守卫、直接子标签函数
//! 声明守卫。
//! 声明守卫、被嵌套函数捕获形。
use std::sync::Arc;
@@ -73,3 +73,10 @@ fn block_fn_direct_labeled_decl_before_read_stays_function() {
// 直接子标签函数声明守卫:入口物化闭包,声明点前读仍是函数对象。
truthy("function t(){ var r = typeof g; l: function g(){return 1}; return r === 'function' && typeof g === 'function' } t()");
}
#[test]
fn block_fn_captured_before_read_is_undefined() {
// 捕获形:块函数名被嵌套函数引用时同样在入口实例化为 undefined,块前读不得
// 取调用方寄存器残留;块求值后闭包读到写回的函数对象。
truthy("function t(){ var r = typeof g; var f = function(){ return g }; { function g(){} } return r === 'undefined' && typeof f() === 'function' } t()");
}