Skip to content

Commit

Permalink
refactor(minifier): clean up ReplaceKnownMethods
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Jan 4, 2025
1 parent bd8d677 commit 0845162
Showing 1 changed file with 25 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::borrow::Cow;

use cow_utils::CowUtils;

use oxc_ast::ast::*;
use oxc_ecmascript::{
constant_evaluation::ConstantEvaluation, StringCharAt, StringCharCodeAt, StringIndexOf,
Expand Down Expand Up @@ -37,55 +40,24 @@ impl PeepholeReplaceKnownMethods {
node: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
let Expression::CallExpression(call_expr) = node else { return };

let Expression::StaticMemberExpression(member) = &call_expr.callee else { return };
if let Expression::StringLiteral(string_lit) = &member.object {
let Expression::CallExpression(ce) = node else { return };
let Expression::StaticMemberExpression(member) = &ce.callee else { return };
if let Expression::StringLiteral(s) = &member.object {
let replacement = match member.property.name.as_str() {
"toLowerCase" | "toUpperCase" | "trim" => match member.property.name.as_str() {
"toLowerCase" => Some(ctx.ast.expression_string_literal(
call_expr.span,
string_lit.value.cow_to_lowercase(),
None,
)),
"toUpperCase" => Some(ctx.ast.expression_string_literal(
call_expr.span,
string_lit.value.cow_to_uppercase(),
None,
)),
"trim" => Some(ctx.ast.expression_string_literal(
call_expr.span,
string_lit.value.trim(),
None,
)),
_ => None,
},
"indexOf" | "lastIndexOf" => Self::try_fold_string_index_of(
call_expr.span,
call_expr,
member,
string_lit,
ctx,
),
"substring" | "slice" => Self::try_fold_string_substring_or_slice(
call_expr.span,
call_expr,
string_lit,
ctx,
),
"charAt" => {
Self::try_fold_string_char_at(call_expr.span, call_expr, string_lit, ctx)
"toLowerCase" | "toUpperCase" | "trim" => {
let value = match member.property.name.as_str() {
"toLowerCase" => s.value.cow_to_lowercase(),
"toUpperCase" => s.value.cow_to_uppercase(),
"trim" => Cow::Borrowed(s.value.trim()),
_ => return,
};
Some(ctx.ast.expression_string_literal(ce.span, value, None))
}
"charCodeAt" => {
Self::try_fold_string_char_code_at(call_expr.span, call_expr, string_lit, ctx)
}
"replace" | "replaceAll" => Self::try_fold_string_replace_or_string_replace_all(
call_expr.span,
call_expr,
member,
string_lit,
ctx,
),
"indexOf" | "lastIndexOf" => Self::try_fold_string_index_of(ce, member, s, ctx),
"substring" | "slice" => Self::try_fold_string_substring_or_slice(ce, s, ctx),
"charAt" => Self::try_fold_string_char_at(ce, s, ctx),
"charCodeAt" => Self::try_fold_string_char_code_at(ce, s, ctx),
"replace" | "replaceAll" => Self::try_fold_string_replace(ce, member, s, ctx),
_ => None,
};

Expand All @@ -97,7 +69,6 @@ impl PeepholeReplaceKnownMethods {
}

fn try_fold_string_index_of<'a>(
span: Span,
call_expr: &CallExpression<'a>,
member: &StaticMemberExpression<'a>,
string_lit: &StringLiteral<'a>,
Expand All @@ -108,35 +79,32 @@ impl PeepholeReplaceKnownMethods {
None => None,
_ => return None,
};

let search_start_index = match call_expr.arguments.get(1) {
Some(Argument::NumericLiteral(numeric_lit)) => Some(numeric_lit.value),
None => None,
_ => return None,
};

let result = match member.property.name.as_str() {
"indexOf" => string_lit.value.as_str().index_of(search_value, search_start_index),
"lastIndexOf" => {
string_lit.value.as_str().last_index_of(search_value, search_start_index)
}
_ => unreachable!(),
};

let span = call_expr.span;
#[expect(clippy::cast_precision_loss)]
Some(ctx.ast.expression_numeric_literal(span, result as f64, None, NumberBase::Decimal))
}

fn try_fold_string_substring_or_slice<'a>(
span: Span,
call_expr: &CallExpression<'a>,
string_lit: &StringLiteral<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if call_expr.arguments.len() > 2 {
return None;
}

let span = call_expr.span;
let start_idx = call_expr.arguments.first().and_then(|arg| match arg {
Argument::SpreadElement(_) => None,
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
Expand All @@ -145,20 +113,17 @@ impl PeepholeReplaceKnownMethods {
Argument::SpreadElement(_) => None,
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
});

#[expect(clippy::cast_precision_loss)]
if start_idx.is_some_and(|start| start > string_lit.value.len() as f64 || start < 0.0)
|| end_idx.is_some_and(|end| end > string_lit.value.len() as f64 || end < 0.0)
{
return None;
}

if let (Some(start), Some(end)) = (start_idx, end_idx) {
if start > end {
return None;
}
};

Some(ctx.ast.expression_string_literal(
span,
string_lit.value.as_str().substring(start_idx, end_idx),
Expand All @@ -167,15 +132,14 @@ impl PeepholeReplaceKnownMethods {
}

fn try_fold_string_char_at<'a>(
span: Span,
call_expr: &CallExpression<'a>,
string_lit: &StringLiteral<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if call_expr.arguments.len() > 1 {
return None;
}

let span = call_expr.span;
let char_at_index: Option<f64> = match call_expr.arguments.first() {
Some(Argument::NumericLiteral(numeric_lit)) => Some(numeric_lit.value),
Some(Argument::UnaryExpression(unary_expr))
Expand All @@ -200,7 +164,6 @@ impl PeepholeReplaceKnownMethods {
}

fn try_fold_string_char_code_at<'a>(
span: Span,
call_expr: &CallExpression<'a>,
string_lit: &StringLiteral<'a>,
ctx: &mut TraverseCtx<'a>,
Expand All @@ -209,16 +172,14 @@ impl PeepholeReplaceKnownMethods {
Argument::SpreadElement(_) => None,
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
})?;

let span = call_expr.span;
// TODO: if `result` is `None`, return `NaN` instead of skipping the optimization
let result = string_lit.value.as_str().char_code_at(Some(char_at_index))?;

#[expect(clippy::cast_lossless)]
Some(ctx.ast.expression_numeric_literal(span, result as f64, None, NumberBase::Decimal))
}

fn try_fold_string_replace_or_string_replace_all<'a>(
span: Span,
fn try_fold_string_replace<'a>(
call_expr: &CallExpression<'a>,
member: &StaticMemberExpression<'a>,
string_lit: &StringLiteral<'a>,
Expand All @@ -227,7 +188,7 @@ impl PeepholeReplaceKnownMethods {
if call_expr.arguments.len() != 2 {
return None;
}

let span = call_expr.span;
let search_value = call_expr.arguments.first().unwrap();
let search_value = match search_value {
Argument::SpreadElement(_) => return None,
Expand All @@ -242,11 +203,9 @@ impl PeepholeReplaceKnownMethods {
Ctx(ctx).get_side_free_string_value(replace_value.to_expression())?
}
};

if replace_value.contains('$') {
return None;
}

let result = match member.property.name.as_str() {
"replace" => {
string_lit.value.as_str().cow_replacen(search_value.as_ref(), &replace_value, 1)
Expand All @@ -256,7 +215,6 @@ impl PeepholeReplaceKnownMethods {
}
_ => unreachable!(),
};

Some(ctx.ast.expression_string_literal(span, result, None))
}
}
Expand Down

0 comments on commit 0845162

Please sign in to comment.