From 50d05bd4a0fa33f3b973d055cb6026b98f79645c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 16 Oct 2026 18:41:55 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9D=97=E5=87=BD=E6=95=B0=E5=90=8D?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=8F=AF=E5=86=99=E5=8C=96=E5=B9=B6=E6=94=B6?= =?UTF-8?q?=E7=AA=84=20catch=20=E5=8F=82=E6=95=B0=E6=8A=91=E5=88=B6?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 42 +++++++++---------- Cargo.toml | 2 +- crates/oxide_emit/src/capture/names.rs | 32 ++++++++------ crates/oxide_emit/src/class/emit.rs | 1 + crates/oxide_emit/src/class/method.rs | 1 + crates/oxide_emit/src/compile_ctx.rs | 5 +++ crates/oxide_emit/src/expr/function/mod.rs | 2 + crates/oxide_emit/src/function_body.rs | 14 ++++++- crates/oxide_emit/src/program.rs | 4 ++ crates/oxide_emit/src/stmt/block.rs | 2 +- .../src/stmt/declaration/function_decl.rs | 29 ++++++------- crates/oxide_vm/tests/switch_case_fn_tests.rs | 8 ++-- 12 files changed, 88 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31927dad..9251e5a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.19.36" +version = "0.19.37" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.19.36" +version = "0.19.37" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.19.36" +version = "0.19.37" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.19.36" +version = "0.19.37" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.19.36" +version = "0.19.37" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.19.36" +version = "0.19.37" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.19.36" +version = "0.19.37" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.19.36" +version = "0.19.37" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.19.36" +version = "0.19.37" [[package]] name = "oxide_parser" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.19.36" +version = "0.19.37" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.19.36" +version = "0.19.37" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.19.36" +version = "0.19.37" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.19.36" +version = "0.19.37" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index de070c07..5b7ea1a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.19.36" +version = "0.19.37" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/capture/names.rs b/crates/oxide_emit/src/capture/names.rs index e419fc74..eabaee25 100644 --- a/crates/oxide_emit/src/capture/names.rs +++ b/crates/oxide_emit/src/capture/names.rs @@ -44,8 +44,8 @@ pub(crate) struct FnScopeNames { /// 块级普通函数声明名(源序去重;直接子级是提升 var 声明不纳,async/生成器 /// 是块级词法绑定不纳,不递归嵌套函数体)。 pub(crate) block_function_names: Vec, - /// 块级函数名的外层 var 绑定抑制集(形参名 ∪ 树内词法声明名;命中抑制的名字 - /// 不建外层 var 绑定、不求值写回)。 + /// 块级函数名的外层 var 绑定抑制集(形参名 ∪ 树内词法声明名,绑定标识符 catch + /// 参数除外;命中抑制的名字不建外层 var 绑定、不求值写回)。 pub(crate) block_fn_suppressed: HashSet, } @@ -112,6 +112,8 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: } } // 抑制面:词法名与块函数名同名时退化为纯块作用域(各层均收集)。 + // 规范口径:var 声明与函数体任一层词法声明同名即产生早期错误 + // (LexicallyDeclaredNames 递归收集嵌套块),故嵌套 let/const 亦入集。 if !is_var { for d in &vd.declarations { collect_binding_pattern_names(&d.id, &mut out.block_fn_suppressed); @@ -141,7 +143,7 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: let name = id.name.to_string(); // own 面:类名是本作用域 const 绑定。 out.own_binding_names.insert(name.clone()); - // 抑制面:类名是词法声明,与块函数名同名时退化纯块作用域。 + // 抑制面:类名是词法声明,与块函数名同名时退化纯块作用域(各层均收集)。 out.block_fn_suppressed.insert(name.clone()); if top { // 直接子词法面:顶层类是顶层词法绑定。 @@ -269,7 +271,7 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_binding_pattern_names(&d.id, &mut out.block_level_decl_names); } } - // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域(各层均收集)。 if !is_var { for d in &vd.declarations { collect_binding_pattern_names(&d.id, &mut out.block_fn_suppressed); @@ -288,7 +290,7 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_for_left_decl_names(&fi.left, &mut out.own_binding_names); if !is_var { collect_for_left_decl_names(&fi.left, &mut out.block_level_decl_names); - // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域(各层均收集)。 collect_for_left_decl_names(&fi.left, &mut out.block_fn_suppressed); } } @@ -303,7 +305,7 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: collect_for_left_decl_names(&fo.left, &mut out.own_binding_names); if !is_var { collect_for_left_decl_names(&fo.left, &mut out.block_level_decl_names); - // 抑制面:for 头词法名与块函数名同名时退化纯块作用域。 + // 抑制面:for 头词法名与块函数名同名时退化纯块作用域(各层均收集)。 collect_for_left_decl_names(&fo.left, &mut out.block_fn_suppressed); } } @@ -319,11 +321,16 @@ fn collect_fn_scope_names_impl(stmts: &[Statement], out: &mut FnScopeNames, top: // 与旧收集器逐位一致(各集合面不受访问序影响)。 collect_fn_scope_names_impl(&ts.block.body, out, false); if let Some(h) = &ts.handler { - // catch 参数是本作用域的词法绑定:own、块级与抑制三面均计入。 + // catch 参数是本作用域的词法绑定:own 与块级两面的计入。 + // 抑制面:仅解构 catch 参数(BindingPattern)计入。绑定标识符 + // catch 参数(CatchParameter:BindingIdentifier)按 B.3.5 例外条款 + // 豁免 var 碰撞早期错误,同名块函数仍提升全局,不入抑制集。 if let Some(param) = &h.param { collect_binding_pattern_names(¶m.pattern, &mut out.own_binding_names); collect_binding_pattern_names(¶m.pattern, &mut out.block_level_decl_names); - collect_binding_pattern_names(¶m.pattern, &mut out.block_fn_suppressed); + if !matches!(param.pattern, oxide_parser::BindingPattern::BindingIdentifier(_)) { + collect_binding_pattern_names(¶m.pattern, &mut out.block_fn_suppressed); + } } collect_fn_scope_names_impl(&h.body.body, out, false); } @@ -656,7 +663,7 @@ mod tests { &[], &[], &[], - &["b", "e"], + &["b"], ); assert_scope_names( "try { var a; } catch { let b; }", @@ -795,7 +802,7 @@ mod tests { &[], &[], &["bf6", "bf7", "bf8"], - &["e"], + &[], ); assert_scope_names( "L2: { function bf9() {} } with (o) { function bf10() {} }", @@ -891,7 +898,8 @@ mod tests { &["s"], &["s"], ); - // catch 参数与块函数名同名:catch 体外的 if 臂块函数被抑制。 + // catch 参数与块函数名同名:绑定标识符 catch 参数按 B.3.5 例外条款豁免 var + // 碰撞早期错误,不入抑制集(同名块函数仍提升全局)。 assert_scope_names( "try {} catch (e) {} if (x) { function e() {} }", &[], @@ -902,7 +910,7 @@ mod tests { &[], &[], &["e"], - &["e"], + &[], ); // 形参名进抑制面:块函数名撞形参名退化纯块作用域(形参名同时注入 own 面)。 assert_scope_names( diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index a93b41d2..1c30cf6c 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -278,6 +278,7 @@ impl Emitter { is_async: false, own_strict: true, name: None, + is_block_function: false, }; let ctor_hooks = ClassElementHooks { extra_bindings: &self_binding, diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index 21d14b75..9e75326b 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -140,6 +140,7 @@ impl Emitter { is_async: method_value.r#async, own_strict: true, name: None, + is_block_function: false, }, hooks, )?; diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index cf5f3dc8..5f8a7265 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -63,6 +63,10 @@ pub struct CompileCtx { /// `is_eval_script`——该标志只标识当前顶层程序,且还控制顶层全局写点与块级 /// 函数门控,整体传播语义过宽。 pub(crate) is_eval_origin: bool, + /// 是否处于首个 sub-pass(提升函数声明发射期):声明点据此区分顶层函数 + /// 声明(名绑定恒不可写)与块级函数声明(Annex B.3.3 块绑定为可变 var 绑定)。 + /// 仅程序/函数体发射入口在提升 sub-pass 内置位,不跨嵌套函数继承。 + pub(crate) in_hoisting_subpass: bool, /// 源码是否为 `source_escape` 产物(动态编译入口):正则字面量源文本切片 /// 的池键形态据此选择(见 [`Emitter::source_encoded`])。 pub(crate) source_encoded: bool, @@ -285,6 +289,7 @@ impl CompileCtx { repl_persist: false, is_eval_script: false, is_eval_origin: false, + in_hoisting_subpass: false, source_encoded: false, static_block_this_reg: None, field_buffer: None, diff --git a/crates/oxide_emit/src/expr/function/mod.rs b/crates/oxide_emit/src/expr/function/mod.rs index de9c8ac1..c52ee7ca 100644 --- a/crates/oxide_emit/src/expr/function/mod.rs +++ b/crates/oxide_emit/src/expr/function/mod.rs @@ -45,6 +45,7 @@ impl Emitter { is_async: arrow.r#async, own_strict: arrow.has_use_strict_directive(), name: None, + is_block_function: false, }; let mut sub_module = self.compile_simple_function_body(¶m_names, body_stmts, ctx, FunctionBodyContext::Arrow, flags)?; @@ -100,6 +101,7 @@ impl Emitter { is_async: fe.r#async, own_strict, name: fn_name, + is_block_function: false, }; let mut sub_module = self.compile_simple_function_body(¶m_names, body_stmts, ctx, FunctionBodyContext::Ordinary, flags)?; diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index 1edb112e..596da12c 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -73,6 +73,9 @@ pub struct FunctionFlags<'a> { pub own_strict: bool, /// 函数名(具名函数声明/表达式传名,箭头与匿名传 `None`),用于函数名不可写绑定登记。 pub name: Option<&'a str>, + /// 块级普通函数声明(Annex B.3.3):函数名绑定为可写 var 绑定,体内容器内 + /// `f = 123` 可覆写块绑定(非块级函数声明的函数名绑定恒不可写)。 + pub is_block_function: bool, } /// 类元素钩子:类元素函数(构造器/方法/字段)的额外绑定、字段发射与捕获组, @@ -353,6 +356,7 @@ impl Emitter { hooks.extra_upvalue_names, flags.name, &scope_names, + flags.is_block_function, )?; // 实例字段 computed key 数组所在 upvalue 下标,供字段初始化 emit 定位。 ctx.field_keys_uv = ctx @@ -574,6 +578,7 @@ impl Emitter { &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, ctx: &mut CompileCtx, body_context: FunctionBodyContext, extra_capture_exprs: &[&'a Expression<'a>], extra_upvalue_names: &[(String, u8)], name: Option<&str>, scope_names: &FnScopeNames, + is_block_function: bool, ) -> Result { ctx.push_scope_with_kind(ScopeKind::FunctionScope); let param_base = ctx.next_reg; @@ -633,6 +638,9 @@ impl Emitter { // 绑定,帧槽写入把它初始化为函数对象。抑制面(形参同名 / 体内容器 var // 同名 / arguments 对象占位)不建名绑定:前两者名解析到参数/var 绑定, // 后者 arguments 绑定占位(var 同名时由帧槽写入初始化为函数对象)。 + // 块级普通函数声明(Annex B.3.3)的块绑定是可变 var 绑定:体内容器内 + // `f = 123` 可覆写块绑定(非块级函数声明的函数名绑定恒不可写);strict + // 函数名绑定恒不可写(strict 块函数名绑定 eval 自身词法环境,同不可写)。 // 名登记须先于 set_captured_bindings:嵌套函数捕获名分析依赖 own_bindings // 完整(名被捕获时须识别为本函数绑定并建 cell)。 if let Some(fn_name) = name { @@ -651,7 +659,7 @@ impl Emitter { lexical: false, predeclared: false, cell_idx: None, - non_writable: true, + non_writable: ctx.is_strict || !is_block_function, }, ); ctx.own_bindings.insert(fn_name.to_string()); @@ -1088,6 +1096,9 @@ impl Emitter { /// 入口物化闭包,次 sub-pass 跳过声明语句本身,声明点不重编以保函数对象同一性。 fn emit_body_stmts(&self, body_stmts: &[Statement], ctx: &mut CompileCtx) -> Result, String> { let mut last_result_reg = None; + // 提升 sub-pass 标志置位:声明点据此区分顶层函数声明(名绑定恒不可写) + // 与块级函数声明(Annex B.3.3 块绑定为可变 var 绑定)。 + ctx.in_hoisting_subpass = true; for stmt in body_stmts { if Self::is_body_hoisted_function_decl(stmt) { if let Some(reg) = self.emit_statement(stmt, ctx)? { @@ -1095,6 +1106,7 @@ impl Emitter { } } } + ctx.in_hoisting_subpass = false; for stmt in body_stmts { if Self::is_body_hoisted_function_decl(stmt) { continue; diff --git a/crates/oxide_emit/src/program.rs b/crates/oxide_emit/src/program.rs index 665a97e0..6d06e72e 100644 --- a/crates/oxide_emit/src/program.rs +++ b/crates/oxide_emit/src/program.rs @@ -448,11 +448,15 @@ impl Emitter { } // 首个 sub-pass:发函数声明(hoisting),保证任何代码运行前函数对象已就绪。 + // 提升 sub-pass 标志置位:声明点据此区分顶层函数声明(名绑定恒不可写) + // 与块级函数声明(Annex B.3.3 块绑定为可变 var 绑定)。 + ctx.in_hoisting_subpass = true; for stmt in &program.body { if matches!(stmt, Statement::FunctionDeclaration(_)) { self.emit_statement(stmt, &mut ctx)?; } } + ctx.in_hoisting_subpass = false; // 第二个 sub-pass:发其余所有语句。 let mut last_result: Option = None; diff --git a/crates/oxide_emit/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs index f641593c..d1d2f8de 100644 --- a/crates/oxide_emit/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -77,7 +77,7 @@ impl Emitter { return Ok(()); }; let name = id.name.to_string(); - self.materialize_function_declaration(fd, &name, ctx)?; + self.materialize_function_declaration(fd, &name, ctx, true)?; // 登记声明节点 arena 地址(Box 解引到 arena 本体):声明点 // 以同一节点地址判同一,区分本声明入口物化与同名支臂。 ctx.block_fn_entry_mats diff --git a/crates/oxide_emit/src/stmt/declaration/function_decl.rs b/crates/oxide_emit/src/stmt/declaration/function_decl.rs index e8223483..a53dafee 100644 --- a/crates/oxide_emit/src/stmt/declaration/function_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/function_decl.rs @@ -1,6 +1,5 @@ //! 函数声明语句 emit:`emit_function_declaration_statement` 创建闭包并绑定函数名。 -use crate::symbol_table::ScopeKind; use crate::{CompileCtx, Emitter, FunctionBodyContext, FunctionFlags, ParamSpec}; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; @@ -34,20 +33,20 @@ impl Emitter { // 闭包落 fresh 寄存器写回外层 var; // - 其余(支臂首次求值):执行期自物化闭包写入块槽。 // 非块路径(顶层/函数作用域声明点)恒自物化。 - let binding_scope_kind = ctx - .scopes - .symbols - .lookup_any_binding(&name) - .map(|(_, scope_idx)| ctx.scopes.symbols.scopes[scope_idx].kind); let node = fd as *const _ as *const (); - let var_reg = if binding_scope_kind == Some(ScopeKind::BlockScope) { + // 块级普通函数声明(Annex B.3.3):块槽 Let 绑定承载块函数,函数名绑定 + // 为可变 var 绑定(体内容器内 `f = 123` 可覆写块绑定);非块路径恒不可写。 + // 判定依据提升 sub-pass 标志:首个 sub-pass 发顶层函数声明(恒不可写), + // 其余 sub-pass 发的函数声明皆为块级(if/while/for/switch/try 支臂与块直接子)。 + let is_block_function = !ctx.in_hoisting_subpass; + let var_reg = if is_block_function { match ctx.block_fn_entry_mats.last().and_then(|m| m.get(&name)) { Some(&m) if m == node => ctx.lookup(&name)?, - Some(_) => self.materialize_function_declaration_var_only(fd, &name, ctx)?, - None => self.materialize_function_declaration(fd, &name, ctx)?, + Some(_) => self.materialize_function_declaration_var_only(fd, &name, ctx, true)?, + None => self.materialize_function_declaration(fd, &name, ctx, true)?, } } else { - self.materialize_function_declaration(fd, &name, ctx)? + self.materialize_function_declaration(fd, &name, ctx, false)? }; // sloppy 模式下按 Annex B.3.3 为块级普通函数声明建外层 var 绑定并写回: // 预声明期为该名实例化的函数作用域 var 绑定在此写入块槽位里的函数对象, @@ -110,11 +109,11 @@ impl Emitter { /// - 闭包的 upvalue/cell 捕获在函数入口的捕获分析期固定,本调用点的时序 /// 只决定 CREATE_CLOSURE 在指令流中的落点。 pub(crate) fn materialize_function_declaration( - &self, fd: &oxide_parser::Function, name: &str, ctx: &mut CompileCtx, + &self, fd: &oxide_parser::Function, name: &str, ctx: &mut CompileCtx, is_block_function: bool, ) -> Result { let var_reg = ctx.lookup(name)?; ctx.reserve_reg(var_reg); - self.materialize_function_closure(fd, name, var_reg, ctx, true)?; + self.materialize_function_closure(fd, name, var_reg, ctx, true, is_block_function)?; Ok(var_reg) } @@ -122,15 +121,16 @@ impl Emitter { /// 直接子占用块槽,嵌套捕获读槽 cell 与 V8 块绑定一致)。调用点经返回 /// 寄存器把闭包写回外层 var 绑定。 pub(crate) fn materialize_function_declaration_var_only( - &self, fd: &oxide_parser::Function, name: &str, ctx: &mut CompileCtx, + &self, fd: &oxide_parser::Function, name: &str, ctx: &mut CompileCtx, is_block_function: bool, ) -> Result { let reg = ctx.alloc_reg(); - self.materialize_function_closure(fd, name, reg, ctx, false)?; + self.materialize_function_closure(fd, name, reg, ctx, false, is_block_function)?; Ok(reg) } fn materialize_function_closure( &self, fd: &oxide_parser::Function, name: &str, reg: u32, ctx: &mut CompileCtx, write_slot: bool, + is_block_function: bool, ) -> Result<(), String> { let mut param_names = Vec::new(); for (idx, param) in fd.params.items.iter().enumerate() { @@ -157,6 +157,7 @@ impl Emitter { is_async: fd.r#async, own_strict, name: Some(name), + is_block_function, }; let mut sub_module = self.compile_simple_function_body(¶m_names, body_stmts, ctx, FunctionBodyContext::Ordinary, flags)?; diff --git a/crates/oxide_vm/tests/switch_case_fn_tests.rs b/crates/oxide_vm/tests/switch_case_fn_tests.rs index 20f82e04..130e5915 100644 --- a/crates/oxide_vm/tests/switch_case_fn_tests.rs +++ b/crates/oxide_vm/tests/switch_case_fn_tests.rs @@ -132,13 +132,13 @@ fn switch_case_function_captured_by_outer() { #[test] fn switch_case_function_binding_independent_of_outer_var() { - // 函数名不可写绑定(15.2.10.1 步 m):体内容器对 f 重新赋值静默失败(sloppy), - // 块内 f 仍为函数对象;外层 var 绑定独立,不被改写。 + // 块级函数(Annex B.3.3)块绑定为可变 var 绑定:体内容器 `f = 123` 覆写块绑定, + // currentBV 取 123;外层 var 绑定(globalThis.f)独立,仍为函数对象。 let src = "var r1, r2; (function(){ var initialBV, currentBV; \ switch(1){case 1: function f(){ initialBV=f; f=123; currentBV=f; return 'decl'; }} \ var varBinding=f; f(); r1 = initialBV() === 'decl'; \ - r2 = currentBV === f && varBinding() === 'decl'; }()); r1 && r2"; - assert!(eval(src).unwrap().as_bool(), "函数名不可写绑定自引用重赋静默失败,外层 var 绑定独立"); + r2 = currentBV === 123 && varBinding() === 'decl'; }()); r1 && r2"; + assert!(eval(src).unwrap().as_bool(), "块函数块绑定可变覆写,外层 var 绑定独立"); } #[test]