mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): implement folding charCodeAt string fns (#6475)
This commit is contained in:
parent
4348eae157
commit
e6a5a1b11c
3 changed files with 85 additions and 17 deletions
|
|
@ -8,6 +8,7 @@ mod prop_name;
|
|||
|
||||
// Abstract Operations
|
||||
mod string_char_at;
|
||||
mod string_char_code_at;
|
||||
mod string_index_of;
|
||||
mod string_last_index_of;
|
||||
mod string_to_big_int;
|
||||
|
|
@ -27,8 +28,8 @@ pub mod side_effects;
|
|||
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, string_to_big_int::StringToBigInt,
|
||||
string_to_number::StringToNumber, to_big_int::ToBigInt, to_boolean::ToBoolean,
|
||||
to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString,
|
||||
string_char_at::StringCharAt, string_char_code_at::StringCharCodeAt,
|
||||
string_index_of::StringIndexOf, string_last_index_of::StringLastIndexOf,
|
||||
string_to_big_int::StringToBigInt, string_to_number::StringToNumber, to_big_int::ToBigInt,
|
||||
to_boolean::ToBoolean, to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString,
|
||||
};
|
||||
|
|
|
|||
32
crates/oxc_ecmascript/src/string_char_code_at.rs
Normal file
32
crates/oxc_ecmascript/src/string_char_code_at.rs
Normal file
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
use cow_utils::CowUtils;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_ecmascript::{StringCharAt, StringIndexOf, StringLastIndexOf};
|
||||
use oxc_ecmascript::{
|
||||
constant_evaluation::ConstantEvaluation, StringCharAt, StringCharCodeAt, StringIndexOf,
|
||||
StringLastIndexOf,
|
||||
};
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::CompressorPass;
|
||||
use crate::{node_util::Ctx, CompressorPass};
|
||||
|
||||
/// Minimize With Known Methods
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java>
|
||||
|
|
@ -78,7 +81,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,
|
||||
|
|
@ -133,6 +138,10 @@ impl PeepholeReplaceKnownMethods {
|
|||
string_lit: &StringLiteral<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if call_expr.arguments.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
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))
|
||||
|
|
@ -155,6 +164,35 @@ 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 = call_expr.arguments.first();
|
||||
let char_at_index = if let Some(v) = char_at_index {
|
||||
let val = match v {
|
||||
Argument::SpreadElement(_) => None,
|
||||
_ => Ctx(ctx).get_side_free_number_value(v.to_expression()),
|
||||
}?;
|
||||
Some(val)
|
||||
} else {
|
||||
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>
|
||||
|
|
@ -418,12 +456,10 @@ mod test {
|
|||
fold("x = 'abcde'.charAt(2)", "x = 'c'");
|
||||
fold("x = 'abcde'.charAt(3)", "x = 'd'");
|
||||
fold("x = 'abcde'.charAt(4)", "x = 'e'");
|
||||
// START: note, the following test cases outputs differ from Google's
|
||||
fold("x = 'abcde'.charAt(5)", "x = ''");
|
||||
fold("x = 'abcde'.charAt(-1)", "x = ''");
|
||||
fold("x = 'abcde'.charAt()", "x = 'a'");
|
||||
fold("x = 'abcde'.charAt(0, ++z)", "x = 'a'");
|
||||
// END
|
||||
fold_same("x = 'abcde'.charAt(0, ++z)");
|
||||
fold_same("x = 'abcde'.charAt(y)");
|
||||
fold_same("x = 'abcde'.charAt(null)"); // or x = 'a'
|
||||
fold_same("x = 'abcde'.charAt(true)"); // or x = 'b'
|
||||
|
|
@ -436,7 +472,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");
|
||||
|
|
@ -446,12 +481,12 @@ 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)
|
||||
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");
|
||||
// fold("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606");
|
||||
fold("x = 'abcde'.charCodeAt()", "x = 97");
|
||||
fold("x = 'abcde'.charCodeAt(0, ++z)", "x = 97");
|
||||
fold("x = 'abcde'.charCodeAt(null)", "x = 97");
|
||||
fold("x = 'abcde'.charCodeAt(true)", "x = 98");
|
||||
// fold("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348");
|
||||
// fold("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606");
|
||||
|
||||
// Template strings
|
||||
fold_same("x = `abcdef`.charCodeAt(0)");
|
||||
|
|
|
|||
Loading…
Reference in a new issue