diff --git a/Cargo.lock b/Cargo.lock index 22ccee07..e3cfbdd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.14.4" +version = "0.14.5" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.14.4" +version = "0.14.5" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.14.4" +version = "0.14.5" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.14.4" +version = "0.14.5" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.14.4" +version = "0.14.5" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.14.4" +version = "0.14.5" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.14.4" +version = "0.14.5" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.14.4" +version = "0.14.5" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.14.4" +version = "0.14.5" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.14.4" +version = "0.14.5" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.14.4" +version = "0.14.5" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 29a08b22..ea5ef453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.14.4" +version = "0.14.5" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_compiler/src/hash/expression.rs b/crates/oxide_compiler/src/hash/expression.rs index cbfb3dc7..4d9155a9 100644 --- a/crates/oxide_compiler/src/hash/expression.rs +++ b/crates/oxide_compiler/src/hash/expression.rs @@ -16,9 +16,19 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i Expression::CallExpression(call) => { (call.arguments.len() as u32).hash(h); hash_expression(&call.callee, h, include_binding_names); + // spread 实参改指令形态(CALL 变 CALL_SPREAD)与装载方式,逐位打形态标记。 for arg in &call.arguments { - if let Some(expr) = arg.as_expression() { - hash_expression(expr, h, include_binding_names); + match arg { + Argument::SpreadElement(sp) => { + 1u8.hash(h); + hash_expression(&sp.argument, h, include_binding_names); + } + other => { + 0u8.hash(h); + if let Some(expr) = other.as_expression() { + hash_expression(expr, h, include_binding_names); + } + } } } } @@ -69,6 +79,11 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i } Expression::TemplateLiteral(tl) => { (tl.quasis.len() as u32).hash(h); + // 段值是字节码依赖(发射侧把 cooked 写入常量池);cooked 为 None 与 + // Some(空串) 的池键都是空串,字节码相同,口径上合并。 + for q in &tl.quasis { + q.value.cooked.as_deref().unwrap_or("").hash(h); + } for expr in &tl.expressions { hash_expression(expr, h, include_binding_names); } @@ -76,6 +91,12 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i Expression::TaggedTemplateExpression(tt) => { hash_expression(&tt.tag, h, include_binding_names); (tt.quasi.quasis.len() as u32).hash(h); + // 发射侧同时消费 cooked 与 raw;cooked 为 None 时写标记字,与 + // Some(空串) 的常量下标字不同,须保留 None 区分。 + for q in &tt.quasi.quasis { + q.value.cooked.hash(h); + q.value.raw.hash(h); + } for expr in &tt.quasi.expressions { hash_expression(expr, h, include_binding_names); } @@ -128,9 +149,19 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i Expression::NewExpression(ne) => { hash_expression(&ne.callee, h, include_binding_names); (ne.arguments.len() as u32).hash(h); + // spread 实参改指令形态(NEW_EXPRESSION 变 NEW_EXPRESSION_SPREAD),逐位打形态标记。 for arg in &ne.arguments { - if let Some(expr) = arg.as_expression() { - hash_expression(expr, h, include_binding_names); + match arg { + Argument::SpreadElement(sp) => { + 1u8.hash(h); + hash_expression(&sp.argument, h, include_binding_names); + } + other => { + 0u8.hash(h); + if let Some(expr) = other.as_expression() { + hash_expression(expr, h, include_binding_names); + } + } } } } @@ -169,15 +200,19 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i } Expression::ArrayExpression(arr) => { (arr.elements.len() as u32).hash(h); + // 元素形态(spread / 省略 / 值)逐位打标记:spread 源改变装载指令, + // 位置敏感,`[...a]` 与 `[a]` 不得同键。 for element in &arr.elements { match element { ArrayExpressionElement::SpreadElement(spread) => { + 1u8.hash(h); hash_expression(&spread.argument, h, include_binding_names); } ArrayExpressionElement::Elision(_) => { - 1u8.hash(h); + 2u8.hash(h); } other => { + 0u8.hash(h); if let Some(expr) = other.as_expression() { hash_expression(expr, h, include_binding_names); } @@ -262,9 +297,19 @@ pub(super) fn hash_chain_element(element: &ChainElement, h: &mut rustc_hash::FxH hash_expression(&call.callee, h, include_binding_names); call.optional.hash(h); (call.arguments.len() as u32).hash(h); + // spread 实参改指令形态(CALL_SPREAD),逐位打形态标记。 for arg in &call.arguments { - if let Some(expr) = arg.as_expression() { - hash_expression(expr, h, include_binding_names); + match arg { + Argument::SpreadElement(sp) => { + 1u8.hash(h); + hash_expression(&sp.argument, h, include_binding_names); + } + other => { + 0u8.hash(h); + if let Some(expr) = other.as_expression() { + hash_expression(expr, h, include_binding_names); + } + } } } } diff --git a/crates/oxide_compiler/src/hash/mod.rs b/crates/oxide_compiler/src/hash/mod.rs index 486f102a..4f9428b8 100644 --- a/crates/oxide_compiler/src/hash/mod.rs +++ b/crates/oxide_compiler/src/hash/mod.rs @@ -10,7 +10,7 @@ //! 其内部绑定名仅精确键计入。 use oxide_parser::{ - ArrayAssignmentTarget, ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault, + ArrayAssignmentTarget, ArrayExpressionElement, Argument, AssignmentTarget, AssignmentTargetMaybeDefault, AssignmentTargetProperty, BindingPattern, ChainElement, Class, ClassElement, Declaration, ExportDefaultDeclarationKind, Expression, ForStatementInit, ForStatementLeft, Function, ImportDeclarationSpecifier, ModuleExportName, ObjectAssignmentTarget, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement, diff --git a/crates/oxide_compiler/src/hash/target.rs b/crates/oxide_compiler/src/hash/target.rs index b9f11e52..78cf707e 100644 --- a/crates/oxide_compiler/src/hash/target.rs +++ b/crates/oxide_compiler/src/hash/target.rs @@ -13,14 +13,20 @@ pub(super) fn hash_simple_assignment_target( } SimpleAssignmentTarget::StaticMemberExpression(member) => { expression::hash_expression(&member.object, h, include_binding_names); + // 属性名是字节码依赖(发射侧写入常量池作 IC_SET 等键);optional 与 + // 读侧口径对齐(写侧发射当前忽略该标志,防未来写侧开始区分时再漏)。 + member.property.name.as_str().hash(h); + member.optional.hash(h); } SimpleAssignmentTarget::ComputedMemberExpression(member) => { expression::hash_expression(&member.object, h, include_binding_names); expression::hash_expression(&member.expression, h, include_binding_names); + member.optional.hash(h); } SimpleAssignmentTarget::PrivateFieldExpression(member) => { expression::hash_expression(&member.object, h, include_binding_names); member.field.name.as_str().hash(h); + member.optional.hash(h); } SimpleAssignmentTarget::TSAsExpression(ts) => { expression::hash_expression(&ts.expression, h, include_binding_names); diff --git a/crates/oxide_compiler/tests/hash_collision_tests.rs b/crates/oxide_compiler/tests/hash_collision_tests.rs index 29d0b1b4..d71d3c5c 100644 --- a/crates/oxide_compiler/tests/hash_collision_tests.rs +++ b/crates/oxide_compiler/tests/hash_collision_tests.rs @@ -227,3 +227,44 @@ fn function_async_generator_flags_distinguished() { // 同源等价回归 assert_eq!(compiled("async function f(){ return 1 }"), compiled("async function f(){ return 1 }")); } + +#[test] +fn template_segment_values_distinguished() { + // 模板段值是字节码依赖(发射侧把 cooked 写入常量池):段值不入哈希时 + // 同键异码,缓存命中复用错误字节码。 + assert_ne!(compiled("`${a}x`"), compiled("`${a}y`")); + assert_ne!(compiled("t`${a}x`"), compiled("t`${a}y`")); + // 段值在两种哈希粒度下都计入(非绑定名输入)。 + assert_ne!(structural("`${a}x`"), structural("`${a}y`")); + // 同源等价回归。 + assert_eq!(compiled("`${a}x`"), compiled("`${a}x`")); +} + +#[test] +fn call_spread_arguments_distinguished() { + assert_ne!(compiled("f(...a)"), compiled("f(...b)")); + assert_ne!(compiled("new F(...a)"), compiled("new F(...b)")); + assert_ne!(compiled("o.m(...a)"), compiled("o.m(...b)")); + // spread 与非 spread 同长同表达式须区分(逐位形态标记)。 + assert_ne!(compiled("f(a, ...b)"), compiled("f(a, b)")); + // spread 形态标记在两种哈希粒度下都计入(标记本身非绑定名输入)。 + assert_ne!(structural("f(...a, 1)"), structural("f(a, 1)")); +} + +#[test] +fn array_spread_elements_distinguished() { + // spread 元素与值元素同位置须区分(形态标记逐位计入)。 + assert_ne!(compiled("[...a]"), compiled("[a]"), "spread vs value element must differ"); + assert_ne!(structural("[...a, 1]"), structural("[a, 1]"), "spread element shape must be in both granularities"); +} + +#[test] +fn assignment_target_member_names_distinguished() { + // 赋值目标静态成员属性名是字节码依赖(发射侧写入常量池):属性名不入哈希时 + // 同键异码。覆盖赋值、自增减与 for 头三个调用点。 + assert_ne!(compiled("a.x = 1"), compiled("a.y = 1")); + assert_ne!(compiled("a.x++"), compiled("a.y++")); + assert_ne!(compiled("for (a.x of iter) {}"), compiled("for (a.y of iter) {}")); + // 属性名在两种哈希粒度下都计入。 + assert_ne!(structural("a.x = 1"), structural("a.y = 1")); +}