From b7b6ffd7b194ffa69b3fdf645af744bfbe2fee87 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 6 Aug 2026 11:43:39 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=97=B6=E7=AD=89=E4=BB=B7=E5=AF=B9=E6=AF=94=E9=9B=86=E6=88=90?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=88D-08=20=E4=B8=BB=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - parse→emit→(dce 开/关)→lower→Vm::run 双管线对比 - 覆盖语句丢弃/重复赋值/if(false)/死闭包/try-catch/对象算术/顶层返回值/先调用后丢弃/const 重赋值 --- crates/oxide_dce/tests/runtime_eq.rs | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 crates/oxide_dce/tests/runtime_eq.rs diff --git a/crates/oxide_dce/tests/runtime_eq.rs b/crates/oxide_dce/tests/runtime_eq.rs new file mode 100644 index 00000000..1bc7ab68 --- /dev/null +++ b/crates/oxide_dce/tests/runtime_eq.rs @@ -0,0 +1,66 @@ +//! 运行时等价对比(D-08):DCE 开/关两份字节码 lower + VM 执行结果一致。 +//! +//! 同一 JS 源码走两条管线:parse → emit →(dce 开/关)→ lower → `Vm::run`, +//! 对比顶层执行结果。纯表读错 VM handler 也能被此兜底(D-08)。 +//! **不经 `Compiler::compile`**(D-10:测试层直调底层函数,不依赖开关)。 + +use oxide_vm::vm::Vm; +use oxide_vm::JsValue; + +/// parse→emit→(可选 DCE)→lower→run 全链路,返回顶层执行结果。 +fn run_source(src: &str, dce: bool) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, src).expect("parse failed"); + let mut ir = oxide_emit::Emitter::new().emit_program(&program).expect("emit failed"); + if dce { + oxide_dce::dce(&mut ir); + } + let module = oxide_ir::lower::lower(&ir).expect("lower failed"); + let mut vm = Vm::new(); + vm.run(&module) +} + +/// 结果规约成可断言形态:`(是否 Ok, 字符串化结果或错误消息)`。 +/// Err 双方同为 Err 且消息一致视为一致(如 const 重赋值样例两跑均抛 TypeError)。 +fn normalize(r: Result) -> (bool, String) { + match r { + Ok(v) => (true, v.to_string()), + Err(e) => (false, e), + } +} + +/// 死代码样例集(04-RESEARCH.md Pattern 5 清单)。 +/// 对每个样例:DCE 开与关分别执行,断言结果一致。 +#[test] +fn dead_code_removed_semantics_preserved() { + // 覆盖:表达式语句丢弃结果、重复赋值只读最后值、if(false) 分支、死闭包、 + // try/catch 内死代码、对象操作数算术(D-05 coerce 风险兜底)、 + // 顶层表达式返回值(HALT reg0 链,Pitfall 2)、先调用后丢弃(CALL 隐式 reg0,Pitfall 1)、 + // const 重赋值抛错保留(Pitfall 5)。 + let samples = [ + "1+2;3+4", // 语句丢弃结果 + "'a'+'b';5;", // 语句丢弃结果(字符串) + "var x=1; x=2; x;", // 重复赋值只读最后值 + "function a(){return 1;} if(false){a();} 3;", // if(false) 分支 + "function f(){}; 1;", // 死闭包 + "try{1+2;}catch(e){3;} 4;", // try/catch 内死代码 + "({valueOf(){side=1; return 1}})+2;", // 对象操作数算术(coerce 兜底) + "1+2", // 顶层表达式返回值(Pitfall 2) + "function f(){return 1;} f(); 2+3;", // 先调用后丢弃(Pitfall 1) + "const x=1; x=2;", // const 重赋值(Pitfall 5) + ]; + for src in samples { + let a = normalize(run_source(src, true)); + let b = normalize(run_source(src, false)); + assert_eq!(a, b, "DCE 前后语义不一致: {src}"); + } +} + +/// 单个样例 DCE 开/关断言(失败时给出独立测试名,便于定位)。 +#[test] +fn single_sample_top_level_return_value() { + let a = normalize(run_source("1+2", true)); + let b = normalize(run_source("1+2", false)); + assert_eq!(a, b, "顶层返回值在 DCE 后变化"); + assert_eq!(a, (true, "3".to_string()), "顶层 1+2 应返回 3"); +}