diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index c3bcd8558db517..5664ec36be9b84 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -3,6 +3,7 @@ mod exploit_assigns; mod peephole_fold_constants; mod peephole_minimize_conditions; mod peephole_remove_dead_code; +mod peephole_replace_known_methods; mod peephole_substitute_alternate_syntax; mod remove_syntax; mod statement_fusion; @@ -12,6 +13,7 @@ pub use exploit_assigns::ExploitAssigns; pub use peephole_fold_constants::PeepholeFoldConstants; pub use peephole_minimize_conditions::PeepholeMinimizeConditions; pub use peephole_remove_dead_code::PeepholeRemoveDeadCode; +pub use peephole_replace_known_methods::PeepholeReplaceKnownMethods; pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax; pub use remove_syntax::RemoveSyntax; pub use statement_fusion::StatementFusion; diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs new file mode 100644 index 00000000000000..c5303eb7ae0d51 --- /dev/null +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -0,0 +1,95 @@ +use oxc_ast::ast::*; +use oxc_traverse::{Traverse, TraverseCtx}; + +use crate::CompressorPass; + +/// Minimize With Known Methods +/// +pub struct PeepholeReplaceKnownMethods { + changed: bool, +} + +impl<'a> CompressorPass<'a> for PeepholeReplaceKnownMethods { + fn changed(&self) -> bool { + self.changed + } + + fn build(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { + self.changed = false; + // oxc_traverse::walk_program(self, program, ctx); + } +} + +impl<'a> Traverse<'a> for PeepholeReplaceKnownMethods {} + +impl PeepholeReplaceKnownMethods { + pub fn new() -> Self { + Self { changed: false } + } +} + +/// +#[cfg(test)] +mod test { + use oxc_allocator::Allocator; + + use crate::tester; + + fn test(source_text: &str, positive: &str) { + let allocator = Allocator::default(); + let mut pass = super::PeepholeReplaceKnownMethods::new(); + tester::test(&allocator, source_text, positive, &mut pass); + } + + fn test_same(source_text: &str) { + test(source_text, source_text); + } + + fn fold_same(js: &str) { + test_same(js); + } + + fn fold(js: &str, expected: &str) { + test(js, expected); + } + + #[test] + #[ignore] + fn test_string_index_of() { + fold("x = 'abcdef'.indexOf('g')", "x = -1"); + fold("x = 'abcdef'.indexOf('b')", "x = 1"); + fold("x = 'abcdefbe'.indexOf('b', 2)", "x = 6"); + fold("x = 'abcdef'.indexOf('bcd')", "x = 1"); + fold("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13"); + + fold("x = 'abcdef'.lastIndexOf('b')", "x = 1"); + fold("x = 'abcdefbe'.lastIndexOf('b')", "x = 6"); + fold("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1"); + + // Both elements must be strings. Don't do anything if either one is not + // string. + fold("x = 'abc1def'.indexOf(1)", "x = 3"); + fold("x = 'abcNaNdef'.indexOf(NaN)", "x = 3"); + fold("x = 'abcundefineddef'.indexOf(undefined)", "x = 3"); + fold("x = 'abcnulldef'.indexOf(null)", "x = 3"); + fold("x = 'abctruedef'.indexOf(true)", "x = 3"); + + // The following test case fails with JSC_PARSE_ERROR. Hence omitted. + // fold_same("x = 1.indexOf('bcd');"); + fold_same("x = NaN.indexOf('bcd')"); + fold_same("x = undefined.indexOf('bcd')"); + fold_same("x = null.indexOf('bcd')"); + fold_same("x = true.indexOf('bcd')"); + fold_same("x = false.indexOf('bcd')"); + + // Avoid dealing with regex or other types. + fold_same("x = 'abcdef'.indexOf(/b./)"); + fold_same("x = 'abcdef'.indexOf({a:2})"); + fold_same("x = 'abcdef'.indexOf([1,2])"); + + // Template Strings + fold_same("x = `abcdef`.indexOf('b')"); + fold_same("x = `Hello ${name}`.indexOf('a')"); + fold_same("x = tag `Hello ${name}`.indexOf('a')"); + } +} diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index 5d7a1135704f0b..cb1f7076020e9c 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -6,8 +6,8 @@ use oxc_traverse::TraverseCtx; use crate::{ ast_passes::{ CollapseVariableDeclarations, ExploitAssigns, PeepholeFoldConstants, - PeepholeMinimizeConditions, PeepholeRemoveDeadCode, PeepholeSubstituteAlternateSyntax, - RemoveSyntax, StatementFusion, + PeepholeMinimizeConditions, PeepholeRemoveDeadCode, PeepholeReplaceKnownMethods, + PeepholeSubstituteAlternateSyntax, RemoveSyntax, StatementFusion, }, CompressOptions, CompressorPass, }; @@ -46,13 +46,13 @@ impl<'a> Compressor<'a> { CollapseVariableDeclarations::new(self.options).build(program, &mut ctx); // See `latePeepholeOptimizations` - let mut passes: [&mut dyn CompressorPass; 5] = [ + let mut passes: [&mut dyn CompressorPass; 6] = [ &mut StatementFusion::new(), &mut PeepholeRemoveDeadCode::new(), // TODO: MinimizeExitPoints &mut PeepholeMinimizeConditions::new(), &mut PeepholeSubstituteAlternateSyntax::new(self.options), - // TODO: PeepholeReplaceKnownMethods + &mut PeepholeReplaceKnownMethods::new(), &mut PeepholeFoldConstants::new(), ];