pub(crate) mod pass;
pub(crate) mod walker;
use self::{pass::ConstantFolding, walker::Walker};
use crate::Context;
use bitflags::bitflags;
use boa_ast::{visitor::VisitorMut, Expression, StatementList};
use std::{fmt, ops::ControlFlow};
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OptimizerOptions: u8 {
const STATISTICS = 0b0000_0001;
const CONSTANT_FOLDING = 0b0000_0010;
const OPTIMIZE_ALL = Self::CONSTANT_FOLDING.bits();
}
}
#[derive(Debug)]
pub(crate) enum PassAction<T> {
Keep,
Modified,
Replace(T),
}
#[derive(Debug, Default, Clone, Copy)]
pub struct OptimizerStatistics {
pub constant_folding_run_count: usize,
pub constant_folding_pass_count: usize,
}
impl fmt::Display for OptimizerStatistics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Optimizer {{")?;
writeln!(
f,
" constant folding: {} run(s), {} pass(es) ({} mutating, {} checking)",
self.constant_folding_run_count,
self.constant_folding_pass_count,
self.constant_folding_pass_count
.saturating_sub(self.constant_folding_run_count),
self.constant_folding_run_count
)?;
writeln!(f, "}}")?;
Ok(())
}
}
#[derive(Debug)]
pub(crate) struct Optimizer<'context, 'host> {
statistics: OptimizerStatistics,
context: &'context mut Context<'host>,
}
impl<'context, 'host> Optimizer<'context, 'host> {
pub(crate) fn new(context: &'context mut Context<'host>) -> Self {
Self {
statistics: OptimizerStatistics::default(),
context,
}
}
fn run_constant_folding_pass(&mut self, expr: &mut Expression) -> bool {
self.statistics.constant_folding_run_count += 1;
let mut has_changes = false;
loop {
self.statistics.constant_folding_pass_count += 1;
let mut walker = Walker::new(|expr| -> PassAction<Expression> {
ConstantFolding::fold_expression(expr, self.context)
});
walker.walk_expression_postorder(expr);
if !walker.changed() {
break;
}
has_changes = true;
}
has_changes
}
fn run_all(&mut self, expr: &mut Expression) {
if self
.context
.optimizer_options()
.contains(OptimizerOptions::CONSTANT_FOLDING)
{
self.run_constant_folding_pass(expr);
}
}
pub(crate) fn apply(&mut self, statement_list: &mut StatementList) -> OptimizerStatistics {
self.visit_statement_list_mut(statement_list);
if self
.context
.optimizer_options()
.contains(OptimizerOptions::STATISTICS)
{
println!("{}", self.statistics);
}
self.statistics
}
}
impl<'ast> VisitorMut<'ast> for Optimizer<'_, '_> {
type BreakTy = ();
fn visit_expression_mut(&mut self, node: &'ast mut Expression) -> ControlFlow<Self::BreakTy> {
self.run_all(node);
ControlFlow::Continue(())
}
}