mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 缓存 debug 校验递归比子模块,赋值目标属性名按键粒度计入
This commit is contained in:
@@ -30,7 +30,7 @@ pub enum Constant {
|
||||
/// `enclosing_reg` 是外层函数中该变量的寄存器位;`cell_idx` 是父函数 own cell
|
||||
/// 表下标(`parent_uv_idx` 为 None 时)。多级闭包(外层变量本身就是父函数从更
|
||||
/// 外层捕获的 upvalue)时 `parent_uv_idx` 给出父闭包 `upvalues` 数组下标。
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UpvalueCapture {
|
||||
pub name: String,
|
||||
pub enclosing_reg: u32,
|
||||
@@ -50,6 +50,10 @@ pub struct UpvalueCapture {
|
||||
/// - `is_arrow` / `captured_this_const_idx` — 箭头函数词法 `this`;
|
||||
/// - `is_class_constructor` / `is_derived_constructor` / `needs_home_object` — 类相关;
|
||||
/// - `upvalue_captures` / `cells_needed` — 闭包捕获描述。
|
||||
///
|
||||
/// 派生 `PartialEq` 供缓存 debug 重编译校验做整结构比较(递归覆盖子模块树);
|
||||
/// `Debug` 供校验失败时输出整棵模块树(与 `Display` 反汇编互补)。
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CompiledModule {
|
||||
pub bytecode: Arc<[opcode::Instr]>,
|
||||
pub constants: Vec<Constant>,
|
||||
|
||||
@@ -59,7 +59,7 @@ impl CodeForge {
|
||||
}
|
||||
|
||||
/// 命中返回缓存模块,未命中则调用 `compile` 编译后缓存并返回。
|
||||
/// debug 构建下命中时会重编译校验 bytecode,检测结构哈希碰撞。
|
||||
/// debug 构建下命中时会重编译校验整模块(含子模块树),检测结构哈希碰撞。
|
||||
pub fn get_or_insert_with<F>(&self, hash: u64, compile: F) -> Result<Arc<CompiledModule>, String>
|
||||
where
|
||||
F: Fn() -> Result<CompiledModule, String>,
|
||||
@@ -73,9 +73,11 @@ impl CodeForge {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let fresh = compile()?;
|
||||
// 整结构比较(递归覆盖子模块树):只比顶层字节码会漏检嵌套
|
||||
// 函数体内的同键异码。
|
||||
debug_assert_eq!(
|
||||
module.bytecode, fresh.bytecode,
|
||||
"structural hash collision: cached bytecode differs from recompiled for hash {hash}",
|
||||
module.as_ref(), &fresh,
|
||||
"structural hash collision: cached module differs from recompiled for hash {hash}",
|
||||
);
|
||||
}
|
||||
return Ok(module);
|
||||
|
||||
@@ -85,6 +85,67 @@ fn cache_hit_debug_verifies_bytecode() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// 构造带指定字节码的子模块(嵌套函数体夹具)。
|
||||
fn submodule(bytecode: Vec<u32>) -> CompiledModule {
|
||||
let mut m = CompiledModule::new();
|
||||
m.bytecode = Arc::from(bytecode);
|
||||
m
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_hit_debug_verifies_nested_submodules_identical() {
|
||||
let forge = forge(16);
|
||||
let mut m1 = CompiledModule::new();
|
||||
m1.bytecode = Arc::from(vec![1, 2, 3]);
|
||||
m1.sub_modules = vec![Arc::new(submodule(vec![4, 5, 6]))];
|
||||
let cached = forge.insert(7, m1);
|
||||
let result = forge
|
||||
.get_or_insert_with(7, || {
|
||||
let mut m = CompiledModule::new();
|
||||
m.bytecode = Arc::from(vec![1, 2, 3]);
|
||||
m.sub_modules = vec![Arc::new(submodule(vec![4, 5, 6]))];
|
||||
Ok(m)
|
||||
})
|
||||
.unwrap();
|
||||
assert!(Arc::ptr_eq(&cached, &result));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(debug_assertions, should_panic(expected = "structural hash collision"))]
|
||||
fn cache_hit_debug_verifies_nested_submodule_differs() {
|
||||
let forge = forge(16);
|
||||
let mut m1 = CompiledModule::new();
|
||||
m1.bytecode = Arc::from(vec![1, 2, 3]);
|
||||
m1.sub_modules = vec![Arc::new(submodule(vec![4, 5, 6]))];
|
||||
forge.insert(8, m1);
|
||||
let _ = forge
|
||||
.get_or_insert_with(8, || {
|
||||
let mut m = CompiledModule::new();
|
||||
m.bytecode = Arc::from(vec![1, 2, 3]);
|
||||
m.sub_modules = vec![Arc::new(submodule(vec![7, 8, 9]))];
|
||||
Ok(m)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(debug_assertions, should_panic(expected = "structural hash collision"))]
|
||||
fn cache_hit_debug_verifies_top_level_differs_keeps_prefix() {
|
||||
let forge = forge(16);
|
||||
let mut m1 = CompiledModule::new();
|
||||
m1.bytecode = Arc::from(vec![1, 2, 3]);
|
||||
m1.sub_modules = vec![Arc::new(submodule(vec![4, 5, 6]))];
|
||||
forge.insert(9, m1);
|
||||
let _ = forge
|
||||
.get_or_insert_with(9, || {
|
||||
let mut m = CompiledModule::new();
|
||||
m.bytecode = Arc::from(vec![9, 8, 7]);
|
||||
m.sub_modules = vec![Arc::new(submodule(vec![4, 5, 6]))];
|
||||
Ok(m)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_hit_returns_cached_not_recompiled() {
|
||||
let forge = forge(16);
|
||||
|
||||
@@ -118,14 +118,16 @@ fn hash_assignment_target_maybe_default(
|
||||
}
|
||||
}
|
||||
|
||||
/// 哈希对象赋值目标属性:标识符形态计绑定名与默认值(若有),
|
||||
/// 哈希对象赋值目标属性:标识符形态计绑定名(仅精确键)与默认值(若有),
|
||||
/// 属性形态计键与绑定递归。
|
||||
fn hash_assignment_target_property(
|
||||
prop: &AssignmentTargetProperty, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
|
||||
) {
|
||||
match prop {
|
||||
AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) => {
|
||||
id.binding.name.as_str().hash(h);
|
||||
if include_binding_names {
|
||||
id.binding.name.as_str().hash(h);
|
||||
}
|
||||
if let Some(init) = &id.init {
|
||||
expression::hash_expression(init, h, include_binding_names);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user