From 4a2bf09f7823056d82036a4eeacf075dfb6533ca 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: Wed, 5 Aug 2026 01:17:22 +0800 Subject: [PATCH] refactor(compiler): remove statement jump count helpers --- crates/oxide_compiler/src/compiler.rs | 18 +++-- .../src/stmt/basic/break_continue.rs | 8 --- .../oxide_compiler/src/stmt/basic/labeled.rs | 18 ----- crates/oxide_compiler/src/stmt/basic/mod.rs | 9 --- crates/oxide_compiler/src/stmt/control.rs | 27 -------- .../oxide_compiler/src/stmt/exception/mod.rs | 7 -- .../src/stmt/exception/try_catch.rs | 67 ++----------------- crates/oxide_compiler/src/stmt/switch.rs | 37 ---------- .../tests/compile_stmt_tests.rs | 12 ++++ 9 files changed, 28 insertions(+), 175 deletions(-) diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 30c6811..86d5c5a 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -331,6 +331,17 @@ impl CompileCtx { }); } + pub(crate) fn emit_try_finally_begin_labeled(&mut self, label: Label) { + let pc = self.bytecode.len(); + self.emit(opcode::encode_try_finally_begin(0)); + self.fixups.push(JumpFixup { + pc, + label, + opcode: OpCode::TRY_FINALLY_BEGIN, + rd: 0, + }); + } + pub(crate) fn count_word(&mut self) { self.projected_pc += 1; } @@ -481,6 +492,7 @@ impl CompileCtx { OpCode::JMP_IF_TRUE => opcode::encode_jmp_if_true(fixup.rd, offset), OpCode::JMP_IF_NULLISH => opcode::encode_jmp_if_nullish(fixup.rd, offset), OpCode::TRY_BEGIN => opcode::encode_try_begin(offset), + OpCode::TRY_FINALLY_BEGIN => opcode::encode_try_finally_begin(offset), _ => return Err(format!("Unsupported fixup opcode {:?}", fixup.opcode)), }; } @@ -1275,17 +1287,11 @@ impl Compiler { pub(crate) fn count_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) { match stmt { - Statement::BreakStatement(_) | Statement::ContinueStatement(_) | Statement::LabeledStatement(_) => { - self.count_basic(stmt, ctx) - } - Statement::IfStatement(_) => self.count_control_domain(stmt, ctx), Statement::WhileStatement(_) | Statement::DoWhileStatement(_) | Statement::ForStatement(_) | Statement::ForInStatement(_) | Statement::ForOfStatement(_) => self.count_iteration_domain(stmt, ctx), - Statement::SwitchStatement(_) => self.count_switch_domain(stmt, ctx), - Statement::TryStatement(_) => self.count_exception_domain(stmt, ctx), _ => {} } } diff --git a/crates/oxide_compiler/src/stmt/basic/break_continue.rs b/crates/oxide_compiler/src/stmt/basic/break_continue.rs index d9f4bd3..1536144 100644 --- a/crates/oxide_compiler/src/stmt/basic/break_continue.rs +++ b/crates/oxide_compiler/src/stmt/basic/break_continue.rs @@ -1,14 +1,6 @@ use crate::compiler::{CompileCtx, Compiler}; impl Compiler { - pub(super) fn count_break_statement(&self, ctx: &mut CompileCtx) { - ctx.projected_pc += 1; - } - - pub(super) fn count_continue_statement(&self, ctx: &mut CompileCtx) { - ctx.projected_pc += 1; - } - pub(crate) fn emit_break_statement( &self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx, ) -> Result, String> { diff --git a/crates/oxide_compiler/src/stmt/basic/labeled.rs b/crates/oxide_compiler/src/stmt/basic/labeled.rs index dd15f4f..f9358b6 100644 --- a/crates/oxide_compiler/src/stmt/basic/labeled.rs +++ b/crates/oxide_compiler/src/stmt/basic/labeled.rs @@ -2,24 +2,6 @@ use crate::compiler::{CompileCtx, Compiler, Label}; use oxide_parser::Statement; impl Compiler { - pub(super) fn count_labeled_statement(&self, stmt: &oxide_parser::LabeledStatement<'_>, ctx: &mut CompileCtx) { - let body_is_loop = matches!( - stmt.body, - Statement::WhileStatement(_) - | Statement::DoWhileStatement(_) - | Statement::ForStatement(_) - | Statement::ForInStatement(_) - | Statement::ForOfStatement(_) - ); - if body_is_loop { - self.count_statement(&stmt.body, ctx); - } else { - let id = ctx.next_label_id(); - self.count_statement(&stmt.body, ctx); - ctx.labels.label_map.insert(Label::LabeledEnd(id), ctx.projected_pc); - } - } - fn is_iteration_statement(stmt: &Statement) -> bool { matches!( stmt, diff --git a/crates/oxide_compiler/src/stmt/basic/mod.rs b/crates/oxide_compiler/src/stmt/basic/mod.rs index 41e33cd..06c691b 100644 --- a/crates/oxide_compiler/src/stmt/basic/mod.rs +++ b/crates/oxide_compiler/src/stmt/basic/mod.rs @@ -15,13 +15,4 @@ impl Compiler { _ => Ok(None), } } - - pub(crate) fn count_basic(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::BreakStatement(_) => self.count_break_statement(ctx), - Statement::ContinueStatement(_) => self.count_continue_statement(ctx), - Statement::LabeledStatement(ls) => self.count_labeled_statement(ls, ctx), - _ => {} - } - } } diff --git a/crates/oxide_compiler/src/stmt/control.rs b/crates/oxide_compiler/src/stmt/control.rs index a98fe3c..279d6e3 100644 --- a/crates/oxide_compiler/src/stmt/control.rs +++ b/crates/oxide_compiler/src/stmt/control.rs @@ -4,27 +4,6 @@ use oxide_bytecode::opcode::{self, OpCode}; use oxide_parser::Statement; impl Compiler { - fn count_if_statement(&self, stmt: &oxide_parser::IfStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let else_label = Label::IfElse(id); - let end_label = Label::IfEnd(id); - - self.count_expression(&stmt.test, ctx); - ctx.projected_pc += 1; - self.count_statement(&stmt.consequent, ctx); - ctx.alloc_reg(); - ctx.projected_pc += 1; - if stmt.alternate.is_some() { - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(else_label, ctx.projected_pc); - if let Some(alt_stmt) = &stmt.alternate { - self.count_statement(alt_stmt, ctx); - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - } - fn emit_if_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::IfStatement(ifs) = stmt else { return Ok(None); @@ -66,12 +45,6 @@ impl Compiler { Ok(Some(result_reg)) } - pub(crate) fn count_control_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - if let Statement::IfStatement(ifs) = stmt { - self.count_if_statement(ifs, ctx); - } - } - pub(crate) fn emit_control_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::IfStatement(_) => self.emit_if_statement(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/exception/mod.rs b/crates/oxide_compiler/src/stmt/exception/mod.rs index 4140599..f9fe0f9 100644 --- a/crates/oxide_compiler/src/stmt/exception/mod.rs +++ b/crates/oxide_compiler/src/stmt/exception/mod.rs @@ -5,13 +5,6 @@ mod throw; mod try_catch; impl Compiler { - pub(crate) fn count_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::TryStatement(ts) => self.count_try_statement(ts, ctx), - _ => {} - } - } - pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/exception/try_catch.rs b/crates/oxide_compiler/src/stmt/exception/try_catch.rs index 43f268f..109fdd6 100644 --- a/crates/oxide_compiler/src/stmt/exception/try_catch.rs +++ b/crates/oxide_compiler/src/stmt/exception/try_catch.rs @@ -3,71 +3,19 @@ use oxide_bytecode::opcode::{self, OpCode}; use oxide_parser::{Statement, VariableDeclarationKind}; impl Compiler { - pub(crate) fn count_try_statement(&self, stmt: &oxide_parser::TryStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let catch_label = Label::CatchBody(id); - let try_end_label = Label::TryEnd(id); - let has_catch = stmt.handler.is_some(); - let has_finally = stmt.finalizer.is_some(); - ctx.alloc_reg(); - if has_finally { - ctx.projected_pc += 1; - } - if has_catch { - ctx.projected_pc += 1; - } - for s in &stmt.block.body { - self.count_statement(s, ctx); - } - ctx.projected_pc += 1; - if has_catch { - ctx.projected_pc += 1; - } - let jmp_needed = has_catch || has_finally; - if jmp_needed { - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(catch_label, ctx.projected_pc); - if let Some(catch) = &stmt.handler { - ctx.push_scope(); - if let Some(param) = &catch.param { - ctx.alloc_reg(); - if let oxide_parser::BindingPattern::BindingIdentifier(_) = ¶m.pattern { - ctx.projected_pc += 1; - } - } - for cs in &catch.body.body { - self.count_statement(cs, ctx); - } - ctx.projected_pc += 1; - ctx.pop_scope(); - } - if let Some(finally) = &stmt.finalizer { - let finally_label = Label::FinallyBody(id); - ctx.labels.label_map.insert(finally_label, ctx.projected_pc); - for fs in &finally.body { - self.count_statement(fs, ctx); - } - ctx.projected_pc += 1; - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(try_end_label, ctx.projected_pc); - } - pub(crate) fn emit_try_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::TryStatement(ts) = stmt else { return Ok(None); }; let id = ctx.next_label_id(); let catch_label = Label::CatchBody(id); + let finally_label = Label::FinallyBody(id); let try_end_label = Label::TryEnd(id); let has_catch = ts.handler.is_some(); let has_finally = ts.finalizer.is_some(); let result_reg = ctx.alloc_reg(); - let mut try_finally_begin_pos: Option = None; if has_finally { - try_finally_begin_pos = Some(ctx.bytecode.len()); - ctx.emit(opcode::encode_try_finally_begin(0)); + ctx.emit_try_finally_begin_labeled(finally_label); } if has_catch { ctx.emit_try_begin_labeled(catch_label); @@ -84,7 +32,7 @@ impl Compiler { } let jmp_needed = has_catch || has_finally; if jmp_needed { - let target = if has_finally { Label::FinallyBody(id) } else { try_end_label }; + let target = if has_finally { finally_label } else { try_end_label }; ctx.emit_jmp_labeled(target); } let catch_label_pc = ctx.bytecode.len(); @@ -108,14 +56,7 @@ impl Compiler { ctx.pop_scope(); } if has_finally { - let finally_label = Label::FinallyBody(id); - let finally_label_pc = ctx.bytecode.len(); - ctx.labels.label_map.insert(finally_label, finally_label_pc); - if let Some(fb_pos) = try_finally_begin_pos { - let offset = finally_label_pc as isize - (fb_pos as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.bytecode[fb_pos] = opcode::encode_try_finally_begin(offset); - } + ctx.labels.label_map.insert(finally_label, ctx.bytecode.len()); let mut last_finally_result: Option = None; for s in &ts.finalizer.as_ref().unwrap().body { if let Some(r) = self.emit_statement(s, ctx)? { diff --git a/crates/oxide_compiler/src/stmt/switch.rs b/crates/oxide_compiler/src/stmt/switch.rs index d9f075e..d249428 100644 --- a/crates/oxide_compiler/src/stmt/switch.rs +++ b/crates/oxide_compiler/src/stmt/switch.rs @@ -3,37 +3,6 @@ use oxide_bytecode::opcode::{self, OpCode}; use oxide_parser::Statement; impl Compiler { - fn count_switch_statement(&self, stmt: &oxide_parser::SwitchStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let end_label = Label::SwitchEnd(id); - ctx.push_switch(end_label); - self.count_expression(&stmt.discriminant, ctx); - let compare_reg_checkpoint = ctx.reg_checkpoint(); - let cases = &stmt.cases; - for case in cases.iter() { - if let Some(test) = &case.test { - self.count_expression(test, ctx); - ctx.projected_pc += 1; - ctx.alloc_reg(); - ctx.projected_pc += 1; - ctx.restore_reg_checkpoint(compare_reg_checkpoint); - } - } - let has_default = cases.iter().any(|c| c.test.is_none()); - if !has_default { - ctx.projected_pc += 1; - } - for (case_idx, case) in cases.iter().enumerate() { - let case_label = Label::SwitchCase(id, case_idx as u32); - ctx.labels.label_map.insert(case_label, ctx.projected_pc); - for s in &case.consequent { - self.count_statement(s, ctx); - } - } - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - ctx.pop_switch(); - } - fn emit_switch_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::SwitchStatement(sw) = stmt else { return Ok(None); @@ -70,12 +39,6 @@ impl Compiler { Ok(None) } - pub(crate) fn count_switch_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - if let Statement::SwitchStatement(sw) = stmt { - self.count_switch_statement(sw, ctx); - } - } - pub(crate) fn emit_switch_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::SwitchStatement(_) => self.emit_switch_statement(stmt, ctx), diff --git a/crates/oxide_compiler/tests/compile_stmt_tests.rs b/crates/oxide_compiler/tests/compile_stmt_tests.rs index 1e2632a..9138070 100644 --- a/crates/oxide_compiler/tests/compile_stmt_tests.rs +++ b/crates/oxide_compiler/tests/compile_stmt_tests.rs @@ -218,6 +218,18 @@ fn compile_switch_break() { assert!(!module.bytecode.is_empty()); } +#[test] +fn compile_try_finally_resolves_finally_offset() { + let module = compile_source("try { 1; } finally { 2; }"); + let try_finally_begin = module + .bytecode + .iter() + .copied() + .find(|&instr| opcode::opcode(instr) == OpCode::TRY_FINALLY_BEGIN) + .expect("try/finally should emit TRY_FINALLY_BEGIN"); + assert_ne!(opcode::offset16(try_finally_begin), 0, "finally target offset should be resolved"); +} + #[test] fn compile_continue_in_switch_errors() { let result = std::panic::catch_unwind(|| {