Skip to content

Commit 8bfef35

Browse files
authoredMar 8, 2023
perf(es/utils): Introduce NodeIgnoringSpan (#7030)
1 parent 50ee7d1 commit 8bfef35

File tree

8 files changed

+184
-151
lines changed

8 files changed

+184
-151
lines changed
 

‎Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/swc/src/config/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ use swc_ecma_transforms_optimization::{
7272
simplify::{dce::Config as DceConfig, Config as SimplifyConfig},
7373
GlobalExprMap,
7474
};
75+
use swc_ecma_utils::NodeIgnoringSpan;
7576
use swc_ecma_visit::{Fold, VisitMutWith};
7677

7778
pub use crate::plugin::PluginConfig;
@@ -1698,11 +1699,11 @@ impl GlobalPassOption {
16981699
.filter(|(k, _)| k.contains('.'))
16991700
.map(|(k, v)| {
17001701
(
1701-
*expr(cm, handler, k.to_string()),
1702+
NodeIgnoringSpan::owned(*expr(cm, handler, k.to_string())),
17021703
*expr(cm, handler, v.to_string()),
17031704
)
17041705
})
1705-
.collect::<Vec<_>>();
1706+
.collect::<AHashMap<_, _>>();
17061707
let map = Arc::new(map);
17071708
CACHE.insert(cache_key, map.clone());
17081709
map

‎crates/swc_ecma_minifier/src/metadata/mod.rs

+9-40
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,17 @@
1-
use std::hash::Hash;
2-
31
use rustc_hash::FxHashSet;
42
use swc_common::{
53
comments::{Comment, CommentKind, Comments},
6-
EqIgnoreSpan, Span, SyntaxContext,
4+
Span, SyntaxContext,
75
};
86
use swc_ecma_ast::*;
97
use swc_ecma_usage_analyzer::marks::Marks;
10-
use swc_ecma_utils::find_pat_ids;
8+
use swc_ecma_utils::{find_pat_ids, NodeIgnoringSpan};
119
use swc_ecma_visit::{
1210
noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith,
1311
};
1412

1513
use crate::option::CompressOptions;
1614

17-
#[derive(Debug, Eq)]
18-
struct HashEqIgnoreSpanExprRef<'a>(&'a Expr);
19-
20-
impl<'a> PartialEq for HashEqIgnoreSpanExprRef<'a> {
21-
fn eq(&self, other: &Self) -> bool {
22-
Ident::within_ignored_ctxt(|| self.0.eq_ignore_span(other.0))
23-
}
24-
}
25-
26-
impl<'a> Hash for HashEqIgnoreSpanExprRef<'a> {
27-
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
28-
// In pratice, most of cases/input we are dealing with are Expr::Member or
29-
// Expr::Ident.
30-
match self.0 {
31-
Expr::Ident(i) => {
32-
i.sym.hash(state);
33-
}
34-
Expr::Member(i) => {
35-
Self(&i.obj).hash(state);
36-
if let MemberProp::Ident(prop) = &i.prop {
37-
prop.sym.hash(state);
38-
}
39-
}
40-
_ => {
41-
// Other expression kind would fallback to the same empty hash.
42-
// So, their will spend linear time to do comparisons.
43-
}
44-
}
45-
}
46-
}
47-
4815
#[cfg(test)]
4916
mod tests;
5017

@@ -59,7 +26,7 @@ pub(crate) fn info_marker<'a>(
5926
options
6027
.pure_funcs
6128
.iter()
62-
.map(|f| HashEqIgnoreSpanExprRef(f))
29+
.map(|f| NodeIgnoringSpan::borrowed(f.as_ref()))
6330
.collect()
6431
});
6532
InfoMarker {
@@ -81,7 +48,7 @@ struct State {
8148
struct InfoMarker<'a> {
8249
#[allow(dead_code)]
8350
options: Option<&'a CompressOptions>,
84-
pure_funcs: Option<FxHashSet<HashEqIgnoreSpanExprRef<'a>>>,
51+
pure_funcs: Option<FxHashSet<NodeIgnoringSpan<'a, Expr>>>,
8552
comments: Option<&'a dyn Comments>,
8653
marks: Marks,
8754
// unresolved_mark: Mark,
@@ -170,9 +137,11 @@ impl VisitMut for InfoMarker<'_> {
170137
} else if let Some(pure_fns) = &self.pure_funcs {
171138
if let Callee::Expr(e) = &n.callee {
172139
// Check for pure_funcs
173-
if pure_fns.contains(&HashEqIgnoreSpanExprRef(e)) {
174-
n.span = n.span.apply_mark(self.marks.pure);
175-
};
140+
Ident::within_ignored_ctxt(|| {
141+
if pure_fns.contains(&NodeIgnoringSpan::borrowed(e)) {
142+
n.span = n.span.apply_mark(self.marks.pure);
143+
};
144+
})
176145
}
177146
}
178147
}

