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!(