Files
OxideJS-New/crates/oxide_vm/tests/tdz_upvalue_capture_tests.rs

332 lines
12 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 被捕获词法绑定(let/const)的 TDZ 语义钉:入口 TDZ 占位 cell 与赋值引用形
//! 静态守卫跳过的引擎侧行为锁。顶层/函数作用域的词法绑定被嵌套函数捕获后,
//! 声明语句执行前的读写须经运行时 cell 的未初始化标志抛 ReferenceError,
//! 声明语句执行后读写正常;const 捕获绑定的写点抛 TypeError。
//!
//! 完成值一律收敛为布尔(JsValue 的 Display 不暴露字符串内容),字符串比较在
//! 引擎内完成。
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
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();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
}
}
// ── 顶层 let 被函数声明捕获:声明后赋值引用形(编译序判别面)──
#[test]
fn captured_top_let_update_after_decl_returns_new_value() {
// 函数声明先于 let 声明语句编译,捕获 cell 在声明语句执行后才翻转已初始化,
// 赋值引用形不得误报编译期 TDZ。
assert_eq!(
eval("let step; function r() { step++; return step; } step = 0; r() === 1"),
"true",
"captured top-level let update works after the declaration executes"
);
}
#[test]
fn captured_top_let_simple_assign_after_decl() {
assert_eq!(
eval("let step; function r() { step = 7; return step; } step = 0; r() === 7"),
"true",
"captured top-level let simple assignment works after the declaration executes"
);
}
#[test]
fn captured_top_let_compound_assign_after_decl() {
assert_eq!(
eval("let step; function r() { step += 5; return step; } step = 1; r() === 6"),
"true",
"captured top-level let compound assignment works after the declaration executes"
);
}
#[test]
fn captured_top_let_bare_read_after_decl() {
// 裸读不经静态守卫,守卫跳过面不得波及裸读路径。
assert_eq!(
eval("let step; function r() { return step; } step = 0; r() === 0"),
"true",
"captured top-level let bare read works after the declaration executes"
);
}
#[test]
fn captured_top_let_update_in_stringify_replacer() {
// builtin 回调深度不影响判别:赋值引用形只由编译序决定。
assert_eq!(
eval(
"let step; function r() { step++; return step; } step = 0; JSON.stringify({ a: 1 }, (k, v) => { r(); return v; }); step === 2"
),
"true",
"captured top-level let update works from a stringify replacer"
);
}
#[test]
fn captured_top_let_update_in_map_callback() {
assert_eq!(
eval("let step; function r() { step++; return step; } step = 0; [1, 2].map(() => r()).join(',') === '1,2'"),
"true",
"captured top-level let update works from an array map callback"
);
}
#[test]
fn captured_top_let_update_in_stringify_replacer_expr() {
assert_eq!(
eval("let step; function r() { step++; return step; } step = 0; JSON.stringify({ a: 1 }, (k, v) => r() || v); step === 1"),
"true",
"captured top-level let update works when the replacer is a bare call expression"
);
}
#[test]
fn captured_top_let_update_in_getter() {
assert_eq!(
eval("let step; function r() { step++; return step; } step = 0; Object.values({ get a() { return r(); } }).join(',') === '1'"),
"true",
"captured top-level let update works from an object getter"
);
}
#[test]
fn captured_top_let_update_in_nested_arrow() {
// 链式捕获(内层箭头的 upvalue 指向外层闭包的 upvalue cell)。
assert_eq!(
eval("let step; function outer() { return () => { step++; return step; }; } step = 0; outer()() === 1"),
"true",
"chained capture of a top-level let update works in a nested arrow"
);
}
#[test]
fn captured_top_let_simple_assign_in_stringify_setter() {
assert_eq!(
eval(
"let step; function setter() { step = 5; } function reader() { return step; } step = 0; JSON.stringify({}, (k, v) => { setter(); return reader(); }); step === 5"
),
"true",
"captured top-level let simple assignment works from a stringify setter"
);
}
#[test]
fn captured_top_let_update_in_named_replacer() {
assert_eq!(
eval(
"let step; function replacer(x, k, v) { step++; return x; } step = 0; JSON.stringify({}, (k, v) => replacer(2, k, v)); step === 1"
),
"true",
"captured top-level let update works through an indirect named replacer"
);
}
#[test]
fn captured_top_let_with_init_bare_read() {
assert_eq!(
eval("let step = 3; function r() { return step; } r() === 3"),
"true",
"captured top-level let with initializer reads through the closure"
);
}
#[test]
fn function_decl_before_let_bare_read() {
// 函数声明在 let 之前(源序前),裸读经运行时 cell,声明后正常。
assert_eq!(
eval("function r() { return step; } let step; step = 0; r() === 0"),
"true",
"function declared before a captured let reads it after initialization"
);
}
// ── 箭头在 let 前:入口 TDZ 占位 cell 面(无编译期命中,运行时判定)──
#[test]
fn arrow_after_let_update_returns_new_value() {
assert_eq!(
eval("let step; const r = () => { step++; return step; }; step = 0; r() === 1"),
"true",
"arrow created after a captured let updates the binding"
);
}
#[test]
fn arrow_before_let_update_returns_new_value() {
// 箭头先于 let 创建:入口 TDZ 占位 cell 使声明前读抛真 TDZ,
// 声明后 MAKE_CELL 原位翻转,更新正常。
assert_eq!(
eval("const r = () => { step++; return step; }; let step; step = 0; r() === 1"),
"true",
"arrow created before the captured let updates the binding after initialization"
);
}
// ── 捕获 const:写点 TypeError(TDZ 守卫跳过面不得吞 const 判定)──
#[test]
fn captured_top_const_update_throws_type_error() {
assert_eq!(
eval("const step = 1; function r() { step++; } try { r(); false } catch (e) { e.constructor === TypeError }"),
"true",
"updating a captured const throws TypeError, not a TDZ reference error"
);
}
#[test]
fn captured_top_const_simple_assign_throws_type_error() {
assert_eq!(
eval("const step = 1; function r() { step = 2; return step; } try { r(); false } catch (e) { e.constructor === TypeError }"),
"true",
"assigning a captured const throws TypeError"
);
}
// ── 真 TDZ 保抛:声明语句执行前的读写仍抛 ReferenceError ──
#[test]
fn true_tdz_captured_let_update_throws_reference_error() {
// 声明语句在 try 块之后才执行:调用点命中入口 TDZ 占位 cell。
assert_eq!(
eval("function f() { x++; } try { f(); false } catch (e) { e.constructor === ReferenceError } let x = 1"),
"true",
"updating a captured let before its declaration throws ReferenceError"
);
}
#[test]
fn true_tdz_captured_let_update_in_nested_depth() {
assert_eq!(
eval(
"function f() { x++; } JSON.stringify({}, (k, v) => { try { f(); } catch (e) { return e.constructor.name; } return v; }); let x = 1; x === 1"
),
"true",
"true TDZ on a captured let survives builtin callback depth and stays catchable"
);
}
#[test]
fn true_tdz_captured_let_bare_read_throws_inside_catcher() {
// 裸读真 TDZ 在捕获器内抛(结果未检视,仅锁"抛且可捕获"行为)。
assert_eq!(
eval(
"function r() { return step; } JSON.stringify({}, (k, v) => { try { String(r()); return 'no-throw'; } catch (e) { return e.constructor.name; } }); let step = 1; 'done' === 'done'"
),
"true",
"bare read of a captured let before its declaration throws inside the catcher"
);
}
#[test]
fn block_level_let_tdz_update_still_throws() {
// 块级 let 无嵌套捕获(纯静态 TDZ 面):守卫跳过面不得波及,仍编译期抛。
assert_eq!(
eval("function f() { y = y + 1; let y; return y; } try { f(); false } catch (e) { e.constructor === ReferenceError }"),
"true",
"block-level let TDZ on an uncaptured binding still throws ReferenceError"
);
}
#[test]
fn block_level_let_captured_closure_reads_updated_value() {
// 块级 let 被块内闭包捕获:块内声明后更新,块外读回更新值(重执行族对照面)。
assert_eq!(
eval("function g() { { let a = 5; const c = () => a; a = 6; return c; } return null; } g()() === 6"),
"true",
"closure over a block-level let reads the updated value after the block"
);
}
// ── 非捕获对照面:顶层 var / delete / typeof 不变 ──
#[test]
fn captured_top_var_update_after_decl() {
// 顶层 var 被捕获:入口即初始化为 undefined(提升语义),更新正常。
assert_eq!(
eval("var x; function r() { x++; return x; } x = 0; r() === 1 && x === 1"),
"true",
"captured top-level var update works, var hoisting semantics preserved"
);
}
#[test]
fn delete_captured_top_let_returns_false() {
assert_eq!(
eval("let step = 1; function r() { return delete step; } r() === false && step === 1"),
"true",
"deleting a captured top-level let returns false and leaves the value"
);
}
#[test]
fn typeof_captured_top_let_after_decl() {
assert_eq!(
eval("let step; function r() { return typeof step === 'number'; } step = 0; r()"),
"true",
"typeof on a captured top-level let after initialization reports number"
);
}
// ── 解构赋值写被捕获顶层 let:标识符目标写须落 cell,闭包读新值 ──
#[test]
fn captured_top_let_array_destructuring_assign() {
// 数组解构标识符目标:写只落寄存器而 cell 不更新时,闭包读旧值,此处判红。
assert_eq!(
eval("let x = 1; function f() { return x; } [x] = [2]; f() === 2 && x === 2"),
"true",
"array destructuring assignment to a captured top-level let updates the cell"
);
}
#[test]
fn captured_top_let_object_destructuring_assign() {
assert_eq!(
eval("let x = 1; function f() { return x; } ({x} = {x: 3}); f() === 3 && x === 3"),
"true",
"object destructuring assignment to a captured top-level let updates the cell"
);
}
#[test]
fn captured_top_let_destructuring_default_assign() {
// 默认值路径:元素为 undefined 触发默认值,写同样须落 cell。
assert_eq!(
eval("let x = 1; function f() { return x; } [x = 9] = [undefined]; f() === 9 && x === 9"),
"true",
"destructuring assignment with a default value to a captured top-level let updates the cell"
);
}
#[test]
fn captured_top_let_rest_destructuring_assign() {
// rest 元素须为末位,标识符目标在 rest 前:写落 cell 的判别与主形同。
assert_eq!(
eval("let x = 0; function f() { return x; } [x, ...r] = [1, 2]; f() === 1 && x === 1"),
"true",
"rest destructuring assignment to a captured top-level let updates the cell"
);
}