‎crates/swc_ecma_minifier/src/metadata/tests.rs

-77
Original file line numberDiff line numberDiff line change
@@ -158,80 +158,3 @@
158158
// fn export_default_fn_1() {
159159
// assert_standalone("export default function f(module, exports) {}", 0);
160160
// }
161-
162-
use rustc_hash::FxHashSet;
163-
use swc_common::{util::take::Take, Mark, DUMMY_SP};
164-
use swc_ecma_utils::{member_expr, quote_expr};
165-
166-
use super::HashEqIgnoreSpanExprRef;
167-
168-
#[test]
169-
fn test_hash_eq_ignore_span_expr_ref() {
170-
use swc_ecma_ast::*;
171-
172-
fn expr_ref(expr_ref: &Expr) -> HashEqIgnoreSpanExprRef {
173-
HashEqIgnoreSpanExprRef(expr_ref)
174-
}
175-
176-
testing::run_test(false, |_cm, _handler| {
177-
let dummy_sp = DUMMY_SP;
178-
let meaningful_sp = dummy_sp.apply_mark(Mark::new());
179-
180-
let meaningful_ident_expr = Expr::Ident(Ident::new("foo".into(), meaningful_sp));
181-
let dummy_ident_expr = Expr::Ident(Ident::new("foo".into(), dummy_sp));
182-
183-
let meaningful_member_expr = member_expr!(meaningful_sp, foo.bar);
184-
let dummy_member_expr = member_expr!(dummy_sp, foo.bar);
185-
186-
let meaningful_null_expr = quote_expr!(meaningful_sp, null);
187-
let dummy_null_expr = quote_expr!(dummy_sp, null);
188-
189-
let meaningful_array_expr = Box::new(Expr::Array(ArrayLit {
190-
span: meaningful_sp,
191-
elems: Default::default(),
192-
}));
193-
194-
let dummy_array_expr = Box::new(Expr::Array(ArrayLit::dummy()));
195-
196-
// Should equal ignoring span and syntax context
197-
assert_eq!(
198-
expr_ref(&meaningful_ident_expr),
199-
expr_ref(&dummy_ident_expr)
200-
);
201-
202-
assert_eq!(
203-
expr_ref(&meaningful_array_expr),
204-
expr_ref(&dummy_array_expr)
205-
);
206-
207-
let mut set = FxHashSet::from_iter([
208-
expr_ref(&meaningful_ident_expr),
209-
expr_ref(&meaningful_member_expr),
210-
expr_ref(&meaningful_null_expr),
211-
expr_ref(&meaningful_array_expr),
212-
]);
213-
214-
// Should produce the same hash value ignoring span and syntax context
215-
assert!(set.contains(&expr_ref(&dummy_ident_expr)));
216-
assert!(set.contains(&expr_ref(&dummy_member_expr)));
217-
assert!(set.contains(&expr_ref(&dummy_null_expr)));
218-
assert!(set.contains(&expr_ref(&dummy_array_expr)));
219-
220-
set.insert(expr_ref(&dummy_ident_expr));
221-
set.insert(expr_ref(&dummy_member_expr));
222-
set.insert(expr_ref(&dummy_null_expr));
223-
set.insert(expr_ref(&dummy_array_expr));
224-
assert_eq!(set.len(), 4);
225-
226-
// Should not equal ignoring span and syntax context
227-
let dummy_ident_expr = Expr::Ident(Ident::new("baz".into(), dummy_sp));
228-
let dummy_member_expr = member_expr!(dummy_sp, baz.bar);
229-
let dummy_arrow_expr = Box::new(Expr::Arrow(ArrowExpr::dummy()));
230-
assert!(!set.contains(&expr_ref(&dummy_ident_expr)));
231-
assert!(!set.contains(&expr_ref(&dummy_member_expr)));
232-
assert!(!set.contains(&expr_ref(&dummy_arrow_expr)));
233-
234-
Ok(())
235-
})
236-
.unwrap();
237-
}

‎crates/swc_ecma_transforms_optimization/src/inline_globals.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@ use swc_atoms::{js_word, JsWord};
22
use swc_common::{
33
collections::{AHashMap, AHashSet},
44
sync::Lrc,
5-
EqIgnoreSpan,
65
};
76
use swc_ecma_ast::*;
87
use swc_ecma_transforms_base::perf::Parallel;
98
use swc_ecma_transforms_macros::parallel;
10-
use swc_ecma_utils::collect_decls;
9+
use swc_ecma_utils::{collect_decls, NodeIgnoringSpan};
1110
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
1211

