From 2f456058c08d6ac8557f14f7979fc2df69f48c96 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 4 Oct 2026 14:04:31 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20var=20=E8=A7=A3=E6=9E=84=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E5=8F=B6=E5=90=8D=E9=80=92=E5=BD=92=E9=A2=84=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E5=85=A5=E6=A7=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 36 +++--- Cargo.toml | 2 +- crates/oxide_emit/src/prepass/declare.rs | 53 +++++--- .../oxide_vm/tests/for_dstr_var_head_tests.rs | 118 ++++++++++++++++++ 4 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 crates/oxide_vm/tests/for_dstr_var_head_tests.rs diff --git a/Cargo.lock b/Cargo.lock index b9d4ca32..752c7662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,7 +1095,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.1.0" +version = "0.6.0" dependencies = [ "chrono", "fast-float", @@ -1120,7 +1120,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.1.0" +version = "0.6.0" dependencies = [ "num-bigint", "oxide_log", @@ -1128,7 +1128,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.1.0" +version = "0.6.0" dependencies = [ "ansi_term", "bumpalo", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.1.0" +version = "0.6.0" dependencies = [ "lru", "oxide_bytecode", @@ -1168,7 +1168,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1187,7 +1187,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1201,7 +1201,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.1.0" +version = "0.6.0" dependencies = [ "num-bigint", "oxc_allocator", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bumpalo", "dashmap", @@ -1240,7 +1240,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.1.0" +version = "0.6.0" dependencies = [ "tracing", "tracing-appender", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1276,7 +1276,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1292,7 +1292,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.1.0" +version = "0.6.0" dependencies = [ "num-bigint", "num-traits", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.1.0" +version = "0.6.0" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 7b21dc14..a2768894 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.1.0" +version = "0.6.0" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/prepass/declare.rs b/crates/oxide_emit/src/prepass/declare.rs index d4ce28f9..7a58d668 100644 --- a/crates/oxide_emit/src/prepass/declare.rs +++ b/crates/oxide_emit/src/prepass/declare.rs @@ -66,6 +66,39 @@ impl Emitter { } } + /// 递归预声明 var 解构 pattern 内的全部绑定名(数组/对象/默认值/rest 叶名), + /// 使解构声明的叶名进入函数/全局 var 预声明集合,入口实例化得以到达它们。 + /// + /// # 边界与前提 + /// - 叶名走 `predeclare_var_name` 漏斗,保留顶层内置名镜像槽特判,与单绑定 + /// 名行为一致;同 scope 既有 var 同名时经 `declare_initialized` 幂等复用槽。 + fn predeclare_var_pattern(&self, pattern: &BindingPattern, ctx: &mut CompileCtx) { + match pattern { + BindingPattern::BindingIdentifier(bi) => { + self.predeclare_var_name(bi.name.as_str(), ctx); + } + BindingPattern::ArrayPattern(ap) => { + for e in ap.elements.iter().flatten() { + self.predeclare_var_pattern(e, ctx); + } + if let Some(rest) = &ap.rest { + self.predeclare_var_pattern(&rest.argument, ctx); + } + } + BindingPattern::ObjectPattern(op) => { + for prop in &op.properties { + self.predeclare_var_pattern(&prop.value, ctx); + } + if let Some(rest) = &op.rest { + self.predeclare_var_pattern(&rest.argument, ctx); + } + } + BindingPattern::AssignmentPattern(ap) => { + self.predeclare_var_pattern(&ap.left, ctx); + } + } + } + /// 预声明语句列表中的全部 `var` 绑定,使先发的提升函数声明能解析它们。 /// 顶层 `var` 名在编译闭包捕获它的函数体时必须可见。 pub(crate) fn predeclare_var_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) { @@ -76,9 +109,7 @@ impl Emitter { continue; } for d in &decl.declarations { - if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { - self.predeclare_var_name(bi.name.as_str(), ctx); - } + self.predeclare_var_pattern(&d.id, ctx); } } Statement::BlockStatement(bs) => self.predeclare_var_declarations(&bs.body, ctx), @@ -98,9 +129,7 @@ impl Emitter { if let Some(oxide_parser::ForStatementInit::VariableDeclaration(decl)) = &fs.init { if matches!(decl.kind, VariableDeclarationKind::Var) { for d in &decl.declarations { - if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { - self.predeclare_var_name(bi.name.as_str(), ctx); - } + self.predeclare_var_pattern(&d.id, ctx); } } } @@ -113,9 +142,7 @@ impl Emitter { if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fi.left { if matches!(decl.kind, VariableDeclarationKind::Var) { for d in &decl.declarations { - if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { - self.predeclare_var_name(bi.name.as_str(), ctx); - } + self.predeclare_var_pattern(&d.id, ctx); } } } @@ -125,9 +152,7 @@ impl Emitter { if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fo.left { if matches!(decl.kind, VariableDeclarationKind::Var) { for d in &decl.declarations { - if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { - self.predeclare_var_name(bi.name.as_str(), ctx); - } + self.predeclare_var_pattern(&d.id, ctx); } } } @@ -159,9 +184,7 @@ impl Emitter { continue; } for d in &decl.declarations { - if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { - self.predeclare_var_name(bi.name.as_str(), ctx); - } + self.predeclare_var_pattern(&d.id, ctx); } } } diff --git a/crates/oxide_vm/tests/for_dstr_var_head_tests.rs b/crates/oxide_vm/tests/for_dstr_var_head_tests.rs new file mode 100644 index 00000000..94c159c0 --- /dev/null +++ b/crates/oxide_vm/tests/for_dstr_var_head_tests.rs @@ -0,0 +1,118 @@ +//! for-in/for-of 解构 `var` 头叶名预声明:解构声明的绑定名须全部进入 +//! 函数/全局 var 预声明集合,入口实例化到达叶名,零迭代后读见 undefined, +//! 不得误写外层同名继承槽、不得取到寄存器残留。 +//! +//! 覆盖:顶层误写主形、外层函数域同名、for-of/for-in 主形与孪生、同 scope +//! 复用绑定、1 迭代覆写、默认值叶、嵌套叶、捕获叶、顶层形、数组/对象 rest、 +//! 深层嵌套叶。 + +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?; + let mut vm = Vm::new(); + vm.run(&Arc::new(module)) +} + +fn truthy(source: &str) { + let result = eval(source).unwrap_or_else(|e| panic!("{source} -> {e}")); + assert!(result.as_bool(), "{source} -> 期望 true,得 {:?}", result); +} + +#[test] +fn top_level_same_name_not_clobbered() { + // 主红形:函数内解构 var 头叶名与顶层 var 同名,零迭代后顶层值不被误写。 + truthy("var x = 5; function f(){ for (var {x} of []) {} } f(); x === 5"); +} + +#[test] +fn outer_function_scope_same_name_is_undefined() { + // 外层函数域同名(仅内层声明、未赋值):叶名建本地槽,读见 undefined。 + truthy("function f(){ function g(){ var x = 5; } for (var {x} of []) {} return typeof x } f() === 'undefined'"); +} + +#[test] +fn for_of_dstr_object_head_zero_iterations_is_undefined() { + // for-of 对象解构头零迭代:叶名本地槽入口实例化,读见 undefined。 + truthy("function f(){ for (var {x} of []) {} return typeof x } f() === 'undefined'"); +} + +#[test] +fn for_in_dstr_array_head_zero_iterations_is_undefined() { + // for-in 数组解构头零迭代(空可枚举):孪生形,读见 undefined。 + truthy("function f(){ for (var [x] in {}) {} return typeof x } f() === 'undefined'"); +} + +#[test] +fn same_scope_reuse_binding_keeps_value() { + // 同 scope 既有 var 同名:叶名经幂等复用同槽,声明点值保留。 + truthy("function f(){ var x = 5; for (var {x} of []) {} return typeof x } f() === 'number'"); +} + +#[test] +fn single_iteration_overwrites_entry_init() { + // 1 迭代覆写:绑定点写入覆盖入口 undefined,读见迭代值。 + truthy("function f(){ for (var {x} of [{x:1}]) {} return typeof x } f() === 'number'"); +} + +#[test] +fn default_value_leaf_is_undefined() { + // 默认值叶零迭代:默认值不触发,读见 undefined。 + truthy("function f(){ for (var {x = 1} of []) {} return typeof x } f() === 'undefined'"); +} + +#[test] +fn nested_leaf_is_undefined() { + // 嵌套对象解构叶(递归深度):读见 undefined。 + truthy("function f(){ for (var {a: {b}} of []) {} return typeof b } f() === 'undefined'"); +} + +#[test] +fn captured_leaf_is_undefined() { + // 捕获叶:闭包经 cell 入口实例化,读见 undefined。 + truthy( + "function f(){ for (var {x} of []) {} var g = function(){ return typeof x }; return g() } f() === 'undefined'", + ); +} + +#[test] +fn top_level_dstr_head_not_clobbered() { + // 顶层形:顶层解构 var 头零迭代,顶层值保留。 + truthy("var x = 5; for (var {x} of []) {} x === 5"); +} + +#[test] +fn array_rest_leaf_is_undefined() { + // 数组解构 rest 叶:元素与 rest 均见 undefined。 + truthy("function f(){ for (var [a, ...b] of []) {} return typeof a + typeof b } f() === 'undefinedundefined'"); +} + +#[test] +fn object_rest_leaf_is_undefined() { + // 对象解构 rest 叶:具名与 rest 均见 undefined。 + truthy("function f(){ for (var {x, ...r} of []) {} return typeof x + typeof r } f() === 'undefinedundefined'"); +} + +#[test] +fn deep_nested_array_leaf_is_undefined() { + // 深层嵌套数组解构叶:内外叶均见 undefined。 + truthy("function f(){ for (var [[a], b] of []) {} return typeof a + typeof b } f() === 'undefinedundefined'"); +} + +#[test] +fn for_in_object_head_zero_iterations_is_undefined() { + // for-in 对象解构头零迭代(空可枚举):读见 undefined。 + truthy("function f(){ for (var {x} in {}) {} return typeof x } f() === 'undefined'"); +} + +#[test] +fn for_of_array_head_zero_iterations_is_undefined() { + // for-of 数组解构头零迭代:读见 undefined。 + truthy("function f(){ for (var [x] of []) {} return typeof x } f() === 'undefined'"); +}