mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
332 lines
12 KiB
Rust
332 lines
12 KiB
Rust
//! 被捕获词法绑定(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"
|
||
);
|
||
}
|