mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 编译缓存键递归纳入解构形态绑定名
This commit is contained in:
@@ -58,9 +58,8 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i
|
||||
}
|
||||
Expression::AssignmentExpression(assign) => {
|
||||
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);
|
||||
}
|
||||
Expression::UpdateExpression(update) => {
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
//! `Program` 的稳定哈希,用作编译缓存键(code cache)。
|
||||
//!
|
||||
//! `structural_hash` 为粗粒度键,忽略变量声明名、标识符读、import 本地名与
|
||||
//! 函数参数等大部分绑定名;`compiled_module_hash` 为精确键,额外纳入标识符形态的绑定名
|
||||
//! (变量声明、参数与 catch 的解构形态均不贡献哈希)。
|
||||
//! 函数参数等大部分绑定名(含声明/参数位置的解构形态);`compiled_module_hash`
|
||||
//! 为精确键,额外纳入绑定名:标识符形态直接计入,解构形态(数组/对象/默认值
|
||||
//! pattern)递归计入键、名、默认值表达式与 rest 的完整结构。
|
||||
//! 函数声明名、类名与 catch 参数标识名是编译产物物化名,两种粒度下恒计入哈希;
|
||||
//! 变量声明、函数/箭头/方法参数与 catch 参数为解构形态时均不贡献哈希。
|
||||
//! 解构赋值目标的完整结构(元素数、键、rest、默认值表达式)两种粒度下均计入,
|
||||
//! 其内部绑定名仅精确键计入。
|
||||
|
||||
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,
|
||||
ModuleExportName, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement,
|
||||
ModuleExportName, ObjectAssignmentTarget, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
@@ -75,12 +78,52 @@ fn hash_program(program: &oxide_parser::Program, include_binding_names: bool) ->
|
||||
h.finish()
|
||||
}
|
||||
|
||||
/// 哈希 `BindingIdentifier` 的绑定名;只由 `include_binding_names` 为真的调用点
|
||||
/// 使用(变量声明、函数与箭头参数、方法参数、catch 参数),非标识符模式
|
||||
/// (数组/对象解构)不贡献哈希。
|
||||
/// 哈希 `BindingPattern` 的完整结构:标识符形态计绑定名,解构形态(数组/对象/
|
||||
/// 默认值 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) {
|
||||
if let BindingPattern::BindingIdentifier(ident) = pattern {
|
||||
ident.name.as_str().hash(h);
|
||||
match pattern {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
std::mem::discriminant(left).hash(h);
|
||||
if let ForStatementLeft::VariableDeclaration(decl) = left {
|
||||
hash_variable_declaration(decl, h, include_binding_names);
|
||||
} else if let Some(target) = left.as_simple_assignment_target() {
|
||||
target::hash_simple_assignment_target(target, h, include_binding_names);
|
||||
match left {
|
||||
ForStatementLeft::VariableDeclaration(decl) => {
|
||||
hash_variable_declaration(decl, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! 赋值目标(SimpleAssignmentTarget)的结构哈希。
|
||||
//! 赋值目标(SimpleAssignmentTarget / AssignmentTarget)的结构哈希。
|
||||
|
||||
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]
|
||||
fn structural_hash_ignores_binding_names() {
|
||||
// 结构哈希忽略绑定名:仅参数/变量改名后哈希相等。
|
||||
|
||||
Reference in New Issue
Block a user