diff --git a/Cargo.lock b/Cargo.lock index 9251e5a7..f185228f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.19.37" +version = "0.19.38" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.19.37" +version = "0.19.38" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.19.37" +version = "0.19.38" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.19.37" +version = "0.19.38" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.19.37" +version = "0.19.38" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.19.37" +version = "0.19.38" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.19.37" +version = "0.19.38" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.19.37" +version = "0.19.38" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.19.37" +version = "0.19.38" [[package]] name = "oxide_parser" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.19.37" +version = "0.19.38" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.19.37" +version = "0.19.38" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.19.37" +version = "0.19.38" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.19.37" +version = "0.19.38" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 5b7ea1a8..f323fe73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.19.37" +version = "0.19.38" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index 5f8a7265..b7801e46 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -954,6 +954,14 @@ impl CompileCtx { NON_WRITABLE_GLOBAL_BUILTINS.contains(&name) } + /// 早期错误跳过谓词(Annex B.3.3.3 对 EvalDeclarationInstantiation 的增补): + /// sloppy eval 代码内块函数声明的名若与同树词法声明/形参同名,替换为变量 + /// 语句即产生早期错误,规范不为其建任何绑定——函数体只求值副作用,不写块 + /// Let、不写外层 var、不写全局对象属性。预声明门与存储臂共用本谓词。 + pub(crate) fn is_eval_early_error_suppressed(&self, name: &str) -> bool { + self.is_eval_script && !self.is_strict && self.block_fn_suppressed.contains(name) + } + /// 进入 with 语句体:记录对象寄存器与当前作用域深度,供动态标识符解析。 pub(crate) fn push_with(&mut self, obj_reg: u32) { let depth = self.scopes.symbols.scopes.len(); diff --git a/crates/oxide_emit/src/prepass/declare.rs b/crates/oxide_emit/src/prepass/declare.rs index 225bd8e9..f2869f86 100644 --- a/crates/oxide_emit/src/prepass/declare.rs +++ b/crates/oxide_emit/src/prepass/declare.rs @@ -230,7 +230,13 @@ impl Emitter { let Some(identifier) = &f.id else { continue; }; - if if_arm && Self::if_arm_has_outer_var_carrier(ctx, identifier.name.as_str()) { + // 早期错误跳过(Annex B.3.3.3):sloppy eval 代码内支臂函数名撞 + // 同树词法声明时不建块 Let 绑定,使词法预声明不撞名报重复声明 + // 错(函数体只求值副作用,不写任何绑定)。 + if if_arm + && (Self::if_arm_has_outer_var_carrier(ctx, identifier.name.as_str()) + || ctx.is_eval_early_error_suppressed(identifier.name.as_str())) + { continue; } let reg = ctx.alloc_reg(); diff --git a/crates/oxide_emit/src/stmt/declaration/function_decl.rs b/crates/oxide_emit/src/stmt/declaration/function_decl.rs index a53dafee..ed822d1d 100644 --- a/crates/oxide_emit/src/stmt/declaration/function_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/function_decl.rs @@ -40,10 +40,16 @@ impl Emitter { // 其余 sub-pass 发的函数声明皆为块级(if/while/for/switch/try 支臂与块直接子)。 let is_block_function = !ctx.in_hoisting_subpass; let var_reg = if is_block_function { - match ctx.block_fn_entry_mats.last().and_then(|m| m.get(&name)) { - Some(&m) if m == node => ctx.lookup(&name)?, - Some(_) => self.materialize_function_declaration_var_only(fd, &name, ctx, true)?, - None => self.materialize_function_declaration(fd, &name, ctx, true)?, + if ctx.is_eval_early_error_suppressed(&name) { + // 早期错误跳过(Annex B.3.3.3):不写入任何绑定,函数体只求值 + // 副作用(CREATE_CLOSURE 照发,结果寄存器丢弃,不发 STORE_VAR)。 + self.materialize_function_declaration_var_only(fd, &name, ctx, true)? + } else { + match ctx.block_fn_entry_mats.last().and_then(|m| m.get(&name)) { + Some(&m) if m == node => ctx.lookup(&name)?, + Some(_) => self.materialize_function_declaration_var_only(fd, &name, ctx, true)?, + None => self.materialize_function_declaration(fd, &name, ctx, true)?, + } } } else { self.materialize_function_declaration(fd, &name, ctx, false)? diff --git a/crates/oxide_emit/tests/emit_ir_tests.rs b/crates/oxide_emit/tests/emit_ir_tests.rs index 0d3c934c..4041db79 100644 --- a/crates/oxide_emit/tests/emit_ir_tests.rs +++ b/crates/oxide_emit/tests/emit_ir_tests.rs @@ -411,6 +411,31 @@ fn block_fn_suppressed_by_destructured_param_leaf() { ); } +/// 面 22:Annex B.3.3.3 早期错误跳过——sloppy eval 代码内块函数名撞同树词法 +/// 声明时编译不报重复声明错,且函数声明不覆写词法绑定(STORE_VAR 少一条写回)。 +#[test] +fn eval_early_error_skip_no_binding() { + // base 形:碰撞在 eval 顶层 let。编译成功且函数不覆写词法绑定。 + let alloc = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&alloc, "let f = 123; if (true) function f() {}").unwrap(); + let ir = Emitter::new() + .emit_program(&program, false, true) + .unwrap_or_else(|e| panic!("base 形应编译成功:{e}")); + // `let f = 123` 初始化写(1)+ 函数声明不覆写词法绑定(0)= 1。 + assert_eq!( + count_op(&ir.insts, OpCode::STORE_VAR), + 1, + "早期错误跳过:函数声明不覆写词法绑定" + ); + + // -block 形:碰撞在同块 let。编译不报「Identifier has already been declared」。 + let alloc = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&alloc, "{ let f = 123; if (true) function f() {} }").unwrap(); + let _ir = Emitter::new() + .emit_program(&program, false, true) + .unwrap_or_else(|e| panic!("-block 形应编译成功:{e}")); +} + /// 面 14:双常量折叠入口结构回归——双字面量算术编译后目标操作码计数为 0、 /// 常量池含折值,锁死折叠入口不随后续重构丢失。 #[test]