diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index 88d4bec..fd2a0c5 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -405,6 +405,87 @@ impl Emitter { } } + /// 预声明当前块直接子树中的函数声明(ES2015 块级绑定,kind=Let 落当前块), + /// 使块内声明点之前的引用在编译期可见。递归进入嵌套块时压/弹作用域, + /// 与 emit 阶段嵌套块 push_scope 的时机对齐。 + /// + /// # 边界与前提 + /// - 只处理块内函数声明;switch 不推 scope(其 case 内函数声明随 switch + /// 作用域修复一并处理);export 声明不可能出现在块内。 + /// - 与 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) { + for statement in statements { + match statement { + Statement::FunctionDeclaration(f) => { + let Some(identifier) = &f.id else { + 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); + ctx.pop_scope(); + } + Statement::IfStatement(is) => { + self.predeclare_block_function_declarations(std::slice::from_ref(&is.consequent), ctx); + if let Some(alt) = &is.alternate { + self.predeclare_block_function_declarations(std::slice::from_ref(alt), ctx); + } + } + Statement::WhileStatement(wh) => { + self.predeclare_block_function_declarations(std::slice::from_ref(&wh.body), ctx); + } + Statement::DoWhileStatement(dw) => { + self.predeclare_block_function_declarations(std::slice::from_ref(&dw.body), ctx); + } + Statement::ForStatement(fs) => { + // 循环推独立作用域(头声明所在),函数声明落该作用域与 emit 对齐。 + ctx.push_scope(); + self.predeclare_block_function_declarations(std::slice::from_ref(&fs.body), ctx); + ctx.pop_scope(); + } + Statement::ForInStatement(fi) => { + ctx.push_scope(); + self.predeclare_block_function_declarations(std::slice::from_ref(&fi.body), ctx); + ctx.pop_scope(); + } + Statement::ForOfStatement(fo) => { + ctx.push_scope(); + self.predeclare_block_function_declarations(std::slice::from_ref(&fo.body), ctx); + ctx.pop_scope(); + } + Statement::TryStatement(ts) => { + ctx.push_scope(); + self.predeclare_block_function_declarations(&ts.block.body, ctx); + ctx.pop_scope(); + if let Some(handler) = &ts.handler { + ctx.push_scope(); + self.predeclare_block_function_declarations(&handler.body.body, ctx); + ctx.pop_scope(); + } + if let Some(finalizer) = &ts.finalizer { + ctx.push_scope(); + self.predeclare_block_function_declarations(&finalizer.body, ctx); + ctx.pop_scope(); + } + } + Statement::LabeledStatement(ls) => { + self.predeclare_block_function_declarations(std::slice::from_ref(&ls.body), ctx); + } + Statement::WithStatement(ws) => { + self.predeclare_block_function_declarations(std::slice::from_ref(&ws.body), ctx); + } + _ => {} + } + } + } + /// 预声明当前作用域直接子语句中的 `let`/`const`/`class` 绑定(未初始化, /// 建立 TDZ 占位)。使声明点之前的读取在编译期可分辨为 TDZ 而非隐式全局, /// 声明点复用预登记槽位。 diff --git a/crates/oxide_emit/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs index a6e1e3a..3d227c8 100644 --- a/crates/oxide_emit/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -9,6 +9,9 @@ impl Emitter { return Ok(None); }; ctx.push_scope(); + // 函数预声明先于 lexical:`{ let g; function g(){} }` 时 lexical 的预声明 + // 命中已存在的函数绑定自然报重复声明错,不被静默覆盖破坏 TDZ。 + self.predeclare_block_function_declarations(&block.body, ctx); self.predeclare_lexical_declarations(&block.body, ctx); let mut r = None; for s in &block.body { diff --git a/crates/oxide_vm/tests/block_function_decl_tests.rs b/crates/oxide_vm/tests/block_function_decl_tests.rs new file mode 100644 index 0000000..2e76020 --- /dev/null +++ b/crates/oxide_vm/tests/block_function_decl_tests.rs @@ -0,0 +1,82 @@ +//! 块内函数声明:块级绑定预声明后,声明点前后引用均可编译并执行。 +//! +//! 覆盖:普通块内函数、块内后续调用、提前调用(阶段 1 现状)、与 let 同名冲突、 +//! 捕获路径、嵌套块作用域隔离、重复声明(Annex B 最后生效)、单语句块体。 + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?; + let mut vm = Vm::new(); + vm.run(&module) +} + +#[test] +fn block_function_decl_compiles_and_runs() { + // 块内函数声明可编译执行,无返回值。 + let result = eval("{ function g(){} }").unwrap(); + assert!(result.is_undefined()); +} + +#[test] +fn block_function_decl_call_after_decl() { + // 块内声明点之后调用:正常返回函数值。 + let result = eval("{ function g(){return 1;} g(); }").unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn block_function_decl_hoisted_call_before_decl_is_not_callable() { + // 阶段 1:声明点之前调用读到未初始化槽 undefined,报 not callable + // (比修复前的编译错好;完整 hoisting 属后续阶段)。 + let err = eval("{ g(); function g(){return 1;} }").unwrap_err(); + assert!(err.contains("not callable") || err.contains("not an object"), "got: {err}"); +} + +#[test] +fn block_function_decl_conflicts_with_let() { + // 块内函数与块内 let 同名:早期错误(重复声明),顺序无关。 + let err = eval("{ let g; function g(){} }").unwrap_err(); + assert!(err.contains("already been declared"), "got: {err}"); + let err = eval("{ function g(){} let g; }").unwrap_err(); + assert!(err.contains("already been declared"), "got: {err}"); +} + +#[test] +fn block_function_decl_capture_escape() { + // 块内函数被外层变量引用(捕获路径 MAKE_CELL),块外可调用。 + let result = eval("var f; { function g(){return 1;} f = g; } f()").unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn block_function_decl_scope_isolated() { + // 嵌套块内函数是块级绑定:块外不可见(typeof 未声明标识符返回 undefined)。 + let result = eval("{ { function g(){return 2;} } } typeof g === 'undefined'").unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn block_function_decl_duplicate_last_wins() { + // 重复块内函数声明(sloppy):最后声明生效(Annex B 语义)。 + let result = eval("{ function g(){} function g(){return 3;} g(); }").unwrap(); + assert_eq!(result.as_int(), 3); +} + +#[test] +fn block_function_decl_in_if_body() { + // 单语句块体(if consequent 为块):递归预声明生效。 + let result = eval("if (true) { function g(){return 8;} g(); }").unwrap(); + assert_eq!(result.as_int(), 8); +} + +#[test] +fn block_function_decl_nested_outer_var_capture() { + // 嵌套块内函数引用外层变量,赋值逃逸后取值正确。 + let result = eval("var r; { { function g(){return 4;} r = g(); } } r").unwrap(); + assert_eq!(result.as_int(), 4); +}