mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: 循环 let/const 每迭代绑定(MAKE_CELL_FRESH + 解构捕获修复)
This commit is contained in:
@@ -204,6 +204,24 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 无条件新建 cell 并替换 cell_stack[cell_idx],值为 `regs[rd]`。
|
||||
///
|
||||
/// 循环每迭代绑定用:把当前迭代值拷入新 cell,本迭代创建的闭包经
|
||||
/// CREATE_CLOSURE 捕获新 cell(旧闭包仍指向旧 cell,值保持)。与
|
||||
/// `dispatch_make_cell` 的唯一区别是恒新建、不复用占位 cell。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_make_cell_fresh(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||
let cell_idx = opcode::imm16(instr) as usize;
|
||||
let value = self.regs[rd];
|
||||
let current = self.cell_stack.last_mut().unwrap();
|
||||
while current.len() <= cell_idx {
|
||||
current.push(std::ptr::null_mut());
|
||||
}
|
||||
let cell = self.gc_state.session_epoch.alloc(Cell::new(value, true));
|
||||
current[cell_idx] = cell as *mut Cell;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let cell_idx = b;
|
||||
|
||||
@@ -1101,6 +1101,9 @@ impl Vm {
|
||||
OpCode::MAKE_CELL => {
|
||||
self.dispatch_make_cell(rd, instr)?;
|
||||
}
|
||||
OpCode::MAKE_CELL_FRESH => {
|
||||
self.dispatch_make_cell_fresh(rd, instr)?;
|
||||
}
|
||||
OpCode::CELL_GET => {
|
||||
self.dispatch_cell_get(rd, a, b)?;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
//! 循环每迭代绑定(CreatePerIterationEnvironment):for/for-of/for-in 的 let/const
|
||||
//! 闭包捕获各自迭代快照。对应 PLAN 06-01(正确性报告 P1/P2)。
|
||||
|
||||
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(&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_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");
|
||||
}
|
||||
Reference in New Issue
Block a user