This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/tests/const_guard_tests.rs
T

114 lines
3.9 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.
//! const guard 回归测试:const 声明路径不依赖寄存器槽新鲜度。
//!
//! 背景:`dispatch_store_var` 的 const guard(b!=0)读 `regs[rd]` 判断目标槽是否已初始化。
//! 声明路径带 b=1 时,checkpoint/寄存器复用残留的陈旧值会误抛 "Assignment to constant variable"。
//! 寄存器分配复用寄存器后此问题必现——本测试锁死声明路径恒 b=0 的行为。
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(&module) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
}
}
// 复现样例:switch 语句(checkpoint 复用槽)后跟 const 声明。
// 修复前误抛 "Assignment to constant variable",修复后输出 5。
#[test]
fn switch_then_const_declaration_no_false_positive() {
assert_eq!(eval("switch (1) { case 1: break; } const x = 5; x"), "5");
}
// 锚:对已声明 const 再赋值仍必须抛错(guard 保留在赋值路径)。
#[test]
fn const_reassignment_still_throws() {
let out = eval("const x = 5; x = 6");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 锚:块级 const 遮蔽互不影响(内层 const 不污染外层槽)。
#[test]
fn block_scoped_const_shadowing_independent() {
assert_eq!(eval("const x = 5; { const x = 1; } x"), "5");
}
// 锚:解构 const 声明正常(emit_object_binding → emit_bind_target)。
#[test]
fn const_destructuring_declaration_works() {
assert_eq!(eval("const { a } = { a: 7 }; a"), "7");
}
// const 初始化为 undefined 时再赋值也必须抛(guard 不能依赖槽值)。
#[test]
fn const_undefined_reassignment_throws() {
let out = eval("const x = undefined; x = 5");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// const 初始化为 undefined 时读取不受影响。
#[test]
fn const_undefined_read_ok() {
assert_eq!(eval("const x = undefined; x"), "undefined");
}
// 闭包捕获 const 赋值抛(cell 写路径编译期拦截)。
#[test]
fn const_captured_write_throws() {
let out = eval("const x = 1; (()=>x=2)()");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 函数体内 const 被嵌套函数捕获(upvalue 路径)写同样抛。
#[test]
fn const_upvalue_write_throws() {
let out = eval("function outer(){ const x=1; function inner(){ x=2; } inner(); } outer()");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 自增/自减路径 const 赋值抛。
#[test]
fn const_update_throws() {
let out = eval("const x = 1; x++");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 复合赋值路径 const 抛。
#[test]
fn const_compound_throws() {
let out = eval("const x = 1; x += 1");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 解构赋值目标 const 抛。
#[test]
fn const_destructuring_assignment_throws() {
let out = eval("const [a] = [1]; a = 5");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}
// 逻辑赋值短路场景:const truthy 时 ||= 不写不抛。
#[test]
fn const_logical_assign_short_circuit_ok() {
assert_eq!(eval("const x = 1; x ||= 5; x"), "1");
}
// 逻辑赋值非短路场景:const falsy 时 ||= 写抛。
#[test]
fn const_logical_assign_write_throws() {
let out = eval("const x = 0; x ||= 5");
assert!(out.contains("Assignment to constant variable"), "got: {out}");
}