use std::time::{Duration, Instant};
use oxc::allocator::Allocator;
use oxc::ast::ast::Program;
use oxc::semantic::{SemanticBuilder, Scoping};
use tracing::{debug, info};
use super::error::Result;
use super::module::Module;
#[derive(Debug)]
pub struct EngineResult {
pub iterations: usize,
pub total_modifications: usize,
pub converged: bool,
pub elapsed: Duration,
}
pub struct Engine {
common: Vec<Box<dyn Module>>,
locked: Vec<Box<dyn Module>>,
max_iterations: usize,
}
impl Engine {
pub fn new(
common: Vec<Box<dyn Module>>,
locked: Vec<Box<dyn Module>>,
max_iterations: usize,
) -> Self {
Self { common, locked, max_iterations }
}
pub fn run<'a>(
&mut self,
allocator: &'a Allocator,
program: &mut Program<'a>,
) -> Result<EngineResult> {
let start = Instant::now();
let mut scoping = build_scoping(program);
let mut total_modifications = 0;
let mut iterations = 0;
let mut converged = false;
for _outer in 0..self.max_iterations {
for _inner in 0..self.max_iterations {
let mut iteration_mods = 0;
for module in &mut self.common {
let result = module.transform(allocator, program, scoping)?;
iteration_mods += result.modifications;
scoping = result.scoping;
if module.changes_symbols() && result.modifications > 0 {
scoping = build_scoping(program);
}
}
total_modifications += iteration_mods;
iterations += 1;
debug!(iteration = iterations, mods = iteration_mods, "common iteration");
if iteration_mods == 0 {
break;
}
}
let mut locked_mods = 0;
for module in &mut self.locked {
let result = module.transform(allocator, program, scoping)?;
locked_mods += result.modifications;
scoping = result.scoping;
if module.changes_symbols() && result.modifications > 0 {
scoping = build_scoping(program);
}
}
total_modifications += locked_mods;
if locked_mods > 0 {
iterations += 1;
debug!(locked_mods, "locked modules changed, restarting common");
}
if locked_mods == 0 {
converged = true;
break;
}
}
info!(
iterations,
total_modifications,
converged,
elapsed_ms = start.elapsed().as_millis(),
"engine run complete"
);
Ok(EngineResult {
iterations,
total_modifications,
converged,
elapsed: start.elapsed(),
})
}
}
fn build_scoping(program: &Program) -> Scoping {
SemanticBuilder::new().build(program).semantic.into_scoping()
}