Files
OxideJS-New/crates/oxide_vm/tests/loop_closure_binding_tests.rs
T

131 lines
4.8 KiB
Rust
Raw 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.
//! 循环每迭代绑定(CreatePerIterationEnvironment):for/for-of/for-in 的 let/const
//! 闭包捕获各自迭代快照。对应 PLAN 06-01(正确性报告 P1/P2)。
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
let result = vm.run(&Arc::new(module))?;
Ok((vm, result))
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.lookup_str(val).unwrap_or_default()
}
#[test]
fn c_style_for_let_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (let i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}
#[test]
fn c_style_for_const_update_writes_per_iteration() {
// for-const 的 update 段写 per-iteration 可变绑定(CreateMutableBinding),
// 不得抛 "Assignment to constant variable"(V8 输出 0 1 2)。
let (vm, result) = eval("let out=[]; for (const i = 0; i < 3; i++) out.push(i); out.join(',')").unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}
#[test]
fn c_style_for_const_captures_per_iteration() {
// for-const 循环变量被闭包捕获:每迭代 fresh,update 段写寄存器不污染本迭代 cell。
let (vm, result) =
eval("let fns = []; for (const i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}
#[test]
fn c_style_for_const_compound_update_writes_per_iteration() {
// 复合赋值形态的 update 同样合法(i += 2 是 update 段的 per-iteration 可变写)。
let (vm, result) = eval("let out=[]; for (const i = 0; i < 6; i += 2) out.push(i); out.join(',')").unwrap();
assert_eq!(to_str(&vm, result), "0,2,4");
}
#[test]
fn c_style_for_let_captures_after_multiple_updates() {
let (vm, result) = eval(
"let fns = []; for (let i = 0; i < 5; i += 2) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "0,2,4");
}
#[test]
fn c_style_for_var_remains_single_binding() {
// var 循环变量是单绑定(规范:闭包捕获同一绑定,最终值共享)。
let (vm, result) =
eval("let fns = []; for (var i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "3,3,3");
}
#[test]
fn for_of_const_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (const a of [1, 2]) fns.push(() => a); [fns[0](), fns[1]()].join(',')").unwrap();
assert_eq!(to_str(&vm, result), "1,2");
}
#[test]
fn for_of_let_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (let a of [10, 20, 30]) fns.push(() => a); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "10,20,30");
}
#[test]
fn for_of_destructuring_captures_per_iteration() {
// P2:解构 + 闭包捕获不再产出垃圾值。
let (vm, result) = eval(
"let fns = []; for (const [a, b] of [[1, 2], [3, 4]]) fns.push(() => a + b); [fns[0](), fns[1]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "3,7");
}
#[test]
fn for_of_object_destructuring_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (const {x, y} of [{x:1,y:2},{x:3,y:4}]) fns.push(() => x * y); [fns[0](), fns[1]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "2,12");
}
#[test]
fn for_in_const_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (const k in {a:1,b:2}) fns.push(() => k); [fns[0](), fns[1]()].join(',')").unwrap();
assert_eq!(to_str(&vm, result), "a,b");
}
#[test]
fn nested_loop_closure_captures_distinct_bindings() {
let (vm, result) = eval(
"let fns = []; for (let i = 0; i < 2; i++) for (let j = 0; j < 2; j++) fns.push(() => i + j); [fns[0](), fns[1](), fns[2](), fns[3]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,1,2");
}
#[test]
fn loop_variable_mutation_visible_to_same_iteration() {
// 同迭代内变量修改(非闭包)仍直接可见:fresh 每迭代一次,迭代内读写走同一 cell。
let (vm, result) = eval(
"let fns = []; for (let i = 0; i < 3; i++) { fns.push(() => i); i += 0; } [fns[0](), fns[1](), fns[2]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}