fix: 编译缓存键递归纳入解构形态绑定名

This commit is contained in:
2026-10-10 07:17:35 +00:00
parent 3cc811e730
commit 17e5992dcc
7 changed files with 217 additions and 38 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_log", "oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cfg" name = "oxide_cfg"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_emit", "oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cli" name = "oxide_cli"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"bumpalo", "bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_code_cache" name = "oxide_code_cache"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_dce" name = "oxide_dce"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_emit" name = "oxide_emit"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxc_allocator", "oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_engine" name = "oxide_engine"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_log", "oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_kernel" name = "oxide_kernel"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_log" name = "oxide_log"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_regalloc" name = "oxide_regalloc"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_runtime_api" name = "oxide_runtime_api"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_test262" name = "oxide_test262"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_types" name = "oxide_types"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.14.3" version = "0.14.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.14.3" version = "0.14.4"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+2 -3
View File
@@ -58,9 +58,8 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i
} }
Expression::AssignmentExpression(assign) => { Expression::AssignmentExpression(assign) => {
std::mem::discriminant(&assign.operator).hash(h); std::mem::discriminant(&assign.operator).hash(h);
if let Some(target) = assign.left.as_simple_assignment_target() { // 全量分派:简单目标与数组/对象解构目标都计入,左值整体不得漏哈希。
target::hash_simple_assignment_target(target, h, include_binding_names); target::hash_assignment_target(&assign.left, h, include_binding_names);
}
hash_expression(&assign.right, h, include_binding_names); hash_expression(&assign.right, h, include_binding_names);
} }
Expression::UpdateExpression(update) => { Expression::UpdateExpression(update) => {
+53 -10
View File
@@ -2,15 +2,18 @@
//! `Program` 的稳定哈希,用作编译缓存键(code cache)。 //! `Program` 的稳定哈希,用作编译缓存键(code cache)。
//! //!
//! `structural_hash` 为粗粒度键,忽略变量声明名、标识符读、import 本地名与 //! `structural_hash` 为粗粒度键,忽略变量声明名、标识符读、import 本地名与
//! 函数参数等大部分绑定名;`compiled_module_hash` 为精确键,额外纳入标识符形态的绑定名 //! 函数参数等大部分绑定名(含声明/参数位置的解构形态);`compiled_module_hash`
//! (变量声明、参数与 catch 的解构形态均不贡献哈希)。 //! 为精确键,额外纳入绑定名:标识符形态直接计入,解构形态(数组/对象/默认值
//! pattern)递归计入键、名、默认值表达式与 rest 的完整结构。
//! 函数声明名、类名与 catch 参数标识名是编译产物物化名,两种粒度下恒计入哈希; //! 函数声明名、类名与 catch 参数标识名是编译产物物化名,两种粒度下恒计入哈希;
//! 变量声明、函数/箭头/方法参数与 catch 参数为解构形态时均不贡献哈希。 //! 解构赋值目标的完整结构(元素数、键、rest、默认值表达式)两种粒度下均计入,
//! 其内部绑定名仅精确键计入。
use oxide_parser::{ use oxide_parser::{
ArrayExpressionElement, BindingPattern, ChainElement, Class, ClassElement, Declaration, ArrayAssignmentTarget, ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault,
AssignmentTargetProperty, BindingPattern, ChainElement, Class, ClassElement, Declaration,
ExportDefaultDeclarationKind, Expression, ForStatementInit, ForStatementLeft, Function, ImportDeclarationSpecifier, ExportDefaultDeclarationKind, Expression, ForStatementInit, ForStatementLeft, Function, ImportDeclarationSpecifier,
ModuleExportName, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement, ModuleExportName, ObjectAssignmentTarget, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement,
}; };
use std::hash::Hash; use std::hash::Hash;
@@ -75,12 +78,52 @@ fn hash_program(program: &oxide_parser::Program, include_binding_names: bool) ->
h.finish() h.finish()
} }
/// 哈希 `BindingIdentifier` 的绑定名;只由 `include_binding_names` 为真的调用点 /// 哈希 `BindingPattern` 的完整结构:标识符形态计绑定名,解构形态(数组/对象/
/// 使用(变量声明、函数与箭头参数、方法参数、catch 参数),非标识符模式 /// 默认值 pattern)递归计入键、名、默认值表达式与 rest 的完整结构。
/// (数组/对象解构)不贡献哈希。 ///
/// # 边界与前提
/// - 只由 `include_binding_names` 为真的调用点使用(变量声明、函数与箭头参数、
/// 方法参数、catch 参数),故嵌套调用对 `hash_expression` / `hash_property_key`
/// 硬编码传 `true`。
///
/// # 注意事项
/// - 数组省略位与 rest 有无须以哨兵区分:位置敏感,`[a, , b]` 与 `[a, b, ]`
/// 同长不同位,`[a, b]` 与 `[a, b, ...r]` 结构不同。
fn hash_binding_pattern(pattern: &BindingPattern, h: &mut rustc_hash::FxHasher) { fn hash_binding_pattern(pattern: &BindingPattern, h: &mut rustc_hash::FxHasher) {
if let BindingPattern::BindingIdentifier(ident) = pattern { match pattern {
ident.name.as_str().hash(h); BindingPattern::BindingIdentifier(ident) => {
ident.name.as_str().hash(h);
}
BindingPattern::ArrayPattern(ap) => {
(ap.elements.len() as u32).hash(h);
for elem in &ap.elements {
match elem {
Some(bp) => hash_binding_pattern(bp, h),
// 省略位(如 `[a, , b]`)与有值位须区分:位置敏感。
None => 0u8.hash(h),
}
}
if let Some(rest) = &ap.rest {
1u8.hash(h);
hash_binding_pattern(&rest.argument, h);
}
}
BindingPattern::ObjectPattern(op) => {
(op.properties.len() as u32).hash(h);
for prop in &op.properties {
property::hash_property_key(&prop.key, h, true);
hash_binding_pattern(&prop.value, h);
}
if let Some(rest) = &op.rest {
1u8.hash(h);
hash_binding_pattern(&rest.argument, h);
}
}
BindingPattern::AssignmentPattern(ap) => {
hash_binding_pattern(&ap.left, h);
// 默认值表达式是字节码依赖(发射时求值入池),须计入,不能只哈希左值名。
expression::hash_expression(&ap.right, h, true);
}
} }
} }
+17 -4
View File
@@ -252,10 +252,23 @@ fn hash_module_export_name(name: &ModuleExportName<'_>, h: &mut rustc_hash::FxHa
fn hash_for_statement_left(left: &ForStatementLeft<'_>, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { fn hash_for_statement_left(left: &ForStatementLeft<'_>, h: &mut rustc_hash::FxHasher, include_binding_names: bool) {
std::mem::discriminant(left).hash(h); std::mem::discriminant(left).hash(h);
if let ForStatementLeft::VariableDeclaration(decl) = left { match left {
hash_variable_declaration(decl, h, include_binding_names); ForStatementLeft::VariableDeclaration(decl) => {
} else if let Some(target) = left.as_simple_assignment_target() { hash_variable_declaration(decl, h, include_binding_names);
target::hash_simple_assignment_target(target, h, include_binding_names); }
// 解构赋值头与赋值目标同构,分派到共享助手。
ForStatementLeft::ArrayAssignmentTarget(ap) => {
target::hash_array_assignment_target(ap, h, include_binding_names);
}
ForStatementLeft::ObjectAssignmentTarget(op) => {
target::hash_object_assignment_target(op, h, include_binding_names);
}
// 简单目标(标识符/成员/TS 断言)走既有分派。
_ => {
if let Some(simple) = left.as_simple_assignment_target() {
target::hash_simple_assignment_target(simple, h, include_binding_names);
}
}
} }
} }
+95 -1
View File
@@ -1,4 +1,4 @@
//! 赋值目标(SimpleAssignmentTarget)的结构哈希。 //! 赋值目标(SimpleAssignmentTarget / AssignmentTarget)的结构哈希。
use super::*; use super::*;
@@ -36,3 +36,97 @@ pub(super) fn hash_simple_assignment_target(
} }
}); });
} }
/// 哈希全量 `AssignmentTarget`:简单目标(标识符/成员/TS 断言)复用既有分派,
/// 数组/对象解构目标递归计入元素、键、默认值表达式与 rest 的完整结构。
///
/// # 边界与前提
/// - `include_binding_names` 透传至简单目标、默认值表达式与解构目标内部绑定名;
/// 解构目标的结构(元素数、属性键、rest)两种粒度下恒计入。
pub(super) fn hash_assignment_target(
target: &AssignmentTarget, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
) {
if let Some(simple) = target.as_simple_assignment_target() {
hash_simple_assignment_target(simple, h, include_binding_names);
return;
}
match target {
AssignmentTarget::ArrayAssignmentTarget(ap) => {
hash_array_assignment_target(ap, h, include_binding_names);
}
AssignmentTarget::ObjectAssignmentTarget(op) => {
hash_object_assignment_target(op, h, include_binding_names);
}
_ => {}
}
}
/// 哈希数组赋值目标:元素逐个哈希(省略位以哨兵区分),rest 有无以哨兵区分。
pub(super) fn hash_array_assignment_target(
target: &ArrayAssignmentTarget, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
) {
(target.elements.len() as u32).hash(h);
for elem in &target.elements {
match elem {
Some(td) => hash_assignment_target_maybe_default(td, h, include_binding_names),
// 省略位与有值位须区分:位置敏感。
None => 0u8.hash(h),
}
}
if let Some(rest) = &target.rest {
1u8.hash(h);
hash_assignment_target(&rest.target, h, include_binding_names);
}
}
/// 哈希对象赋值目标:属性逐个哈希(键与绑定),rest 有无以哨兵区分。
pub(super) fn hash_object_assignment_target(
target: &ObjectAssignmentTarget, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
) {
(target.properties.len() as u32).hash(h);
for prop in &target.properties {
hash_assignment_target_property(prop, h, include_binding_names);
}
if let Some(rest) = &target.rest {
1u8.hash(h);
hash_assignment_target(&rest.target, h, include_binding_names);
}
}
/// 哈希 `AssignmentTargetMaybeDefault`:带默认值形态计绑定递归加默认值表达式,
/// 其余变体(继承自 `AssignmentTarget`)转全量分派。
fn hash_assignment_target_maybe_default(
target: &AssignmentTargetMaybeDefault, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
) {
match target {
AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(d) => {
hash_assignment_target(&d.binding, h, include_binding_names);
// 默认值表达式是字节码依赖(发射时求值入池),须计入。
expression::hash_expression(&d.init, h, include_binding_names);
}
other => {
if let Some(t) = other.as_assignment_target() {
hash_assignment_target(t, h, include_binding_names);
}
}
}
}
/// 哈希对象赋值目标属性:标识符形态计绑定名与默认值(若有),
/// 属性形态计键与绑定递归。
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 let Some(init) = &id.init {
expression::hash_expression(init, h, include_binding_names);
}
}
AssignmentTargetProperty::AssignmentTargetPropertyProperty(pp) => {
property::hash_property_key(&pp.name, h, include_binding_names);
hash_assignment_target_maybe_default(&pp.binding, h, include_binding_names);
}
}
}
@@ -107,6 +107,36 @@ fn import_defer_distinguished() {
); );
} }
#[test]
fn destructuring_binding_names_distinguished() {
// 声明侧:解构绑定名、键、rest 与嵌套结构全量计入精确键。
assert_ne!(compiled("var [a, b] = [1, 2]"), compiled("var [p, q] = [1, 2]"));
assert_ne!(compiled("var {x: a} = o"), compiled("var {y: b} = o"));
assert_ne!(compiled("var [a, ...r] = o"), compiled("var [a, ...s] = o"));
assert_ne!(
compiled("var [{p: a}, b] = o"),
compiled("var [a, b] = o"),
"nested pattern structure must be part of the hash"
);
// 默认值侧:默认值表达式计入哈希。
assert_ne!(compiled("var [a = f()] = o"), compiled("var [a = g()] = o"));
// 赋值目标侧:解构赋值左值整体不得漏哈希。
assert_ne!(compiled("[a, b] = [1, 2]"), compiled("[p, q] = [1, 2]"));
assert_ne!(compiled("({a} = o)"), compiled("({b} = o)"));
// for 头侧:声明形态与赋值形态的解构头都计入哈希。
assert_ne!(compiled("for (var [a, b] of xs) {}"), compiled("for (var [p, q] of xs) {}"));
assert_ne!(compiled("for ({a} of xs) {}"), compiled("for ({b} of xs) {}"));
// 函数参数与 catch 侧:解构参数名计入哈希。
assert_ne!(compiled("function f([a, b]) {}"), compiled("function f([p, q]) {}"));
assert_ne!(compiled("try {} catch ([a]) {}"), compiled("try {} catch ([p]) {}"));
// 同源等价回归。
assert_eq!(compiled("var [a, b] = [1, 2]"), compiled("var [a, b] = [1, 2]"));
}
#[test] #[test]
fn structural_hash_ignores_binding_names() { fn structural_hash_ignores_binding_names() {
// 结构哈希忽略绑定名:仅参数/变量改名后哈希相等。 // 结构哈希忽略绑定名:仅参数/变量改名后哈希相等。