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_compiler/tests/hash_collision_tests.rs
T

133 lines
5.4 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.
//! 结构哈希确定性碰撞的回归测试:
//! 补全 hash 的 `_ => {}` 兜底漏掉的变体后,不同 AST 不得产生相同哈希。
use oxide_parser::Allocator;
fn compiled(source: &str) -> u64 {
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
oxide_compiler::compiler::compiled_module_hash(&program)
}
fn structural(source: &str) -> u64 {
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
oxide_compiler::compiler::structural_hash(&program)
}
#[test]
fn array_literals_distinguished() {
assert_ne!(compiled("[1, 2, 3]"), compiled("[4, 5, 6]"));
assert_ne!(compiled("[1, 2, 3]"), compiled("[1, , 3]"), "elision vs value must differ");
assert_ne!(compiled("[1, 2, ...a]"), compiled("[1, 2, 3]"), "spread vs value must differ");
assert_ne!(compiled("[...a]"), compiled("[...b]"), "different spread sources must differ");
}
#[test]
fn meta_property_distinguished() {
// `new.target` 是 MetaProperty,`a.b` 是普通成员访问,结构不同。
assert_ne!(compiled("function f(){ return new.target }"), compiled("function f(){ return a.b }"));
// 同为 MetaProperty 时 meta/property 名字必须参与哈希(修复前两者哈希相同)。
assert_ne!(
compiled("import './x.js'; function f(){ return import.meta }"),
compiled("import './x.js'; function f(){ return new.target }"),
"meta/property names must be part of the hash"
);
}
#[test]
fn parenthesized_distinguished() {
assert_ne!(compiled("(a)"), compiled("a"), "parenthesized vs bare expression must differ");
assert_ne!(compiled("(a + b)"), compiled("a + b"));
assert_ne!(compiled("(a)"), compiled("(b)"), "inner expression must be hashed");
}
#[test]
fn await_yield_plain_distinguished() {
let await_h = compiled("async function f(){ await x }");
let yield_h = compiled("function* f(){ yield x }");
let plain_h = compiled("function f(){ x }");
assert_ne!(await_h, yield_h);
assert_ne!(await_h, plain_h);
assert_ne!(yield_h, plain_h);
// yield 的 delegate 标志与参数必须参与哈希。
assert_ne!(compiled("function* f(){ yield x }"), compiled("function* f(){ yield* x }"));
assert_ne!(compiled("function* f(){ yield x }"), compiled("function* f(){ yield y }"));
}
#[test]
fn object_literals_distinguished() {
assert_ne!(compiled("var o = { a: 1 }"), compiled("var o = { a: 2 }"));
assert_ne!(compiled("var o = { a: 1 }"), compiled("var o = { b: 1 }"));
assert_ne!(
compiled("var o = { a: 1 }"),
compiled("var o = { a: 1, b: 2 }"),
"property count must be hashed"
);
}
#[test]
fn function_statement_hash_distinguished() {
assert_ne!(compiled("function f(){ return 1 }"), compiled("function f(){ return 2 }"));
assert_ne!(compiled("function f(){ return 1 }"), compiled("function g(){ return 1 }"));
}
#[test]
fn for_of_with_distinguished() {
assert_ne!(compiled("for (x of xs) { body(x) }"), compiled("for (x of ys) { body(x) }"));
assert_ne!(compiled("for (x of xs) { body(x) }"), compiled("for (x in xs) { body(x) }"));
assert_ne!(compiled("with (a) { f() }"), compiled("with (b) { f() }"));
}
#[test]
fn import_expression_distinguished() {
assert_ne!(compiled("import('./a.js')"), compiled("import('./b.js')"));
assert_ne!(compiled("import('./a.js')"), compiled("import('./a.js', { with: { type: 'json' } })"));
}
#[test]
fn structural_hash_ignores_binding_names() {
// 结构哈希忽略绑定名:仅参数/变量改名后哈希相等。
assert_eq!(structural("function f(x){ return x }"), structural("function f(y){ return y }"));
assert_eq!(structural("var a = 1; a + a"), structural("var b = 1; b + b"));
// 编译模块哈希纳入绑定名:改名的程序哈希不等。
assert_ne!(compiled("function f(x){ return x }"), compiled("function f(y){ return y }"));
}
#[test]
fn function_body_directives_distinguished() {
// 嵌套函数体 directives 决定其严格模式:不入 hash 时 strict 标志随缓存键错配。
assert_ne!(
compiled("function f(){ 'use strict'; return 1 }"),
compiled("function f(){ return 1 }"),
"function declaration body directive must be part of the hash"
);
assert_ne!(
compiled("var f = function(){ 'use strict'; return 1 }"),
compiled("var f = function(){ return 1 }"),
"function expression body directive must be part of the hash"
);
assert_ne!(
compiled("var f = () => { 'use strict'; return 1 }"),
compiled("var f = () => { return 1 }"),
"arrow function body directive must be part of the hash"
);
assert_ne!(
compiled("class A { m(){ 'use strict'; return 1 } }"),
compiled("class A { m(){ return 1 } }"),
"class method body directive must be part of the hash"
);
assert_ne!(
compiled("var o = { m(){ 'use strict'; return 1 } }"),
compiled("var o = { m(){ return 1 } }"),
"object literal method body directive must be part of the hash"
);
// 非 prologue 位置的字符串字面量是普通语句,不构成 directive,哈希不混同。
assert_eq!(
compiled("function f(){ 'not-a-directive'; return 1 }"),
compiled("function f(){ 'not-a-directive'; return 1 }"),
"identical sources must hash equally"
);
}