fix: 嵌套函数删除隐式全局名后外层读取不再取残留

This commit is contained in:
2026-09-17 17:29:25 +00:00
parent 8953e18ff1
commit 37b1c4a62b
6 changed files with 86 additions and 5 deletions
+6
View File
@@ -55,6 +55,11 @@ pub struct CompileCtx {
/// 是否为 eval 脚本:脚本顶层 var/function 声明落全局对象时属性
/// configurable:true(普通脚本顶层为 false)。仅动态脚本入口设置。
pub(crate) is_eval_script: bool,
/// 编译树是否源自 eval 脚本:eval 代码自身顶层 var/function 名物化 c:true
/// 全局属性、可删,本标志随嵌套函数继承,供 delete 分类判定。不直接继承
/// `is_eval_script`——该标志只标识当前顶层程序,且还控制顶层全局写点与块级
/// 函数门控,整体传播语义过宽。
pub(crate) is_eval_origin: bool,
/// 源码是否为 `source_escape` 产物(动态编译入口):正则字面量源文本切片
/// 的池键形态据此选择(见 [`Emitter::source_encoded`])。
pub(crate) source_encoded: bool,
@@ -183,6 +188,7 @@ impl CompileCtx {
is_global_scope: false,
repl_persist: false,
is_eval_script: false,
is_eval_origin: false,
source_encoded: false,
static_block_this_reg: None,
field_buffer: None,
+4 -1
View File
@@ -50,7 +50,10 @@ impl Emitter {
// 文档),缺失抛 ReferenceError(unresolvable)。tier 名属性由
// GlobalDeclarationInstantiation(脚本顶层声明实例化)序言创建(仅删除后缺失);
// 未声明名属性缺失即 unresolvable。已声明 var 不再落引擎侧镜像副本。
if self.is_global_tier_name(ctx, name) || ctx.implicit_global_reads.contains(&var_reg) {
// 隐式全局槽(读侧登记与写侧登记同属一个槽)同样读全局对象属性:属性是
// 隐式全局值的唯一真源,嵌套函数内 delete 真删后外层裸读据此可见,引擎侧
// 镜像槽不反映删除。
if self.is_global_tier_name(ctx, name) || ctx.is_implicit_global_reg(var_reg) {
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
ctx.inst(Inst::new(OpCode::LOAD_GLOBAL, Operand::Reg(r), Operand::Const(key_idx), Operand::None));
} else {
+3 -2
View File
@@ -215,9 +215,10 @@ impl Emitter {
// var 名集 ∪ 顶层函数声明名,物化 c:true 全局属性)
// 可删;eval 顶层 let/const 不在名集内(自身 lexical
// 环境不可删),与非 eval 脚本自身顶层 var/函数名
// (c:false)同保留 false。
// (c:false)同保留 false。eval 起源随嵌套函数继承,
// 子 ctx 内 delete 同一名同样判可删。
ctx.is_implicit_global_reg(binding.reg)
|| (ctx.is_eval_script && ctx.global_tier_names.contains(name))
|| (ctx.is_eval_origin && ctx.global_tier_names.contains(name))
} else {
// 函数/块作用域局部绑定:非属性引用,恒 false。
false
+3
View File
@@ -368,6 +368,9 @@ impl Emitter {
// 顶层已声明 var 名集随作用域继承:嵌套函数裸读/写顶层 var 直连全局对象
// (经继承的 scope-0 绑定 + 作用域索引判定,不被局部同名遮蔽误判)。
ctx.global_tier_names = parent_ctx.global_tier_names.clone();
// eval 起源随作用域继承:eval 顶层 var/函数名在嵌套函数内同样可删(物化
// c:true 全局属性),delete 分类判定读此标志。
ctx.is_eval_origin = parent_ctx.is_eval_origin;
for (name, reg) in extra_bindings {
ctx.scopes.symbols.scopes[0].bindings.insert(
(*name).to_string(),
+3
View File
@@ -265,6 +265,9 @@ impl Emitter {
ctx.is_global_scope = true;
ctx.repl_persist = repl_persist;
ctx.is_eval_script = is_eval_script;
// eval 起源随嵌套函数继承,供 delete 分类判定 eval 顶层可删名;与
// `is_eval_script` 区分——后者只标识当前顶层程序,传播会误改全局写点。
ctx.is_eval_origin = is_eval_script;
ctx.source_encoded = self.source_encoded;
// 脚本顶层严格模式由源码 "use strict" directive 决定(嵌套函数经父 ctx 继承)。
ctx.is_strict = program.has_use_strict_directive();