diff --git a/crates/oxide_emit/src/prepass/declare.rs b/crates/oxide_emit/src/prepass/declare.rs index 60658170..9419f81d 100644 --- a/crates/oxide_emit/src/prepass/declare.rs +++ b/crates/oxide_emit/src/prepass/declare.rs @@ -174,127 +174,95 @@ impl Emitter { /// # 边界与前提 /// - 只处理块内函数声明;switch 不推 scope(其 case 内函数声明随 switch /// 作用域修复一并处理);export 声明不可能出现在块内。 - /// - `if` 支臂的裸函数声明属 Annex B 隐式支臂块:名已有外层 var 绑定承载 - /// (非 eval 脚本顶层、未被形参/词法同名抑制、且非脚本顶层只读三常量)时 - /// 不建外层块绑定,声明点物化进该 var 槽(建外层块 Let 会遮蔽同名 var); - /// 名无外层 var 绑定(形参/词法抑制、eval 脚本顶层或只读三常量)时保留 - /// 块级绑定承载支臂闭包,避免声明点覆写外层绑定。支臂为块时由该块自身的 - /// 块入口流程处理。 + /// - `if_arm` 标记当前递归是否位于 `if` 支臂隐式块内。Annex B 下该隐式块的 + /// 函数声明名已有外层 var 绑定承载(非 eval 脚本顶层、未被形参/词法同名 + /// 抑制、且非全局只读三常量)时不建当前块绑定,声明点物化进该 var 槽 + /// (建当前块 Let 会遮蔽同名 var);名无外层 var 绑定(形参/词法抑制、eval + /// 脚本顶层或全局只读三常量)时保留块级绑定承载支臂闭包,避免声明点覆写 + /// 外层绑定。门控只作用于 `if` 支臂下的叶子,块直接子不受影响。 + /// - 支臂为容器(循环/`try`/`with`/标签/显式块)时按各自作用域规则继续下钻, + /// 使嵌套 `if` 支臂的函数声明同样到达门控。 /// - 与 lexical 预声明的顺序:本函数在前,`{ let g; function g(){} }` 时 /// lexical 的 `declare_predeclared` 命中已存在的函数绑定自然报重复声明错, /// 避免函数预声明被 lexical 占位静默覆盖而破坏 let 的 TDZ。 /// - var 与函数同名(`{ var g; function g(){} }`):var 落函数作用域、 /// 函数落当前块,不同 scope 互不冲突。 - pub(crate) fn predeclare_block_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) { + /// - 须在 `block_fn_suppressed` 已构建之后调用(函数体/程序声明实例化阶段 + /// 先于任何块 emit)。 + pub(crate) fn predeclare_block_function_declarations( + &self, statements: &[Statement], ctx: &mut CompileCtx, if_arm: bool, + ) { for statement in statements { match statement { Statement::FunctionDeclaration(f) => { let Some(identifier) = &f.id else { continue; }; + if if_arm && Self::if_arm_has_outer_var_carrier(ctx, identifier.name.as_str()) { + continue; + } let reg = ctx.alloc_reg(); let _ = ctx.declare_initialized(identifier.name.as_str(), reg, VariableDeclarationKind::Let, false); } Statement::BlockStatement(bs) => { ctx.push_scope(); - self.predeclare_block_function_declarations(&bs.body, ctx); + self.predeclare_block_function_declarations(&bs.body, ctx, if_arm); ctx.pop_scope(); } Statement::IfStatement(is) => { - self.predeclare_if_arm_block_function(&is.consequent, ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&is.consequent), ctx, true); if let Some(alt) = &is.alternate { - self.predeclare_if_arm_block_function(alt, ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(alt), ctx, true); } } Statement::WhileStatement(wh) => { - self.predeclare_block_function_declarations(std::slice::from_ref(&wh.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&wh.body), ctx, if_arm); } Statement::DoWhileStatement(dw) => { - self.predeclare_block_function_declarations(std::slice::from_ref(&dw.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&dw.body), ctx, if_arm); } Statement::ForStatement(fs) => { // 循环推独立作用域(头声明所在),函数声明落该作用域与 emit 对齐。 ctx.push_scope(); - self.predeclare_block_function_declarations(std::slice::from_ref(&fs.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&fs.body), ctx, if_arm); ctx.pop_scope(); } Statement::ForInStatement(fi) => { ctx.push_scope(); - self.predeclare_block_function_declarations(std::slice::from_ref(&fi.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&fi.body), ctx, if_arm); ctx.pop_scope(); } Statement::ForOfStatement(fo) => { ctx.push_scope(); - self.predeclare_block_function_declarations(std::slice::from_ref(&fo.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&fo.body), ctx, if_arm); ctx.pop_scope(); } Statement::TryStatement(ts) => { ctx.push_scope(); - self.predeclare_block_function_declarations(&ts.block.body, ctx); + self.predeclare_block_function_declarations(&ts.block.body, ctx, if_arm); ctx.pop_scope(); if let Some(handler) = &ts.handler { ctx.push_scope(); - self.predeclare_block_function_declarations(&handler.body.body, ctx); + self.predeclare_block_function_declarations(&handler.body.body, ctx, if_arm); ctx.pop_scope(); } if let Some(finalizer) = &ts.finalizer { ctx.push_scope(); - self.predeclare_block_function_declarations(&finalizer.body, ctx); + self.predeclare_block_function_declarations(&finalizer.body, ctx, if_arm); ctx.pop_scope(); } } Statement::LabeledStatement(ls) => { - self.predeclare_block_function_declarations(std::slice::from_ref(&ls.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&ls.body), ctx, if_arm); } Statement::WithStatement(ws) => { - self.predeclare_block_function_declarations(std::slice::from_ref(&ws.body), ctx); + self.predeclare_block_function_declarations(std::slice::from_ref(&ws.body), ctx, if_arm); } _ => {} } } } - /// 预声明 `if` 支臂裸函数声明的块级绑定(展开标签链并下钻嵌套 `if` 后取 - /// 函数声明):仅当该名没有外层 var 绑定承载时才在当前块建块 Let,承载支臂 - /// 闭包,避免声明点覆写外层形参/词法绑定;名已有外层 var 绑定(非 eval 脚本 - /// 顶层、未被形参/词法同名抑制、且非全局只读内置三常量)时不建块级绑定, - /// 声明点物化进该 var 槽。 - /// - /// # 边界与前提 - /// - 支臂为块时返回:该块自身的块入口流程预声明其函数声明。 - /// - 支臂为嵌套 `if`(含 `else if` 链)时沿 consequent/alternate 下钻至叶子, - /// 叶子按同一门控判定——嵌套支臂与单层支臂同属 Annex B 隐式支臂块。 - /// - 支臂为其它语句时返回:其中的函数声明不属于 `if` 支臂隐式块。 - /// - 须在 `block_fn_suppressed` 已构建之后调用(函数体/程序声明实例化阶段 - /// 先于任何块 emit);`is_eval_script` 仅标记 eval 脚本顶层程序,其顶层 - /// 不建块级函数外层 var 绑定;脚本顶层的只读三常量全局属性不可配置, - /// 声明实例化不建外层 var 绑定,须保留块级绑定承载支臂闭包。 - fn predeclare_if_arm_block_function(&self, stmt: &Statement, ctx: &mut CompileCtx) { - let mut cur = stmt; - while let Statement::LabeledStatement(ls) = cur { - cur = &ls.body; - } - match cur { - Statement::FunctionDeclaration(f) => { - let Some(identifier) = &f.id else { - return; - }; - if Self::if_arm_has_outer_var_carrier(ctx, identifier.name.as_str()) { - return; - } - let reg = ctx.alloc_reg(); - let _ = ctx.declare_initialized(identifier.name.as_str(), reg, VariableDeclarationKind::Let, false); - } - Statement::IfStatement(is) => { - self.predeclare_if_arm_block_function(&is.consequent, ctx); - if let Some(alt) = &is.alternate { - self.predeclare_if_arm_block_function(alt, ctx); - } - } - _ => {} - } - } - /// `if` 支臂裸函数声明的名是否已有外层 var/函数绑定承载:非 eval 脚本顶层的 /// 非抑制名由实例化阶段建外层 var 绑定,唯一例外是脚本顶层的只读三常量 /// (全局属性不可配置,声明实例化刻意不建外层绑定),其名须退回块级绑定。 diff --git a/crates/oxide_emit/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs index 137729e9..09cd512d 100644 --- a/crates/oxide_emit/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -13,7 +13,7 @@ impl Emitter { ctx.push_scope(); // 函数预声明先于 lexical:`{ let g; function g(){} }` 时 lexical 的预声明 // 命中已存在的函数绑定自然报重复声明错,不被静默覆盖破坏 TDZ。 - self.predeclare_block_function_declarations(&block.body, ctx); + self.predeclare_block_function_declarations(&block.body, ctx, false); // 块 lexical 声明是局部绑定:不做受限全局名检查(重复声明错在 emit 期报)。 let _ = self.predeclare_lexical_declarations(&block.body, ctx, false); // 块级函数声明的块入口初始化:直接子(含标签直接子体)闭包物化入预 diff --git a/crates/oxide_vm/tests/block_fn_if_arm_var_shadow_tests.rs b/crates/oxide_vm/tests/block_fn_if_arm_var_shadow_tests.rs index 694ca619..e0b52f22 100644 --- a/crates/oxide_vm/tests/block_fn_if_arm_var_shadow_tests.rs +++ b/crates/oxide_vm/tests/block_fn_if_arm_var_shadow_tests.rs @@ -4,7 +4,8 @@ //! //! 覆盖:块内前读/typeof 前读/后读、`if(false)` 未求值支、标签包裹支臂、支臂 //! 前 `var` 重赋值、块外后读、外层词法/形参同名不覆写、嵌套 `if`/`else if` 支臂 -//! (eval 顶层与形参抑制面)、只读三常量名支臂、strict 早期错误。 +//! (eval 顶层与形参抑制面)、容器(循环/`with`/标签)内嵌套 `if` 支臂、只读三 +//! 常量名支臂、strict 早期错误。 use std::sync::Arc; @@ -134,6 +135,29 @@ fn if_arm_fn_named_nan_at_script_top_keeps_block_binding() { assert!(eval(source).unwrap().is_undefined()); } +#[test] +fn container_do_if_arm_fn_eval_top_level_keeps_block_binding() { + // 支臂为 `do` 容器、容器体内嵌套 `if` 子句函数:eval 顶层无外层 var 面承载, + // 须下钻容器到叶子并建块级绑定,否则声明点无绑定可物化而报未定义。 + let source = "eval(\"{ if(true) do if(true) function g(){} while(false); typeof g }\")"; + assert_eq!(eval_str(source).unwrap(), "function"); +} + +#[test] +fn container_while_if_arm_fn_keeps_outer_param_binding() { + // 支臂为 `while` 容器、容器体内嵌套 `if` 子句函数同名形参:门控须下钻容器 + // 到达叶子并为抑制名建块槽,支臂不覆写形参值。 + let source = "function f(g){ { if(true) while(false) if(true) function g(){} } return g } f(42)"; + assert_eq!(eval(source).unwrap().as_int(), 42); +} + +#[test] +fn container_do_if_arm_fn_keeps_outer_param_binding() { + // 同形但容器体求值一次:门控失效时闭包会写进形参槽,`return g` 变函数对象。 + let source = "function f(g){ { if(true) do if(true) function g(){} while(false) } return g } f(42)"; + assert_eq!(eval(source).unwrap().as_int(), 42); +} + #[test] fn if_arm_fn_strict_is_early_error() { // strict 代码中 if 子句内的函数声明是早期错误(Annex B 仅 sloppy)。