Skip to content

Commit f87da67

Browse files
committed
perf(transform/styled-components): optimize removal loop (#12284)
Follow-on after #12256. Optimize the `retain` loop for removing quasis and expressions by doing bounds check only once at start, rather than on each turn of the loop.
1 parent 97e5f9c commit f87da67

File tree

2 files changed

+20
-5
lines changed

2 files changed

+20
-5
lines changed

crates/oxc_transformer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ oxc-browserslist = { workspace = true }
2525
oxc_allocator = { workspace = true }
2626
oxc_ast = { workspace = true }
2727
oxc_ast_visit = { workspace = true }
28-
oxc_data_structures = { workspace = true, features = ["inline_string", "rope", "stack"] }
28+
oxc_data_structures = { workspace = true, features = ["assert_unchecked", "inline_string", "rope", "stack"] }
2929
oxc_diagnostics = { workspace = true }
3030
oxc_ecmascript = { workspace = true }
3131
oxc_parser = { workspace = true }

crates/oxc_transformer/src/plugins/styled_components.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ use serde::Deserialize;
6565

6666
use oxc_allocator::{TakeIn, Vec as ArenaVec};
6767
use oxc_ast::{AstBuilder, NONE, ast::*};
68-
use oxc_data_structures::inline_string::InlineString;
68+
use oxc_data_structures::{assert_unchecked, inline_string::InlineString};
6969
use oxc_semantic::SymbolId;
7070
use oxc_span::SPAN;
7171
use oxc_traverse::{Ancestor, Traverse};
@@ -1093,11 +1093,26 @@ fn minify_template_literal<'a>(lit: &mut TemplateLiteral<'a>, ast: AstBuilder<'a
10931093
let output_str = unsafe { std::str::from_utf8_unchecked(&output) };
10941094
quasis.last_mut().unwrap().value.raw = ast.atom(output_str);
10951095

1096-
// Remove quasis that are marked for removal, and the expressions following them
1096+
// Remove quasis that are marked for removal, and the expressions following them.
1097+
// TODO: Remove scopes, symbols, and references for removed `Expression`s.
10971098
if delete_some {
1099+
// We assert the lengths of `quasis` and `expressions` here so that we can safely remove
1100+
// the bounds check in the `retain` loop.
1101+
// It should always be true that `quasis.len() == expressions.len() + 1`
1102+
// but `quasis.len() >= expressions.len()` is all we need to ensure safety of `assert_unchecked!`
1103+
// below, and it's a cheaper check.
1104+
assert!(quasis.len() >= expressions.len());
1105+
10981106
let mut quasis_iter = quasis.iter();
1099-
// TODO: Remove scopes, symbols, and references for removed `Expression`.
1100-
expressions.retain(|_| quasis_iter.next().unwrap().span != REMOVE_SENTINEL);
1107+
expressions.retain(|_| {
1108+
// This unchecked assertion removes bounds check in `unwrap`.
1109+
// SAFETY: We asserted above that there are at least as many quasis as expressions,
1110+
// so `quasis_iter` cannot be exhausted in this loop.
1111+
unsafe { assert_unchecked!(!quasis_iter.as_slice().is_empty()) };
1112+
let quasi = quasis_iter.next().unwrap();
1113+
quasi.span != REMOVE_SENTINEL
1114+
});
1115+
11011116
quasis.retain(|quasi| quasi.span != REMOVE_SENTINEL);
11021117
}
11031118
}

0 commit comments

Comments
 (0)