mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
252 lines
9.3 KiB
Rust
252 lines
9.3 KiB
Rust
//! catch 参数捕获与每入口新 cell 形态锁:catch 参数绑定名(含解构模式全名)计入
|
|
//! 本函数 own 集,被嵌套闭包捕获时 catch 入口发 MAKE_CELL_FRESH——每次 catch 执行
|
|
//! 为参数新建词法环境(替换上一入口的 cell 而非原地覆写),子模块经 upvalue 读;
|
|
//! try 体/finally 体闭包对参数的域外引用保持运行期 ReferenceError。
|
|
//!
|
|
//! 完成值一律收敛为布尔(JsValue 的 Display 只暴露 number/bool,不暴露字符串内容),
|
|
//! 字符串比较在引擎内完成。
|
|
|
|
use std::sync::Arc;
|
|
|
|
use oxide_compiler::compiler::Compiler;
|
|
use oxide_parser::Allocator;
|
|
use oxide_vm::vm::Vm;
|
|
|
|
fn eval(source: &str) -> String {
|
|
let allocator = Allocator::default();
|
|
let program = match oxide_parser::parse(&allocator, source) {
|
|
Ok(p) => p,
|
|
Err(e) => return format!("parse error: {}", e[0].message),
|
|
};
|
|
let module = match Compiler::new().compile(&program) {
|
|
Ok(m) => m,
|
|
Err(e) => return format!("compile error: {e}"),
|
|
};
|
|
let mut vm = Vm::new();
|
|
match vm.run(&Arc::new(module)) {
|
|
Ok(result) => format!("{result}"),
|
|
Err(e) => format!("vm error: {e}"),
|
|
}
|
|
}
|
|
|
|
/// 断言运行期抛出未捕获 ReferenceError(域外引用守卫的期望形态)。
|
|
fn assert_uncaught_reference_error(source: &str, msg: &str) {
|
|
let res = eval(source);
|
|
assert!(
|
|
res.starts_with("vm error") && res.contains("ReferenceError"),
|
|
"{msg}: expected an uncaught ReferenceError, got: {res}"
|
|
);
|
|
}
|
|
|
|
// ── 闭包捕获参数:直引 / 模式名 / 同名遮蔽 / 嵌套函数形 / 顶层 / 写穿 / 跨帧 ──
|
|
|
|
#[test]
|
|
fn catch_param_closure_direct_ref_reads_thrown_value() {
|
|
// 主形态:catch 体闭包直引参数,读值为抛入的异常值。
|
|
assert_eq!(
|
|
eval("(() => { let f; try { throw 1; } catch (e) { f = () => e; } return f(); })()"),
|
|
"1",
|
|
"closure in catch body must read the thrown value through the parameter cell"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_destructured_name_captured_by_closure() {
|
|
// 解构参数模式内的绑定名与裸参数名同口径:闭包捕获读属性值。
|
|
assert_eq!(
|
|
eval("(() => { let f; try { throw { message: 7 }; } catch ({ message: e }) { f = () => e; } return f(); })()"),
|
|
"7",
|
|
"destructuring catch parameter names are captured bindings too"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_shadows_outer_var_closure_reads_param() {
|
|
// 外层 var 同名遮蔽:catch 体内的闭包捕获参数(内层绑定),读抛入值而非外层 var。
|
|
assert_eq!(
|
|
eval("(() => { var e = 99; try { throw 5; } catch (e) { const f = () => e; return f(); } })()"),
|
|
"5",
|
|
"closure captures the catch parameter, not the same-named outer var"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_shadows_function_param_closure_reads_param() {
|
|
// 外层函数形参同名遮蔽:函数作用域内 catch 参数遮蔽形参,闭包读参数。
|
|
assert_eq!(
|
|
eval("(function(e) { try { throw 3; } catch (e) { const f = () => e; return f(); } })(88)"),
|
|
"3",
|
|
"closure captures the catch parameter, not the same-named outer function param"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_function_decl_inside_catch_reads_param() {
|
|
// 函数声明形(非箭头):catch 体内声明的函数引用参数,读抛入值。
|
|
assert_eq!(
|
|
eval("(() => { let f; try { throw 6; } catch (e) { function g() { return e; } f = g; } return f(); })()"),
|
|
"6",
|
|
"function declared in catch body reads the captured parameter"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_class_method_inside_catch_reads_param() {
|
|
// 类方法形:catch 体内类表达式的方法引用参数,读抛入值。
|
|
assert_eq!(
|
|
eval(
|
|
"(() => { let C; try { throw 8; } catch (e) { C = class { m() { return e; } }; } return new C().m(); })()"
|
|
),
|
|
"8",
|
|
"class method defined in catch body reads the captured parameter"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_top_level_closure_reads_param() {
|
|
// 脚本顶层 catch 参数被顶层闭包捕获:顶层 cell 层同样承载参数 cell。
|
|
assert_eq!(
|
|
eval("let f; try { throw 11; } catch (e) { f = () => e; } f()"),
|
|
"11",
|
|
"top-level catch parameter is capturable by top-level closures"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_reassign_then_closure_reads_new_value() {
|
|
// 写穿面:catch 体内重赋参数后闭包读新值(赋值须写穿共享单元)。
|
|
assert_eq!(
|
|
eval("(() => { let f; try { throw 1; } catch (e) { e = 99; f = () => e; } return f(); })()"),
|
|
"99",
|
|
"assignment to the catch parameter writes through the captured cell"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_recursive_frames_each_frame_isolated() {
|
|
// 跨帧隔离:同名参数各调用帧独立 cell,闭包读各自帧的抛入值。
|
|
assert_eq!(
|
|
eval(
|
|
"(() => { function g(n) { try { throw n; } catch (e) { return () => e; } } \
|
|
let fs = [g(1), g(2)]; return fs.map(f => f()).join('|') === '1|2'; })()"
|
|
),
|
|
"true",
|
|
"each call frame builds its own parameter cell for the closure"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_default_value_inner_fn_reads_param() {
|
|
// 参数默认值时序:模式默认值内嵌套函数引用参数,绑定先于默认值求值。
|
|
assert_eq!(
|
|
eval("(() => { let f; try { throw 4; } catch (e) { const g = (x = () => e) => x; f = g(); } return f(); })()"),
|
|
"4",
|
|
"nested function in a parameter default reads the catch parameter"
|
|
);
|
|
}
|
|
|
|
// ── 每 catch 入口新 cell(替换而非覆写)──
|
|
|
|
#[test]
|
|
fn catch_param_loop_reentry_each_iteration_new_cell() {
|
|
// 循环重入:每迭代 catch 入口新 cell,各迭代闭包读各自迭代值。
|
|
assert_eq!(
|
|
eval(
|
|
"(() => { let r = []; for (let i = 0; i < 2; i++) { try { throw i; } catch (e) { r.push(() => e); } } \
|
|
return r.map(f => f()).join('|') === '0|1'; })()"
|
|
),
|
|
"true",
|
|
"each loop iteration's catch entry builds a fresh parameter cell"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_consecutive_try_catch_each_entry_new_cell() {
|
|
// 无循环连续两个 try/catch 同名参数:第二个入口新 cell 不覆写第一个入口闭包
|
|
// 已持的 cell 指针(门控若按循环在飞判定,本形态读错值)。
|
|
assert_eq!(
|
|
eval(
|
|
"(() => { let r = []; try { throw 1; } catch (e) { r.push(() => e); } \
|
|
try { throw 2; } catch (e) { r.push(() => e); } \
|
|
return r.map(f => f()).join('|') === '1|2'; })()"
|
|
),
|
|
"true",
|
|
"consecutive catch entries with the same parameter name build fresh cells"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_nested_catch_outer_closure_keeps_old_cell() {
|
|
// 同名嵌套 catch:内层入口新 cell 替换槽位,外层闭包仍持旧 cell 指针读旧值。
|
|
assert_eq!(
|
|
eval(
|
|
"(() => { let outer; try { throw 1; } catch (e) { outer = () => e; \
|
|
try { throw 2; } catch (e) { } } return outer(); })()"
|
|
),
|
|
"1",
|
|
"inner catch entry must not clobber the outer closure's cell"
|
|
);
|
|
}
|
|
|
|
// ── 守卫:既有直读/遮蔽/B022 模式面语义保真 ──
|
|
|
|
#[test]
|
|
fn catch_param_direct_read_in_body() {
|
|
// 无闭包直读参数(最简形态):直读保真。
|
|
assert_eq!(
|
|
eval("try { throw 42; } catch (e) { e }"),
|
|
"42",
|
|
"direct read of the catch parameter in its own body"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_shadows_outer_var_direct_read() {
|
|
// var 同名遮蔽直读(无闭包):catch 体内直读参数值。
|
|
assert_eq!(
|
|
eval("(() => { var e = 99; try { throw 5; } catch (e) { return e; } })()"),
|
|
"5",
|
|
"direct read in catch body sees the parameter, not the outer var"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_destructured_direct_read() {
|
|
// 解构参数直读(无闭包):模式绑定求值后直读求和。
|
|
assert_eq!(
|
|
eval("(() => { try { throw [1, 2]; } catch ([a, b]) { return a + b; } })()"),
|
|
"3",
|
|
"destructuring catch parameter direct read"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_pattern_default_value_evaluated() {
|
|
// 模式默认值面:抛入值非 undefined 时默认值不求值,直读抛入元素。
|
|
assert_eq!(
|
|
eval("(() => { const g = () => 55; try { throw [1]; } catch ([x = g()]) { return x; } })()"),
|
|
"1",
|
|
"pattern default value is not evaluated when the thrown element is present"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_ref_from_try_body_stays_reference_error() {
|
|
// 域外引用:try 体闭包引用 catch 参数——参数在 try 体域内未声明,
|
|
// 修复后仍须运行期 ReferenceError(与 node 一致)。
|
|
assert_uncaught_reference_error(
|
|
"(() => { let f; try { f = () => e; throw 1; } catch (e) {} return f(); })()",
|
|
"closure in try body referencing the catch parameter must stay unresolvable",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn catch_param_ref_from_finally_body_stays_reference_error() {
|
|
// 域外引用:finally 体闭包引用 catch 参数——参数作用域不含 finally 体,
|
|
// 修复后仍须运行期 ReferenceError(与 node 一致)。
|
|
assert_uncaught_reference_error(
|
|
"(() => { let f; try { throw 1; } catch (e) {} finally { f = () => e; } return f(); })()",
|
|
"closure in finally body referencing the catch parameter must stay unresolvable",
|
|
);
|
|
}
|