Skip to content

Commit

Permalink
feat(minifier): implement folding charCodeAt string fns
Browse files Browse the repository at this point in the history
  • Loading branch information
camc314 committed Oct 12, 2024
1 parent 05a79d4 commit 5124d0f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 8 deletions.
5 changes: 3 additions & 2 deletions crates/oxc_ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ mod prop_name;

// Abstract Operations
mod string_char_at;
mod string_char_code_at;
mod string_index_of;
mod string_last_index_of;
mod to_int_32;

pub use self::{
bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList,
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName,
string_char_at::StringCharAt, string_index_of::StringIndexOf,
string_last_index_of::StringLastIndexOf, to_int_32::ToInt32,
string_char_at::StringCharAt, string_char_code_at::StringCharCodeAt,
string_index_of::StringIndexOf, string_last_index_of::StringLastIndexOf, to_int_32::ToInt32,
};
32 changes: 32 additions & 0 deletions crates/oxc_ecmascript/src/string_char_code_at.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::StringCharAt;

pub trait StringCharCodeAt {
/// `String.prototype.charCodeAt ( pos )`
/// <https://tc39.es/ecma262/#sec-string.prototype.charcodeat>
fn char_code_at(&self, index: Option<f64>) -> Option<u32>;
}

impl StringCharCodeAt for &str {
fn char_code_at(&self, index: Option<f64>) -> Option<u32> {
self.char_at(index).map(|c| c as u32)
}
}

#[cfg(test)]
mod test {

#[test]
fn test_evaluate_char_code_at() {
use crate::StringCharCodeAt;

assert_eq!("abcde".char_code_at(Some(0.0)), Some(97));
assert_eq!("abcde".char_code_at(Some(1.0)), Some(98));
assert_eq!("abcde".char_code_at(Some(2.0)), Some(99));
assert_eq!("abcde".char_code_at(Some(3.0)), Some(100));
assert_eq!("abcde".char_code_at(Some(4.0)), Some(101));
assert_eq!("abcde".char_code_at(Some(5.0)), None);
assert_eq!("abcde".char_code_at(Some(-1.0)), None);
assert_eq!("abcde".char_code_at(None), Some(97));
assert_eq!("abcde".char_code_at(Some(0.0)), Some(97));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use cow_utils::CowUtils;
use oxc_ast::ast::*;
use oxc_ast::ast::*;
use oxc_ecmascript::{StringCharAt, StringIndexOf, StringLastIndexOf};
use oxc_ecmascript::{StringCharAt, StringCharCodeAt, StringIndexOf, StringLastIndexOf};
use oxc_traverse::{Traverse, TraverseCtx};

use crate::CompressorPass;
Expand Down Expand Up @@ -79,7 +78,9 @@ impl PeepholeReplaceKnownMethods {
"charAt" => {
Self::try_fold_string_char_at(call_expr.span, call_expr, string_lit, ctx)
}
"charCodeAt" => None,
"charCodeAt" => {
Self::try_fold_string_char_code_at(call_expr.span, call_expr, string_lit, ctx)
}
"replace" => None,
"replaceAll" => None,
_ => None,
Expand Down Expand Up @@ -156,6 +157,38 @@ impl PeepholeReplaceKnownMethods {

return Some(ctx.ast.expression_from_string_literal(ctx.ast.string_literal(span, result)));
}

fn try_fold_string_char_code_at<'a>(
span: Span,
call_expr: &CallExpression<'a>,
string_lit: &StringLiteral<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
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))
if unary_expr.operator == UnaryOperator::UnaryNegation =>
{
let Expression::NumericLiteral(numeric_lit) = &unary_expr.argument else {
return None;
};
Some(-(numeric_lit.value))
}
None => None,
_ => return None,
};

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

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

/// Port from: <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.java>
Expand Down Expand Up @@ -437,7 +470,6 @@ mod test {
}

#[test]
#[ignore]
fn test_fold_string_char_code_at() {
fold("x = 'abcde'.charCodeAt(0)", "x = 97");
fold("x = 'abcde'.charCodeAt(1)", "x = 98");
Expand All @@ -447,8 +479,10 @@ mod test {
fold_same("x = 'abcde'.charCodeAt(5)"); // or x = (0/0)
fold_same("x = 'abcde'.charCodeAt(-1)"); // or x = (0/0)
fold_same("x = 'abcde'.charCodeAt(y)");
fold_same("x = 'abcde'.charCodeAt()"); // or x = 97
fold_same("x = 'abcde'.charCodeAt(0, ++z)"); // or (++z, 97)
// START: note the following test cases outputs differ from Google's
fold("x = 'abcde'.charCodeAt()", "x = 97");
fold("x = 'abcde'.charCodeAt(0, ++z)", "x = 97");
// END
fold_same("x = 'abcde'.charCodeAt(null)"); // or x = 97
fold_same("x = 'abcde'.charCodeAt(true)"); // or x = 98
// fold("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348");
Expand Down

0 comments on commit 5124d0f

Please sign in to comment.