From fd11b7636a594b4d4ecc1ef79a98717a105b11df 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, 23 Jun 2026 17:45:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8B=86=E5=88=86=20counter=20stat?= =?UTF-8?q?ement=20=E8=AE=A1=E6=95=B0=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oxide_compiler/src/counter/stmt/basic.rs | 26 ++ .../oxide_compiler/src/counter/stmt/block.rs | 11 + .../src/counter/stmt/control.rs | 24 ++ .../src/counter/stmt/declaration.rs | 39 +++ .../src/counter/stmt/exception.rs | 68 ++++ .../src/counter/stmt/iteration.rs | 143 ++++++++ crates/oxide_compiler/src/counter/stmt/mod.rs | 319 ++---------------- .../oxide_compiler/src/counter/stmt/switch.rs | 41 +++ 8 files changed, 377 insertions(+), 294 deletions(-) create mode 100644 crates/oxide_compiler/src/counter/stmt/basic.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/block.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/control.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/declaration.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/exception.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/iteration.rs create mode 100644 crates/oxide_compiler/src/counter/stmt/switch.rs diff --git a/crates/oxide_compiler/src/counter/stmt/basic.rs b/crates/oxide_compiler/src/counter/stmt/basic.rs new file mode 100644 index 0000000..5412995 --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/basic.rs @@ -0,0 +1,26 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) fn count_expression_statement( + &self, stmt: &oxide_parser::ExpressionStatement<'_>, ctx: &mut CompileCtx, + ) { + self.count_expression(&stmt.expression, ctx); + } + + pub(in crate::counter) fn count_return_statement( + &self, stmt: &oxide_parser::ReturnStatement<'_>, ctx: &mut CompileCtx, + ) { + if let Some(arg) = &stmt.argument { + self.count_expression(arg, ctx); + } + ctx.projected_pc += 1; // RETURN + } + + pub(in crate::counter) fn count_break_statement(&self, ctx: &mut CompileCtx) { + ctx.projected_pc += 1; // JMP + } + + pub(in crate::counter) fn count_continue_statement(&self, ctx: &mut CompileCtx) { + ctx.projected_pc += 1; // JMP + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/block.rs b/crates/oxide_compiler/src/counter/stmt/block.rs new file mode 100644 index 0000000..b019998 --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/block.rs @@ -0,0 +1,11 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) fn count_block_statement( + &self, block: &oxide_parser::BlockStatement<'_>, ctx: &mut CompileCtx, + ) { + for s in &block.body { + self.count_statement(s, ctx); + } + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/control.rs b/crates/oxide_compiler/src/counter/stmt/control.rs new file mode 100644 index 0000000..2d3a61a --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/control.rs @@ -0,0 +1,24 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) 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; // JMP_IF_FALSE + self.count_statement(&stmt.consequent, ctx); + ctx.alloc_reg(); // result register + ctx.projected_pc += 1; // LOAD_VAR result <- consequent + if stmt.alternate.is_some() { + ctx.projected_pc += 1; // JMP (skip else) + } + ctx.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; // LOAD_VAR result <- alternate + } + ctx.label_map.insert(end_label, ctx.projected_pc); + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/declaration.rs b/crates/oxide_compiler/src/counter/stmt/declaration.rs new file mode 100644 index 0000000..6863704 --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/declaration.rs @@ -0,0 +1,39 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) fn count_variable_declaration( + &self, decl: &oxide_parser::VariableDeclaration<'_>, ctx: &mut CompileCtx, + ) { + for d in &decl.declarations { + if let Some(init) = &d.init { + self.count_expression(init, ctx); + self.count_binding_pattern(&d.id, ctx); + } else { + ctx.alloc_reg(); + ctx.count_words(2); // LOAD_CONST(undefined) + STORE_VAR + } + } + } + + pub(in crate::counter) fn count_function_declaration( + &self, decl: &oxide_parser::Function<'_>, ctx: &mut CompileCtx, + ) { + let name = if let Some(id) = &decl.id { + id.name.to_string() + } else { + return; + }; + + let func_reg = ctx.alloc_reg(); + let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); + + // Body is compiled in the emit pass only. + ctx.count_words(2); // LOAD_CONST(BytecodeFunc) + STORE_VAR + } + + pub(in crate::counter) fn count_class_declaration(&self, class: &oxide_parser::Class<'_>, ctx: &mut CompileCtx) { + ctx.alloc_reg(); // class binding reg + self.count_class(class, ctx); + ctx.projected_pc += 1; // STORE_VAR binding <- ctor + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/exception.rs b/crates/oxide_compiler/src/counter/stmt/exception.rs new file mode 100644 index 0000000..62c5351 --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/exception.rs @@ -0,0 +1,68 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) fn count_throw_statement( + &self, stmt: &oxide_parser::ThrowStatement<'_>, ctx: &mut CompileCtx, + ) { + self.count_expression(&stmt.argument, ctx); + ctx.projected_pc += 1; // THROW + } + + pub(in crate::counter) 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(); // result_reg + + if has_finally { + ctx.projected_pc += 1; // TRY_FINALLY_BEGIN (before try body) + } + + if has_catch { + ctx.projected_pc += 1; // TRY_BEGIN (before try body) + } + + for s in &stmt.block.body { + self.count_statement(s, ctx); + } + ctx.projected_pc += 1; // LOAD_VAR result_reg (if try body has result) + + if has_catch { + ctx.projected_pc += 1; // TRY_END + } + + let jmp_needed = has_catch || has_finally; + if jmp_needed { + ctx.projected_pc += 1; // JMP + } + + ctx.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(); + ctx.projected_pc += 1; // STORE_VAR + } + for cs in &catch.body.body { + self.count_statement(cs, ctx); + } + ctx.projected_pc += 1; // LOAD_VAR result_reg (if catch body has result) + ctx.pop_scope(); + } + + if let Some(finally) = &stmt.finalizer { + let finally_label = Label::FinallyBody(id); + ctx.label_map.insert(finally_label, ctx.projected_pc); + for fs in &finally.body { + self.count_statement(fs, ctx); + } + ctx.projected_pc += 1; // LOAD_VAR result_reg (if finally has result) + ctx.projected_pc += 1; // TRY_FINALLY_END + } + + ctx.label_map.insert(try_end_label, ctx.projected_pc); + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/iteration.rs b/crates/oxide_compiler/src/counter/stmt/iteration.rs new file mode 100644 index 0000000..83237ea --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/iteration.rs @@ -0,0 +1,143 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) fn count_while_statement( + &self, stmt: &oxide_parser::WhileStatement<'_>, ctx: &mut CompileCtx, + ) { + let id = ctx.next_label_id(); + let start_label = Label::WhileStart(id); + let end_label = Label::WhileEnd(id); + + ctx.label_map.insert(start_label, ctx.projected_pc); + self.count_expression(&stmt.test, ctx); + ctx.projected_pc += 1; // JMP_IF_FALSE + self.count_statement(&stmt.body, ctx); + ctx.projected_pc += 1; // JMP (backward) + ctx.label_map.insert(end_label, ctx.projected_pc); + } + + pub(in crate::counter) fn count_do_while_statement( + &self, stmt: &oxide_parser::DoWhileStatement<'_>, ctx: &mut CompileCtx, + ) { + let id = ctx.next_label_id(); + let start_label = Label::DoWhileStart(id); + let end_label = Label::DoWhileEnd(id); + + ctx.label_map.insert(start_label, ctx.projected_pc); + self.count_statement(&stmt.body, ctx); + self.count_expression(&stmt.test, ctx); + ctx.projected_pc += 1; // JMP_IF_TRUE (backward) + ctx.label_map.insert(end_label, ctx.projected_pc); + } + + pub(in crate::counter) fn count_for_statement(&self, stmt: &oxide_parser::ForStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::ForStart(id); + let update_label = Label::ForUpdate(id); + let end_label = Label::ForEnd(id); + + if let Some(init) = &stmt.init { + if let Some(expr) = init.as_expression() { + self.count_expression(expr, ctx); + } else if let ForStatementInit::VariableDeclaration(decl) = init { + for d in &decl.declarations { + if let Some(init_expr) = &d.init { + self.count_expression(init_expr, ctx); + self.count_binding_pattern(&d.id, ctx); + } else { + ctx.alloc_reg(); + ctx.count_words(2); // LOAD_CONST(undefined) + STORE_VAR + } + } + } + } + ctx.label_map.insert(start_label, ctx.projected_pc); + if let Some(test) = &stmt.test { + self.count_expression(test, ctx); + ctx.projected_pc += 1; // JMP_IF_FALSE + } + self.count_statement(&stmt.body, ctx); + ctx.label_map.insert(update_label, ctx.projected_pc); + if let Some(update) = &stmt.update { + self.count_expression(update, ctx); + } + ctx.projected_pc += 1; // JMP (backward) + ctx.label_map.insert(end_label, ctx.projected_pc); + } + + pub(in crate::counter) fn count_for_in_statement( + &self, stmt: &oxide_parser::ForInStatement<'_>, ctx: &mut CompileCtx, + ) { + let id = ctx.next_label_id(); + let start_label = Label::ForInStart(id); + let end_label = Label::ForInEnd(id); + + self.count_expression(&stmt.right, ctx); + ctx.count_instr(); // FOR_IN_INIT + + ctx.label_map.insert(start_label, ctx.projected_pc); + ctx.count_instr(); // FOR_IN_DONE + ctx.count_jump(); // JMP_IF_FALSE + ctx.count_jump(); // JMP cleanup + + ctx.count_instr(); // FOR_IN_NEXT + match &stmt.left { + oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { + for _d in &decl.declarations { + ctx.alloc_reg(); + ctx.count_instr(); // STORE_VAR + } + } + oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { + ctx.alloc_reg(); // key register + ctx.count_instr(); // STORE_VAR + } + _ => {} + } + + self.count_statement(&stmt.body, ctx); + ctx.count_jump(); // JMP back to start + + ctx.label_map.insert(end_label, ctx.projected_pc); + ctx.count_instr(); // FOR_IN_CLEANUP + } + + pub(in crate::counter) fn count_for_of_statement( + &self, stmt: &oxide_parser::ForOfStatement<'_>, ctx: &mut CompileCtx, + ) { + let id = ctx.next_label_id(); + let start_label = Label::ForOfStart(id); + let end_label = Label::ForOfEnd(id); + + self.count_expression(&stmt.right, ctx); + ctx.count_instr(); // FOR_OF_INIT + ctx.label_map.insert(start_label, ctx.projected_pc); + ctx.alloc_reg(); // has_reg + ctx.count_instr(); // FOR_OF_DONE + ctx.count_jump(); // JMP_IF_FALSE + ctx.alloc_reg(); // val_reg + ctx.count_instr(); // FOR_OF_NEXT + match &stmt.left { + oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { + for d in &decl.declarations { + self.count_binding_pattern(&d.id, ctx); + } + } + oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { + ctx.alloc_reg(); + ctx.count_instr(); // STORE_VAR + } + oxide_parser::ForStatementLeft::ArrayAssignmentTarget(ap) => { + self.count_array_assignment(ap, ctx); + } + oxide_parser::ForStatementLeft::ObjectAssignmentTarget(op) => { + self.count_object_assignment(op, ctx); + } + _ => {} + } + self.count_statement(&stmt.body, ctx); + ctx.count_jump(); // JMP back + ctx.label_map.insert(end_label, ctx.projected_pc); + ctx.count_instr(); // FOR_OF_CLOSE + } +} diff --git a/crates/oxide_compiler/src/counter/stmt/mod.rs b/crates/oxide_compiler/src/counter/stmt/mod.rs index 7157f80..71b79ca 100644 --- a/crates/oxide_compiler/src/counter/stmt/mod.rs +++ b/crates/oxide_compiler/src/counter/stmt/mod.rs @@ -1,302 +1,33 @@ use super::*; +mod basic; +mod block; +mod control; +mod declaration; +mod exception; +mod iteration; +mod switch; + impl Compiler { pub(crate) fn count_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) { match stmt { - Statement::ExpressionStatement(es) => { - self.count_expression(&es.expression, ctx); - } - Statement::VariableDeclaration(decl) => { - for d in &decl.declarations { - if let Some(init) = &d.init { - self.count_expression(init, ctx); - self.count_binding_pattern(&d.id, ctx); - } else { - ctx.alloc_reg(); - ctx.count_words(2); // LOAD_CONST(undefined) + STORE_VAR - } - } - } - Statement::ReturnStatement(ret) => { - if let Some(arg) = &ret.argument { - self.count_expression(arg, ctx); - } - ctx.projected_pc += 1; // RETURN - } - Statement::IfStatement(ifs) => { - let id = ctx.next_label_id(); - let else_label = Label::IfElse(id); - let end_label = Label::IfEnd(id); - - self.count_expression(&ifs.test, ctx); - ctx.projected_pc += 1; // JMP_IF_FALSE - self.count_statement(&ifs.consequent, ctx); - ctx.alloc_reg(); // result register - ctx.projected_pc += 1; // LOAD_VAR result <- consequent - if ifs.alternate.is_some() { - ctx.projected_pc += 1; // JMP (skip else) - } - ctx.label_map.insert(else_label, ctx.projected_pc); - if let Some(alt_stmt) = &ifs.alternate { - self.count_statement(alt_stmt, ctx); - ctx.projected_pc += 1; // LOAD_VAR result <- alternate - } - ctx.label_map.insert(end_label, ctx.projected_pc); - } - Statement::WhileStatement(wh) => { - let id = ctx.next_label_id(); - let start_label = Label::WhileStart(id); - let end_label = Label::WhileEnd(id); - - ctx.label_map.insert(start_label, ctx.projected_pc); - self.count_expression(&wh.test, ctx); - ctx.projected_pc += 1; // JMP_IF_FALSE - self.count_statement(&wh.body, ctx); - ctx.projected_pc += 1; // JMP (backward) - ctx.label_map.insert(end_label, ctx.projected_pc); - } - Statement::DoWhileStatement(dw) => { - let id = ctx.next_label_id(); - let start_label = Label::DoWhileStart(id); - let end_label = Label::DoWhileEnd(id); - - ctx.label_map.insert(start_label, ctx.projected_pc); - self.count_statement(&dw.body, ctx); - self.count_expression(&dw.test, ctx); - ctx.projected_pc += 1; // JMP_IF_TRUE (backward) - ctx.label_map.insert(end_label, ctx.projected_pc); - } - Statement::ForStatement(fr) => { - let id = ctx.next_label_id(); - let start_label = Label::ForStart(id); - let update_label = Label::ForUpdate(id); - let end_label = Label::ForEnd(id); - - if let Some(init) = &fr.init { - if let Some(expr) = init.as_expression() { - self.count_expression(expr, ctx); - } else if let ForStatementInit::VariableDeclaration(decl) = init { - for d in &decl.declarations { - if let Some(init_expr) = &d.init { - self.count_expression(init_expr, ctx); - self.count_binding_pattern(&d.id, ctx); - } else { - ctx.alloc_reg(); - ctx.count_words(2); // LOAD_CONST(undefined) + STORE_VAR - } - } - } - } - ctx.label_map.insert(start_label, ctx.projected_pc); - if let Some(test) = &fr.test { - self.count_expression(test, ctx); - ctx.projected_pc += 1; // JMP_IF_FALSE - } - self.count_statement(&fr.body, ctx); - ctx.label_map.insert(update_label, ctx.projected_pc); - if let Some(update) = &fr.update { - self.count_expression(update, ctx); - } - ctx.projected_pc += 1; // JMP (backward) - ctx.label_map.insert(end_label, ctx.projected_pc); - } - Statement::ForInStatement(fi) => { - let id = ctx.next_label_id(); - let start_label = Label::ForInStart(id); - let end_label = Label::ForInEnd(id); - - self.count_expression(&fi.right, ctx); - ctx.count_instr(); // FOR_IN_INIT - - ctx.label_map.insert(start_label, ctx.projected_pc); - ctx.count_instr(); // FOR_IN_DONE - ctx.count_jump(); // JMP_IF_FALSE - ctx.count_jump(); // JMP cleanup - - ctx.count_instr(); // FOR_IN_NEXT - match &fi.left { - oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { - for _d in &decl.declarations { - ctx.alloc_reg(); - ctx.count_instr(); // STORE_VAR - } - } - oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { - ctx.alloc_reg(); // key register - ctx.count_instr(); // STORE_VAR - } - _ => {} - } - - self.count_statement(&fi.body, ctx); - ctx.count_jump(); // JMP back to start - - ctx.label_map.insert(end_label, ctx.projected_pc); - ctx.count_instr(); // FOR_IN_CLEANUP - } - Statement::ForOfStatement(fo) => { - let id = ctx.next_label_id(); - let start_label = Label::ForOfStart(id); - let end_label = Label::ForOfEnd(id); - - self.count_expression(&fo.right, ctx); - ctx.count_instr(); // FOR_OF_INIT - ctx.label_map.insert(start_label, ctx.projected_pc); - ctx.alloc_reg(); // has_reg - ctx.count_instr(); // FOR_OF_DONE - ctx.count_jump(); // JMP_IF_FALSE - ctx.alloc_reg(); // val_reg - ctx.count_instr(); // FOR_OF_NEXT - match &fo.left { - oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { - for d in &decl.declarations { - self.count_binding_pattern(&d.id, ctx); - } - } - oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { - ctx.alloc_reg(); - ctx.count_instr(); // STORE_VAR - } - oxide_parser::ForStatementLeft::ArrayAssignmentTarget(ap) => { - self.count_array_assignment(ap, ctx); - } - oxide_parser::ForStatementLeft::ObjectAssignmentTarget(op) => { - self.count_object_assignment(op, ctx); - } - _ => {} - } - self.count_statement(&fo.body, ctx); - ctx.count_jump(); // JMP back - ctx.label_map.insert(end_label, ctx.projected_pc); - ctx.count_instr(); // FOR_OF_CLOSE - } - Statement::SwitchStatement(sw) => { - let id = ctx.next_label_id(); - let end_label = Label::SwitchEnd(id); - ctx.push_switch(end_label); - - self.count_expression(&sw.discriminant, ctx); - let compare_reg_checkpoint = ctx.reg_checkpoint(); - - let cases = &sw.cases; - for case in cases.iter() { - if let Some(test) = &case.test { - self.count_expression(test, ctx); - ctx.projected_pc += 1; // EQ - ctx.alloc_reg(); // eq result - ctx.projected_pc += 1; // JMP_IF_TRUE - 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; // JMP to SwitchEnd (no match) - } - - for (case_idx, case) in cases.iter().enumerate() { - let case_label = Label::SwitchCase(id, case_idx as u32); - ctx.label_map.insert(case_label, ctx.projected_pc); - for s in &case.consequent { - self.count_statement(s, ctx); - } - } - - ctx.label_map.insert(end_label, ctx.projected_pc); - ctx.pop_switch(); - } - Statement::BreakStatement(_) => { - ctx.projected_pc += 1; // JMP - } - Statement::ContinueStatement(_) => { - ctx.projected_pc += 1; // JMP - } - Statement::BlockStatement(block) => { - for s in &block.body { - self.count_statement(s, ctx); - } - } - Statement::FunctionDeclaration(fd) => { - let name = if let Some(id) = &fd.id { - id.name.to_string() - } else { - return; - }; - - // Hoisting: declare function name as initialized - let func_reg = ctx.alloc_reg(); - let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); - - // Body is compiled in the emit pass only. - // FD emits LOAD_CONST(BytecodeFunc) + STORE_VAR - ctx.count_words(2); - } - Statement::ClassDeclaration(class) => { - ctx.alloc_reg(); // class binding reg - self.count_class(class, ctx); - ctx.projected_pc += 1; // STORE_VAR binding <- ctor - } - Statement::ThrowStatement(ts) => { - self.count_expression(&ts.argument, ctx); - ctx.projected_pc += 1; // THROW - } - Statement::TryStatement(ts) => { - let id = ctx.next_label_id(); - let catch_label = Label::CatchBody(id); - let try_end_label = Label::TryEnd(id); - let has_catch = ts.handler.is_some(); - let has_finally = ts.finalizer.is_some(); - - ctx.alloc_reg(); // result_reg - - if has_finally { - ctx.projected_pc += 1; // TRY_FINALLY_BEGIN (before try body) - } - - if has_catch { - ctx.projected_pc += 1; // TRY_BEGIN (before try body) - } - - for s in &ts.block.body { - self.count_statement(s, ctx); - } - ctx.projected_pc += 1; // LOAD_VAR result_reg (if try body has result) - - if has_catch { - ctx.projected_pc += 1; // TRY_END - } - - let jmp_needed = has_catch || has_finally; - if jmp_needed { - ctx.projected_pc += 1; // JMP - } - - ctx.label_map.insert(catch_label, ctx.projected_pc); - if let Some(catch) = &ts.handler { - ctx.push_scope(); - if let Some(_param) = &catch.param { - ctx.alloc_reg(); - ctx.projected_pc += 1; // STORE_VAR - } - for cs in &catch.body.body { - self.count_statement(cs, ctx); - } - ctx.projected_pc += 1; // LOAD_VAR result_reg (if catch body has result) - ctx.pop_scope(); - } - - if let Some(finally) = &ts.finalizer { - let finally_label = Label::FinallyBody(id); - ctx.label_map.insert(finally_label, ctx.projected_pc); - for fs in &finally.body { - self.count_statement(fs, ctx); - } - ctx.projected_pc += 1; // LOAD_VAR result_reg (if finally has result) - ctx.projected_pc += 1; // TRY_FINALLY_END - } - - ctx.label_map.insert(try_end_label, ctx.projected_pc); - } + Statement::ExpressionStatement(es) => self.count_expression_statement(es, ctx), + Statement::VariableDeclaration(decl) => self.count_variable_declaration(decl, ctx), + Statement::ReturnStatement(ret) => self.count_return_statement(ret, ctx), + Statement::IfStatement(ifs) => self.count_if_statement(ifs, ctx), + Statement::WhileStatement(wh) => self.count_while_statement(wh, ctx), + Statement::DoWhileStatement(dw) => self.count_do_while_statement(dw, ctx), + Statement::ForStatement(fr) => self.count_for_statement(fr, ctx), + Statement::ForInStatement(fi) => self.count_for_in_statement(fi, ctx), + Statement::ForOfStatement(fo) => self.count_for_of_statement(fo, ctx), + Statement::SwitchStatement(sw) => self.count_switch_statement(sw, ctx), + Statement::BreakStatement(_) => self.count_break_statement(ctx), + Statement::ContinueStatement(_) => self.count_continue_statement(ctx), + Statement::BlockStatement(block) => self.count_block_statement(block, ctx), + Statement::FunctionDeclaration(fd) => self.count_function_declaration(fd, ctx), + Statement::ClassDeclaration(class) => self.count_class_declaration(class, ctx), + Statement::ThrowStatement(ts) => self.count_throw_statement(ts, ctx), + Statement::TryStatement(ts) => self.count_try_statement(ts, ctx), _ => {} } } diff --git a/crates/oxide_compiler/src/counter/stmt/switch.rs b/crates/oxide_compiler/src/counter/stmt/switch.rs new file mode 100644 index 0000000..c3adf18 --- /dev/null +++ b/crates/oxide_compiler/src/counter/stmt/switch.rs @@ -0,0 +1,41 @@ +use super::*; + +impl Compiler { + pub(in crate::counter) 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; // EQ + ctx.alloc_reg(); // eq result + ctx.projected_pc += 1; // JMP_IF_TRUE + 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; // JMP to SwitchEnd (no match) + } + + for (case_idx, case) in cases.iter().enumerate() { + let case_label = Label::SwitchCase(id, case_idx as u32); + ctx.label_map.insert(case_label, ctx.projected_pc); + for s in &case.consequent { + self.count_statement(s, ctx); + } + } + + ctx.label_map.insert(end_label, ctx.projected_pc); + ctx.pop_switch(); + } +}