js_deobfuscator/engine/
pipeline.rs1use std::time::{Duration, Instant};
8
9use oxc::allocator::Allocator;
10use oxc::ast::ast::Program;
11use oxc::semantic::{SemanticBuilder, Scoping};
12
13use tracing::{debug, info};
14
15use super::error::Result;
16use super::module::Module;
17
18#[derive(Debug)]
20pub struct EngineResult {
21 pub iterations: usize,
23 pub total_modifications: usize,
25 pub converged: bool,
27 pub elapsed: Duration,
29}
30
31pub struct Engine {
33 common: Vec<Box<dyn Module>>,
35 locked: Vec<Box<dyn Module>>,
37 max_iterations: usize,
38}
39
40impl Engine {
41 pub fn new(
42 common: Vec<Box<dyn Module>>,
43 locked: Vec<Box<dyn Module>>,
44 max_iterations: usize,
45 ) -> Self {
46 Self { common, locked, max_iterations }
47 }
48
49 pub fn run<'a>(
51 &mut self,
52 allocator: &'a Allocator,
53 program: &mut Program<'a>,
54 ) -> Result<EngineResult> {
55 let start = Instant::now();
56 let mut scoping = build_scoping(program);
57 let mut total_modifications = 0;
58 let mut iterations = 0;
59 let mut converged = false;
60
61 for _outer in 0..self.max_iterations {
62 for _inner in 0..self.max_iterations {
64 let mut iteration_mods = 0;
65
66 for module in &mut self.common {
67 let result = module.transform(allocator, program, scoping)?;
68 iteration_mods += result.modifications;
69 scoping = result.scoping;
70
71 if module.changes_symbols() && result.modifications > 0 {
72 scoping = build_scoping(program);
73 }
74 }
75
76 total_modifications += iteration_mods;
77 iterations += 1;
78
79 debug!(iteration = iterations, mods = iteration_mods, "common iteration");
80
81 if iteration_mods == 0 {
82 break;
83 }
84 }
85
86 let mut locked_mods = 0;
88 for module in &mut self.locked {
89 let result = module.transform(allocator, program, scoping)?;
90 locked_mods += result.modifications;
91 scoping = result.scoping;
92
93 if module.changes_symbols() && result.modifications > 0 {
94 scoping = build_scoping(program);
95 }
96 }
97
98 total_modifications += locked_mods;
99 if locked_mods > 0 {
100 iterations += 1;
101 debug!(locked_mods, "locked modules changed, restarting common");
102 }
103
104 if locked_mods == 0 {
105 converged = true;
106 break;
107 }
108 }
109
110 info!(
111 iterations,
112 total_modifications,
113 converged,
114 elapsed_ms = start.elapsed().as_millis(),
115 "engine run complete"
116 );
117
118 Ok(EngineResult {
119 iterations,
120 total_modifications,
121 converged,
122 elapsed: start.elapsed(),
123 })
124 }
125}
126
127fn build_scoping(program: &Program) -> Scoping {
128 SemanticBuilder::new().build(program).semantic.into_scoping()
129}