1312
/// The key will be compared using [EqIgnoreSpan::eq_ignore_span], and matched
1413
/// expressions will be replaced with the value.
15-
pub type GlobalExprMap = Lrc<Vec<(Expr, Expr)>>;
14+
pub type GlobalExprMap = Lrc<AHashMap<NodeIgnoringSpan<'static, Expr>, Expr>>;
1615

1716
/// Create a global inlining pass, which replaces expressions with the specified
1817
/// value.
@@ -49,7 +48,7 @@ pub fn inline_globals2(
4948
struct InlineGlobals {
5049
envs: Lrc<AHashMap<JsWord, Expr>>,
5150
globals: Lrc<AHashMap<JsWord, Expr>>,
52-
global_exprs: GlobalExprMap,
51+
global_exprs: Lrc<AHashMap<NodeIgnoringSpan<'static, Expr>, Expr>>,
5352

5453
typeofs: Lrc<AHashMap<JsWord, JsWord>>,
5554

@@ -93,12 +92,12 @@ impl VisitMut for InlineGlobals {
9392
}
9493
}
9594

96-
for (key, value) in self.global_exprs.iter() {
97-
if Ident::within_ignored_ctxt(|| key.eq_ignore_span(&*expr)) {
98-
*expr = value.clone();
99-
expr.visit_mut_with(self);
100-
return;
101-
}
95+
if let Some(value) =
96+
Ident::within_ignored_ctxt(|| self.global_exprs.get(&NodeIgnoringSpan::borrowed(expr)))
97+
{
98+
*expr = value.clone();
99+
expr.visit_mut_with(self);
100+
return;
102101
}
103102

104103
expr.visit_mut_children_with(self);

‎crates/swc_ecma_utils/Cargo.toml

+23-22
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
[package]
2-
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
3-
description = "Utilities for swc ecmascript ast nodes"
2+
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
3+
description = "Utilities for swc ecmascript ast nodes"
44
documentation = "https://rustdoc.swc.rs/swc_ecma_utils/"
5-
edition = "2021"
6-
license = "Apache-2.0"
7-
name = "swc_ecma_utils"
8-
repository = "https://github.com/swc-project/swc.git"
9-
version = "0.111.4"
5+
edition = "2021"
6+
license = "Apache-2.0"
7+
name = "swc_ecma_utils"
8+
repository = "https://github.com/swc-project/swc.git"
9+
version = "0.111.4"
1010

11-
[package.metadata.docs.rs]
12-
all-features = true
13-
rustdoc-args = ["--cfg", "docsrs"]
11+
[package.metadata.docs.rs]
12+
all-features = true
13+
rustdoc-args = ["--cfg", "docsrs"]
1414

1515
[lib]
1616
bench = false
@@ -20,17 +20,18 @@ bench = false
2020
concurrent = ["swc_common/concurrent", "rayon"]
2121

2222
[dependencies]
23-
indexmap = "1.6.1"
24-
num_cpus = "1.13.1"
25-
once_cell = "1.10.0"
26-
rayon = {version = "1.5.1", optional = true}
27-
rustc-hash = "1.1.0"
28-
swc_atoms = {version = "0.4.39", path = "../swc_atoms"}
29-
swc_common = {version = "0.29.35", path = "../swc_common"}
30-
swc_ecma_ast = {version = "0.98.2", path = "../swc_ecma_ast"}
31-
swc_ecma_visit = {version = "0.84.2", path = "../swc_ecma_visit"}
32-
tracing = "0.1.32"
33-
unicode-id = "0.3"
23+
indexmap = "1.6.1"
24+
num_cpus = "1.13.1"
25+
once_cell = "1.10.0"
26+
rayon = { version = "1.5.1", optional = true }
27+
rustc-hash = "1.1.0"
28+
swc_atoms = { version = "0.4.39", path = "../swc_atoms" }
29+
swc_common = { version = "0.29.35", path = "../swc_common" }
30+
swc_ecma_ast = { version = "0.98.2", path = "../swc_ecma_ast" }
31+
swc_ecma_visit = { version = "0.84.2", path = "../swc_ecma_visit" }
32+
tracing = "0.1.32"
33+
unicode-id = "0.3"
3434

3535
[dev-dependencies]
36-
swc_ecma_parser = {version = "0.128.4", path = "../swc_ecma_parser"}
36+
swc_ecma_parser = { version = "0.128.4", path = "../swc_ecma_parser" }
37+
testing = { version = "0.31.37", path = "../testing" }

‎crates/swc_ecma_utils/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pub mod parallel;
5353
mod value;
5454
pub mod var;
5555

56+
mod node_ignore_span;
57+
pub use node_ignore_span::NodeIgnoringSpan;
58+
5659
// TODO: remove
5760
pub struct ThisVisitor {
5861
found: bool,

0 commit comments

Comments
 (0)