From 05b149b275676df1a49d11f1d2d41335aeb2f3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Tue, 30 Jun 2026 16:01:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A1=B6=E5=B1=82=20count=20pass=20?= =?UTF-8?q?=E9=95=9C=E5=83=8F=20emit=20=E7=9A=84=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E9=A1=BA=E5=BA=8F=EF=BC=8C=E6=B6=88=E9=99=A4?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E7=9B=AE=E6=A0=87=E6=BC=82=E7=A7=BB=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_compiler/src/compiler.rs | 13 +++++++++++- .../tests/compiler_alignment_expr_tests.rs | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 5bd699c..e999f65 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -866,8 +866,19 @@ impl Compiler { let mut ctx = CompileCtx::new(); ctx.pre_register_builtins(); + // The emit pass below hoists FunctionDeclarations to the front. The count pass + // must walk in that same order: counting in source order would record jump-target + // labels at source-order PCs while emit places code at hoisted-order PCs, drifting + // every jump that sits after a hoisted function declaration (infinite loops at run). for stmt in &program.body { - self.count_statement(stmt, &mut ctx); + if matches!(stmt, Statement::FunctionDeclaration(_)) { + self.count_statement(stmt, &mut ctx); + } + } + for stmt in &program.body { + if !matches!(stmt, Statement::FunctionDeclaration(_)) { + self.count_statement(stmt, &mut ctx); + } } crate::compiler_debug!("counter: {} instructions estimated", ctx.projected_pc); ctx.max_regs = ctx.max_regs.max(1); diff --git a/crates/oxide_vm/tests/compiler_alignment_expr_tests.rs b/crates/oxide_vm/tests/compiler_alignment_expr_tests.rs index 5d8a3e8..050eeb6 100644 --- a/crates/oxide_vm/tests/compiler_alignment_expr_tests.rs +++ b/crates/oxide_vm/tests/compiler_alignment_expr_tests.rs @@ -38,6 +38,26 @@ fn coalesce_nonsimple_then_while() { ); } +// A jump-bearing construct (if/loop) followed by a hoisted function declaration: +// the count pass must walk in the same function-hoisting order as the emit pass, +// otherwise the construct's jump targets drift past the hoisted body and the +// program loops forever at run time. + +#[test] +fn if_then_hoisted_function() { + assert_eq!(eval("if(false){} function f(){return 1} f()"), "1"); +} + +#[test] +fn for_of_then_hoisted_function() { + assert_eq!(eval("var r=0; for(const x of [1,2,3]) r+=x; function f(){return 100} r + f()"), "106"); +} + +#[test] +fn while_then_hoisted_function() { + assert_eq!(eval("var n=0; while(n<3){n=n+1;} function f(){return 100} n + f()"), "103"); +} + #[test] fn coalesce_nonsimple_then_for() { assert_eq!(