mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 块级函数声明支臂不再块入口物化,声明点按入口物化状态区分写回
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
//! 块内函数声明:块级绑定预声明后,声明点前后引用均可编译并执行。
|
||||
//!
|
||||
//! 覆盖:普通块内函数、块内后续调用、提前调用(阶段 1 现状)、与 let 同名冲突、
|
||||
//! 捕获路径、嵌套块作用域隔离、重复声明(Annex B 最后生效)、单语句块体。
|
||||
//! 捕获路径、嵌套块作用域隔离、重复声明(Annex B 最后生效)、单语句块体、
|
||||
//! if 支臂声明(块入口不物化、仅被求值支执行期生效、同名直接子的双绑定面)、
|
||||
//! 标签直接子(块入口可见、外层泄漏)。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -83,3 +85,88 @@ 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_if_arm_decl_no_entry_init() {
|
||||
// 支臂声明不做块入口初始化:声明点前读命中未初始化的块槽,typeof 为
|
||||
// undefined 而非 function(与直接子声明的块入口可见性区分)。
|
||||
let result = eval("{ var t = typeof g; if (true) function g(){return 1}; t }").unwrap();
|
||||
assert!(
|
||||
result.is_string() && unsafe { &*result.as_string_ptr() }.as_str() == "undefined",
|
||||
"支臂声明前读应为 undefined,得 {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_if_arm_decl_runs_on_taken_branch() {
|
||||
// 仅被求值支的声明生效:true 支物化 return 1 的闭包,else 支不求值。
|
||||
let result = eval("{ if (true) function g(){return 1} else function g(){return 2}; g() }").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_if_arm_decl_leaks_outer_from_taken_branch() {
|
||||
// 被求值支的声明经外层 var 写回泄漏块外:块后读见该支的闭包。
|
||||
let result = eval("{ if (true) function g(){return 1} else function g(){return 2} } g()").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_if_arm_call_before_decl_throws() {
|
||||
// 声明点前调用:块槽未物化,调用未初始化值抛错(入口不预物化支臂)。
|
||||
let err = eval("{ g(); if (true) function g(){return 1} }").unwrap_err();
|
||||
assert!(err.contains("not callable"), "got: {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_arm_same_name_as_direct_child_keeps_block_binding() {
|
||||
// 直接子 + 支臂同名:支臂声明只更新外层 var 绑定、块绑定不动——块内读
|
||||
// 恒见直接子的闭包(V8 双绑定面在引擎块槽侧的投影)。
|
||||
let result = eval("{ function g(){return 1}; if (true) function g(){return 2}; g() }").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_arm_same_name_as_direct_child_updates_outer_var() {
|
||||
// 同名形块外读:支臂声明的外层 var 写回生效,块后读见支臂闭包。
|
||||
let result = eval("{ function g(){return 1}; if (true) function g(){return 2} } g()").unwrap();
|
||||
assert_eq!(result.as_int(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_read_between_direct_child_and_arm() {
|
||||
// 直接子声明与 if 之间的读:支臂尚未求值,读命中直接子的块入口闭包。
|
||||
let result = eval("{ function g(){return 1}; var r = g(); if (true) function g(){return 2}; r }").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_if_arm_decl_readable_after_decl() {
|
||||
// 块内声明点后读:被求值支的闭包已物化入块槽,可正常调用。
|
||||
let result = eval("{ if (true) function g(){return 1}; g() }").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_else_arm_decl_evaluated() {
|
||||
// else 支被求值:生效的是 else 支的闭包(执行期物化,与源序末位无关)。
|
||||
let result = eval("{ if (false) function g(){return 1} else function g(){return 2}; g() }").unwrap();
|
||||
assert_eq!(result.as_int(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_labeled_direct_body_entry_visible() {
|
||||
// 标签直接子体声明保持块入口可见:声明点前读命中入口物化的闭包。
|
||||
let result = eval("{ var t = typeof g; l: function g(){return 1}; t }").unwrap();
|
||||
assert!(
|
||||
result.is_string() && unsafe { &*result.as_string_ptr() }.as_str() == "function",
|
||||
"标签声明前读应为 function,得 {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_fn_labeled_direct_body_leaks_outer() {
|
||||
// 标签直接子体声明经外层 var 写回泄漏块外:块后读可调用。
|
||||
let result = eval("{ l: function g(){return 1} } g()").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user