Skip to content

Commit 90d54b4

Browse files
committed
perf(javascript): reduce walker analysis overhead
1 parent e40a0e2 commit 90d54b4

3 files changed

Lines changed: 169 additions & 76 deletions

File tree

crates/rspack_plugin_javascript/src/parser_plugin/inner_graph/plugin.rs

Lines changed: 27 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use swc_next_ecma_ast::{
1111
};
1212

1313
use super::state::{
14-
InnerGraphMapSetValue, InnerGraphMapUsage, InnerGraphMapValue, InnerGraphState,
14+
InnerGraphMapSet, InnerGraphMapSetValue, InnerGraphMapUsage, InnerGraphMapValue, InnerGraphState,
1515
InnerGraphUsageOperation, TopLevelSymbol,
1616
};
1717
use crate::{
@@ -169,7 +169,7 @@ impl InnerGraphParserPlugin {
169169
} else if new_set.is_empty() {
170170
state.set_graph(key, InnerGraphMapValue::Nil);
171171
} else {
172-
state.set_graph(key, InnerGraphMapValue::Set(new_set));
172+
state.set_graph(key, InnerGraphMapValue::Set(new_set.into()));
173173
}
174174
}
175175

@@ -191,12 +191,12 @@ impl InnerGraphParserPlugin {
191191
let mut new_set = match value {
192192
InnerGraphMapValue::Set(set) => std::mem::take(set),
193193
InnerGraphMapValue::True => unreachable!(),
194-
InnerGraphMapValue::Nil => HashSet::default(),
194+
InnerGraphMapValue::Nil => InnerGraphMapSet::default(),
195195
};
196196
let extend_value = match global_value.clone() {
197197
InnerGraphMapValue::Set(set) => set,
198198
InnerGraphMapValue::True => unreachable!(),
199-
InnerGraphMapValue::Nil => HashSet::default(),
199+
InnerGraphMapValue::Nil => InnerGraphMapSet::default(),
200200
};
201201
new_set.extend(extend_value);
202202
*value = InnerGraphMapValue::Set(new_set);
@@ -607,20 +607,6 @@ impl<'p, 'a> JavascriptParserPlugin<'p, 'a> for InnerGraphParserPlugin {
607607
.inner_graph
608608
.decl_with_top_level_symbol
609609
.insert(decl.span(ast), v);
610-
611-
if !matches!(
612-
ast.expr_data(init),
613-
ExprData::Function(_)
614-
| ExprData::ArrowFunctionExpression(_)
615-
| ExprData::StringLiteral(_)
616-
| ExprData::NumericLiteral(_)
617-
| ExprData::BigIntLiteral(_)
618-
| ExprData::BooleanLiteral(_)
619-
| ExprData::NullLiteral(_)
620-
| ExprData::RegExpLiteral(_)
621-
) {
622-
parser.inner_graph.pure_declarators.insert(decl.span(ast));
623-
}
624610
}
625611
}
626612

