Files
OxideJS-New/crates/oxide_compiler/src/counter/expr/control.rs
T

80 lines
3.4 KiB
Rust

use super::*;
impl Compiler {
fn count_conditional_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
let Expression::ConditionalExpression(cond) = expr else {
return;
};
let id = ctx.next_label_id();
let else_label = Label::TernaryElse(id);
let end_label = Label::TernaryEnd(id);
self.count_expression(&cond.test, ctx);
ctx.projected_pc += 1; // JMP_IF_FALSE
self.count_expression(&cond.consequent, ctx);
ctx.alloc_reg(); // result register
ctx.projected_pc += 1; // LOAD_VAR result <- consequent
ctx.projected_pc += 1; // JMP to end
ctx.labels.label_map.insert(else_label, ctx.projected_pc);
self.count_expression(&cond.alternate, ctx);
ctx.projected_pc += 1; // LOAD_VAR result <- alternate
ctx.labels.label_map.insert(end_label, ctx.projected_pc);
}
fn count_sequence_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
let Expression::SequenceExpression(seq) = expr else {
return;
};
for e in &seq.expressions {
self.count_expression(e, ctx);
}
}
fn count_logical_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
let Expression::LogicalExpression(log) = expr else {
return;
};
self.count_expression(&log.left, ctx);
let is_simple = is_side_effect_free(&log.left) && is_side_effect_free(&log.right);
if is_simple {
self.count_expression(&log.right, ctx);
ctx.alloc_reg();
ctx.projected_pc += 1; // AND/OR
} else if matches!(log.operator, LogicalOperator::Coalesce) {
// Mirror emit_logical_expression's Coalesce path: it patches raw bytecode
// positions and does NOT consume a label id. Consuming one here would shift
// every later construct's label id and break resolve_label at emit time.
ctx.alloc_reg(); // dup register
ctx.projected_pc += 1; // LOAD_VAR (DUP)
ctx.projected_pc += 1; // JMP_IF_NULLISH
ctx.projected_pc += 1; // JMP over RHS on non-nullish
self.count_expression(&log.right, ctx);
ctx.projected_pc += 1; // LOAD_VAR (overwrite)
} else {
let id = ctx.next_label_id();
ctx.alloc_reg(); // dup register
ctx.projected_pc += 1; // LOAD_VAR (DUP)
ctx.projected_pc += 1; // JMP_IF_FALSE or JMP_IF_TRUE
self.count_expression(&log.right, ctx);
ctx.projected_pc += 1; // LOAD_VAR (overwrite)
let skip_label = match log.operator {
LogicalOperator::And => Label::TernaryEnd(id),
LogicalOperator::Or => Label::TernaryElse(id),
LogicalOperator::Coalesce => unreachable!("coalesce handled above"),
};
ctx.labels.label_map.insert(skip_label, ctx.projected_pc);
}
}
pub(in crate::counter) fn count_conditional_chain(&self, expr: &Expression, ctx: &mut CompileCtx) {
match expr {
Expression::ConditionalExpression(_) => self.count_conditional_expression(expr, ctx),
Expression::SequenceExpression(_) => self.count_sequence_expression(expr, ctx),
Expression::LogicalExpression(_) => self.count_logical_expression(expr, ctx),
_ => {}
}
}
}