swc_ecma_minifier/compress/optimize/
strings.rsuse swc_atoms::js_word;
use swc_common::{util::take::Take, Spanned, SyntaxContext};
use swc_ecma_ast::*;
use swc_ecma_utils::{ExprExt, Value::Known};
use super::Optimizer;
use crate::mode::Mode;
impl<M> Optimizer<'_, M>
where
M: Mode,
{
pub(super) fn optimize_expr_in_str_ctx_unsafely(&mut self, e: &mut Expr) {
if !self.options.unsafe_passes {
return;
}
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
args,
..
}) = e
{
if args
.iter()
.any(|arg| arg.expr.may_have_side_effects(&self.expr_ctx))
{
return;
}
match &**callee {
Expr::Ident(Ident {
sym: js_word!("RegExp"),
..
}) if self.options.unsafe_regexp => {
if args.len() != 1 {
return;
}
self.optimize_expr_in_str_ctx(&mut args[0].expr);
if let Expr::Lit(Lit::Str(..)) = &*args[0].expr {
self.changed = true;
report_change!(
"strings: Unsafely reduced `RegExp` call in a string context"
);
*e = *args[0].expr.take();
}
}
_ => {}
}
}
}
pub(super) fn optimize_expr_in_str_ctx(&mut self, n: &mut Expr) {
match n {
Expr::Lit(Lit::Str(..)) => return,
Expr::Paren(e) => {
self.optimize_expr_in_str_ctx(&mut e.expr);
if let Expr::Lit(Lit::Str(..)) = &*e.expr {
*n = *e.expr.take();
self.changed = true;
report_change!("string: Removed a paren in a string context");
}
return;
}
_ => {}
}
let value = n.as_pure_string(&self.expr_ctx);
if let Known(value) = value {
let span = n.span();
self.changed = true;
report_change!(
"strings: Converted an expression into a string literal (in string context)"
);
*n = Expr::Lit(Lit::Str(Str {
span,
raw: None,
value: value.into(),
}));
return;
}
match n {
Expr::Lit(Lit::Num(v)) => {
self.changed = true;
report_change!(
"strings: Converted a numeric literal ({}) into a string literal (in string \
context)",
v.value
);
let value = format!("{:?}", v.value);
*n = Expr::Lit(Lit::Str(Str {
span: v.span,
raw: None,
value: value.into(),
}));
}
Expr::Lit(Lit::Regex(v)) => {
if !self.options.evaluate {
return;
}
self.changed = true;
report_change!(
"strings: Converted a regex (/{}/{}) into a string literal (in string context)",
v.exp,
v.flags
);
let value = format!("/{}/{}", v.exp, v.flags);
*n = Expr::Lit(Lit::Str(Str {
span: v.span,
raw: None,
value: value.into(),
}));
}
Expr::Bin(BinExpr {
span,
op: op!("/"),
left,
right,
}) => {
if let (Expr::Lit(Lit::Num(l)), Expr::Lit(Lit::Num(r))) = (&**left, &**right) {
if l.value == 0.0 && r.value == 0.0 {
*n = Expr::Ident(Ident::new(
js_word!("NaN"),
span.with_ctxt(
SyntaxContext::empty().apply_mark(self.marks.unresolved_mark),
),
));
self.changed = true;
report_change!("strings: Evaluated 0 / 0 => NaN in string context");
}
}
}
_ => {}
}
}
}