@@ -845,43 +831,31 @@ impl<'p, 'a> JavascriptParserPlugin<'p, 'a> for InnerGraphParserPlugin {
845831
parser.inner_graph.set_top_level_symbol(Some(*v));
846832

847833
let ast = parser.ast.ast;
848-
if parser
849-
.inner_graph
850-
.pure_declarators
851-
.contains(&decl.span(ast))
852-
{
853-
// class Foo extends Bar {}
854-
// if Foo is not used, we can ignore extends Bar
855-
if let Some(init) = decl.init(ast)
856-
&& let Some(class) = init.as_class(ast)
857-
&& let Some(super_class) = class.super_class(ast)
858-
{
859-
let super_span = super_class.span(ast);
860-
let dep = PureExpressionDependency::new(
861-
DependencyRange::new(super_span.real_lo(), super_span.real_hi()),
862-
*parser.module_identifier,
863-
);
864-
let dep_idx = parser.next_dependency_idx();
865-
parser.add_dependency(BoxDependency::new(dep));
866-
Self::on_usage(parser, InnerGraphUsageOperation::PureExpression(dep_idx));
867-
} else if let Some(init) = decl.init(ast)
868-
&& !init.is_class(ast)
869-
{
870-
let init_span = init.span(ast);
871-
let dep = PureExpressionDependency::new(
872-
DependencyRange::new(init_span.real_lo(), init_span.real_hi()),
873-
*parser.module_identifier,
874-
);
875-
let dep_idx = parser.next_dependency_idx();
876-
parser.add_dependency(BoxDependency::new(dep));
877-
InnerGraphParserPlugin::on_usage(
878-
parser,
879-
InnerGraphUsageOperation::PureExpression(dep_idx),
880-
);
881-
}
834+
let init = decl
835+
.init(ast)
836+
.expect("inner graph declarator has an initializer");
837+
if !matches!(
838+
ast.expr_data(init),
839+
ExprData::Function(_)
840+
| ExprData::ArrowFunctionExpression(_)
841+
| ExprData::StringLiteral(_)
842+
| ExprData::NumericLiteral(_)
843+
| ExprData::BigIntLiteral(_)
844+
| ExprData::BooleanLiteral(_)
845+
| ExprData::NullLiteral(_)
846+
| ExprData::RegExpLiteral(_)
847+
) {
848+
let init_span = init.span(ast);
849+
let dep = PureExpressionDependency::new(
850+
DependencyRange::new(init_span.real_lo(), init_span.real_hi()),
851+
*parser.module_identifier,
852+
);
853+
let dep_idx = parser.next_dependency_idx();
854+
parser.add_dependency(BoxDependency::new(dep));
855+
InnerGraphParserPlugin::on_usage(parser, InnerGraphUsageOperation::PureExpression(dep_idx));
882856
}
883857

884-
parser.walk_expression(decl.init(ast).expect("should have initialization"));
858+
parser.walk_expression(init);
885859
parser.inner_graph.set_top_level_symbol(None);
886860
return Some(true);
887861
} else if decl

crates/rspack_plugin_javascript/src/parser_plugin/inner_graph/state.rs

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::hash::{Hash, Hasher};
22

3+
use either::Either;
34
use rspack_util::atom::AtomKey;
45
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
6+
use smallvec::SmallVec;
57
use swc_next_ecma_ast::Span;
68

79
use crate::Atom;
@@ -52,7 +54,7 @@ pub(super) struct TopLevelSymbolState {
5254

5355
#[derive(Default, Clone, PartialEq, Eq, Debug)]
5456
pub(super) enum InnerGraphMapValue {
55-
Set(HashSet<InnerGraphMapSetValue>),
57+
Set(InnerGraphMapSet),
5658
True,
5759
#[default]
5860
Nil,
@@ -64,6 +66,122 @@ pub(super) enum InnerGraphMapSetValue {
6466
Str(Atom),
6567
}
6668

69+
#[derive(Clone, Debug)]
70+
pub(super) enum InnerGraphMapSet {
71+
Small(SmallVec<[InnerGraphMapSetValue; 2]>),
72+
Large(HashSet<InnerGraphMapSetValue>),
73+
}
74+
75+
impl Default for InnerGraphMapSet {
76+
fn default() -> Self {
77+
Self::Small(SmallVec::new())
78+
}
79+
}
80+
81+
impl InnerGraphMapSet {
82+
fn from_value(value: InnerGraphMapSetValue) -> Self {
83+
let mut values = SmallVec::new();
84+
values.push(value);
85+
Self::Small(values)
86+
}
87+
88+
pub(super) fn insert(&mut self, value: InnerGraphMapSetValue) -> bool {
89+
match self {
90+
Self::Small(values) => {
91+
if values.contains(&value) {
92+
return false;
93+
}
94+
if values.len() < values.inline_size() {
95+
values.push(value);
96+
return true;
97+
}
98+
let mut set = HashSet::default();
99+
set.reserve(values.len() + 1);
100+
set.extend(values.drain(..));
101+
let inserted = set.insert(value);
102+
*self = Self::Large(set);
103+
inserted
104+
}
105+
Self::Large(values) => values.insert(value),
106+
}
107+
}
108+
109+
pub(super) fn contains(&self, value: &InnerGraphMapSetValue) -> bool {
110+
match self {
111+
Self::Small(values) => values.contains(value),
112+
Self::Large(values) => values.contains(value),
113+
}
114+
}
115+
116+
pub(super) fn len(&self) -> usize {
117+
match self {
118+
Self::Small(values) => values.len(),
119+
Self::Large(values) => values.len(),
120+
}
121+
}
122+
123+
pub(super) fn iter(
124+
&self,
125+
) -> Either<
126+
std::slice::Iter<'_, InnerGraphMapSetValue>,
127+
std::collections::hash_set::Iter<'_, InnerGraphMapSetValue>,
128+
> {
129+
self.into_iter()
130+
}
131+
}
132+
133+
impl From<HashSet<InnerGraphMapSetValue>> for InnerGraphMapSet {
134+
fn from(values: HashSet<InnerGraphMapSetValue>) -> Self {
135+
Self::Large(values)
136+
}
137+
}
138+
139+
impl Extend<InnerGraphMapSetValue> for InnerGraphMapSet {
140+
fn extend<T: IntoIterator<Item = InnerGraphMapSetValue>>(&mut self, iter: T) {
141+
for value in iter {
142+
self.insert(value);
143+
}
144+
}
145+
}
146+
147+
impl PartialEq for InnerGraphMapSet {
148+
fn eq(&self, other: &Self) -> bool {
149+
self.len() == other.len() && self.iter().all(|value| other.contains(value))
150+
}
151+
}
152+
153+
impl Eq for InnerGraphMapSet {}
154+
155+
impl<'a> IntoIterator for &'a InnerGraphMapSet {
156+
type Item = &'a InnerGraphMapSetValue;
157+
type IntoIter = Either<
158+
std::slice::Iter<'a, InnerGraphMapSetValue>,
159+
std::collections::hash_set::Iter<'a, InnerGraphMapSetValue>,
160+
>;
161+
162+
fn into_iter(self) -> Self::IntoIter {
163+
match self {
164+
InnerGraphMapSet::Small(values) => Either::Left(values.iter()),
165+
InnerGraphMapSet::Large(values) => Either::Right(values.iter()),
166+
}
167+
}
168+
}
169+
170+
impl IntoIterator for InnerGraphMapSet {
171+
type Item = InnerGraphMapSetValue;
172+
type IntoIter = Either<
173+
smallvec::IntoIter<[InnerGraphMapSetValue; 2]>,
174+
std::collections::hash_set::IntoIter<InnerGraphMapSetValue>,
175+
>;
176+
177+
fn into_iter(self) -> Self::IntoIter {
178+
match self {
179+
Self::Small(values) => Either::Left(values.into_iter()),
180+
Self::Large(values) => Either::Right(values.into_iter()),
181+
}
182+
}
183+
}
184+
67185
impl Hash for InnerGraphMapSetValue {
68186
fn hash<H: Hasher>(&self, state: &mut H) {
69187
match self {
@@ -105,7 +223,6 @@ pub(crate) struct InnerGraphState {
105223
pub(super) statement_pure_part: HashMap<Span, Span>,
106224
pub(super) class_with_top_level_symbol: HashMap<Span, TopLevelSymbol>,
107225
pub(super) decl_with_top_level_symbol: HashMap<Span, TopLevelSymbol>,
108-
pub(super) pure_declarators: HashSet<Span>,
109226
}
110227

111228
impl InnerGraphState {
@@ -237,11 +354,11 @@ impl InnerGraphState {
237354
}
238355
Some(InnerGraphMapValue::True) => {}
239356
Some(value @ InnerGraphMapValue::Nil) => {
240-
*value = InnerGraphMapValue::Set(HashSet::from_iter([set_value]));
357+
*value = InnerGraphMapValue::Set(InnerGraphMapSet::from_value(set_value));
241358
}
242359
None => self.set_graph(
243360
symbol,
244-
InnerGraphMapValue::Set(HashSet::from_iter([set_value])),
361+
InnerGraphMapValue::Set(InnerGraphMapSet::from_value(set_value)),
245362
),
246363
}
247364
}

crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,25 +1036,27 @@ impl<'parser> JavascriptParser<'parser> {
10361036
/// example `require.ensure`'s `require` parameter), while import bindings
10371037
/// need parser-plugin tags before references can consume them.
10381038
fn activate_semantic_scope_bindings(&mut self) {
1039-
let symbols = self
1040-
.ast
1041-
.semantic
1042-
.bindings(self.current_semantic_scope)
1043-
.filter_map(|symbol| {
1044-
let flags = self.ast.semantic.symbol(symbol).flags;
1045-
(flags.intersects(
1046-
SymbolFlags::FUNCTION_SCOPED_VAR
1047-
| SymbolFlags::BLOCK_SCOPED_VAR
1048-
| SymbolFlags::FUNCTION
1049-
| SymbolFlags::CLASS,
1050-
) && !flags
1051-
.intersects(SymbolFlags::PARAMETER | SymbolFlags::CATCH_VAR | SymbolFlags::ANY_IMPORT))
1052-
.then_some(symbol.index())
1053-
})
1054-
.collect::<SmallVec<[_; 16]>>();
1055-
1056-
for index in symbols {
1057-
self.ensure_semantic_variable(index);
1039+
let semantic = &self.ast.semantic;
1040+
let semantic_variables = &mut self.semantic_variables;
1041+
let semantic_normal_variable = self.semantic_normal_variable;
1042+
for symbol in semantic.bindings(self.current_semantic_scope) {
1043+
let flags = semantic.symbol(symbol).flags;
1044+
if flags.intersects(
1045+
SymbolFlags::FUNCTION_SCOPED_VAR
1046+
| SymbolFlags::BLOCK_SCOPED_VAR
1047+
| SymbolFlags::FUNCTION
1048+
| SymbolFlags::CLASS,
1049+
) && !flags
1050+
.intersects(SymbolFlags::PARAMETER | SymbolFlags::CATCH_VAR | SymbolFlags::ANY_IMPORT)
1051+
{
1052+
let index = symbol.index();
1053+
if index >= semantic_variables.len() {
1054+
semantic_variables.resize(index + 1, None);
1055+
}
1056+
if semantic_variables[index].is_none() {
1057+
semantic_variables[index] = Some(semantic_normal_variable);
1058+
}
1059+
}
10581060
}
10591061
}
10601062

0 commit comments

Comments
 (0)