Skip to content

Commit 2ad0af9

Browse files
authored
fix(es/transforms/compat): Improve performance (#1673)
swc_ecma_transforms_compat: - `classes`: Fast-path. - `destructuring`: Fast-path. - `sticky_regex`: Fast-path. - `spread`: Fast-path. - `shorthand`: Fast-path. - `typeof_symbol`: Fast-path. - `block_scoped_functions`: Fast path. - `duplicate_keys`: Use fxhash. - `instance_of`: Fast path. - `reserved_words`: Make string comparison efficient.
1 parent 9381d0d commit 2ad0af9

File tree

16 files changed

+283
-160
lines changed

16 files changed

+283
-160
lines changed

atoms/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
//! [JsWord] is an interened string.
2+
//!
3+
//! This type should be used instead of [String] for values, because lots of
4+
//! values are duplicated. For example, if an identifer is named `myVariable`,
5+
//! there will be lots of identifier usages with the value `myVariable`.
6+
//!
7+
//! This type
8+
//! - makes equality comparison faster.
9+
//! - reduces memory usage.
10+
111
#![allow(clippy::unreadable_literal)]
212

313
include!(concat!(env!("OUT_DIR"), "/js_word.rs"));

common/src/syntax_pos.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,20 @@ impl Globals {
6363
}
6464
}
6565

66-
// scoped_thread_local!(pub static GLOBALS: Globals);
66+
/// Storage for span hygiene data.
67+
///
68+
/// This variable is used to manage identifiers or to identify nodes.
69+
/// Note that it's stored as a thread-local storage, but actually it's shared
70+
/// between threads.
71+
///
72+
/// # Usages
73+
///
74+
/// ## Span hygiene
75+
///
76+
/// [Mark]s are stored in this variable.
77+
///
78+
/// You can see the document how swc uses the span hygiene info at
79+
/// https://rustdoc.swc.rs/swc_ecma_transforms_base/resolver/fn.resolver_with_mark.html
6780
pub static GLOBALS: ::scoped_tls::ScopedKey<Globals> = ::scoped_tls::ScopedKey {
6881
inner: {
6982
thread_local!(static FOO: ::std::cell::Cell<usize> = {

ecmascript/parser/benches/parser.rs

Lines changed: 8 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
extern crate test;
44

5+
use swc_common::comments::SingleThreadedComments;
56
use swc_common::FileName;
67
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
78
use test::Bencher;
@@ -71,97 +72,21 @@ fn yui(b: &mut Bencher) {
7172
bench_module(b, Default::default(), include_str!("./files/yui-3.12.0.js"))
7273
}
7374

74-
#[bench]
75-
fn colors_ts(b: &mut Bencher) {
76-
// Copied from ratel-rust
77-
bench_module(
78-
b,
79-
Syntax::Typescript(Default::default()),
80-
include_str!("../colors.js"),
81-
)
82-
}
83-
84-
#[bench]
85-
fn angular_ts(b: &mut Bencher) {
86-
bench_module(
87-
b,
88-
Syntax::Typescript(Default::default()),
89-
include_str!("./files/angular-1.2.5.js"),
90-
)
91-
}
92-
93-
#[bench]
94-
fn backbone_ts(b: &mut Bencher) {
95-
bench_module(
96-
b,
97-
Syntax::Typescript(Default::default()),
98-
include_str!("./files/backbone-1.1.0.js"),
99-
)
100-
}
101-
102-
#[bench]
103-
fn jquery_ts(b: &mut Bencher) {
104-
bench_module(
105-
b,
106-
Syntax::Typescript(Default::default()),
107-
include_str!("./files/jquery-1.9.1.js"),
108-
)
109-
}
110-
111-
#[bench]
112-
fn jquery_mobile_ts(b: &mut Bencher) {
113-
bench_module(
114-
b,
115-
Syntax::Typescript(Default::default()),
116-
include_str!("./files/jquery.mobile-1.4.2.js"),
117-
)
118-
}
119-
120-
#[bench]
121-
fn mootools_ts(b: &mut Bencher) {
122-
bench_module(
123-
b,
124-
Syntax::Typescript(Default::default()),
125-
include_str!("./files/mootools-1.4.5.js"),
126-
)
127-
}
128-
129-
#[bench]
130-
fn underscore_ts(b: &mut Bencher) {
131-
bench_module(
132-
b,
133-
Syntax::Typescript(Default::default()),
134-
include_str!("./files/underscore-1.5.2.js"),
135-
)
136-
}
137-
138-
#[bench]
139-
fn yui_ts(b: &mut Bencher) {
140-
bench_module(
141-
b,
142-
Syntax::Typescript(Default::default()),
143-
include_str!("./files/yui-3.12.0.js"),
144-
)
145-
}
146-
147-
#[bench]
148-
fn large(b: &mut Bencher) {
149-
bench_module(
150-
b,
151-
Syntax::Typescript(Default::default()),
152-
include_str!("../../codegen/benches/large-partial.js"),
153-
)
154-
}
155-
15675
fn bench_module(b: &mut Bencher, syntax: Syntax, src: &'static str) {
15776
b.bytes = src.len() as _;
15877

15978
let _ = ::testing::run_test(false, |cm, _| {
79+
let comments = SingleThreadedComments::default();
16080
let fm = cm.new_source_file(FileName::Anon, src.into());
16181

16282
b.iter(|| {
16383
let _ = test::black_box({
164-
let lexer = Lexer::new(syntax, Default::default(), StringInput::from(&*fm), None);
84+
let lexer = Lexer::new(
85+
syntax,
86+
Default::default(),
87+
StringInput::from(&*fm),
88+
Some(&comments),
89+
);
16590
let mut parser = Parser::new_from(lexer);
16691
parser.parse_module()
16792
});

ecmascript/transforms/base/src/helpers/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ macro_rules! add_to {
6060
}};
6161
}
6262

63-
scoped_thread_local!(pub static HELPERS: Helpers);
63+
scoped_thread_local!(
64+
/// This variable is used to manage helper scripts like `_inherits` from babel.
65+
///
66+
/// The instance contains flags where each flag denotes if a helper script should be injected.
67+
pub static HELPERS: Helpers
68+
);
6469

6570
/// Tracks used helper methods. (e.g. __extends)
6671
#[derive(Debug, Default)]

ecmascript/transforms/compat/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2018"
66
license = "Apache-2.0/MIT"
77
name = "swc_ecma_transforms_compat"
88
repository = "https://github.com/swc-project/swc.git"
9-
version = "0.16.1"
9+
version = "0.16.2"
1010
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1111

1212
[dependencies]

ecmascript/transforms/compat/src/es2015/block_scoped_fn.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
use swc_common::{Spanned, DUMMY_SP};
22
use swc_ecma_ast::*;
3+
use swc_ecma_transforms_base::perf::Check;
4+
use swc_ecma_transforms_macros::fast_path;
35
use swc_ecma_utils::UsageFinder;
6+
use swc_ecma_visit::noop_visit_type;
7+
use swc_ecma_visit::Node;
8+
use swc_ecma_visit::Visit;
9+
use swc_ecma_visit::VisitWith;
410
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
511

612
pub fn block_scoped_functions() -> impl Fold {
@@ -10,6 +16,7 @@ pub fn block_scoped_functions() -> impl Fold {
1016
#[derive(Clone, Copy)]
1117
struct BlockScopedFns;
1218

19+
#[fast_path(BlockScopedFnFinder)]
1320
impl Fold for BlockScopedFns {
1421
noop_fold_type!();
1522

@@ -61,6 +68,32 @@ impl Fold for BlockScopedFns {
6168
}
6269
}
6370

71+
#[derive(Default)]
72+
struct BlockScopedFnFinder {
73+
found: bool,
74+
}
75+
76+
impl Visit for BlockScopedFnFinder {
77+
noop_visit_type!();
78+
79+
fn visit_stmts(&mut self, stmts: &[Stmt], _: &dyn Node) {
80+
for n in stmts {
81+
n.visit_with(&Invalid { span: DUMMY_SP }, self);
82+
}
83+
84+
self.found |= stmts.iter().any(|stmt| match stmt {
85+
Stmt::Decl(Decl::Fn(..)) => true,
86+
_ => false,
87+
});
88+
}
89+
}
90+
91+
impl Check for BlockScopedFnFinder {
92+
fn should_handle(&self) -> bool {
93+
self.found
94+
}
95+
}
96+
6497
#[cfg(test)]
6598
mod tests {
6699
use super::*;

ecmascript/transforms/compat/src/es2015/classes/mod.rs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use swc_common::{Mark, Spanned, DUMMY_SP};
1313
use swc_ecma_ast::*;
1414
use swc_ecma_transforms_base::helper;
1515
use swc_ecma_transforms_base::native::is_native;
16+
use swc_ecma_transforms_base::perf::Check;
17+
use swc_ecma_transforms_macros::fast_path;
1618
use swc_ecma_utils::quote_expr;
1719
use swc_ecma_utils::quote_str;
1820
use swc_ecma_utils::{
@@ -187,6 +189,7 @@ where
187189
}
188190
}
189191

192+
#[fast_path(ClassFinder)]
190193
impl<C> Fold for Classes<C>
191194
where
192195
C: Comments,
@@ -202,26 +205,6 @@ where
202205
}
203206

204207
fn fold_decl(&mut self, n: Decl) -> Decl {
205-
fn should_work(node: &Decl) -> bool {
206-
struct Visitor {
207-
found: bool,
208-
}
209-
impl Visit for Visitor {
210-
noop_visit_type!();
211-
212-
fn visit_class(&mut self, _: &Class, _: &dyn Node) {
213-
self.found = true
214-
}
215-
}
216-
let mut v = Visitor { found: false };
217-
node.visit_with(&Invalid { span: DUMMY_SP } as _, &mut v);
218-
v.found
219-
}
220-
// fast path
221-
if !should_work(&n) {
222-
return n;
223-
}
224-
225208
let n = match n {
226209
Decl::Class(decl) => Decl::Var(self.fold_class_as_var_decl(decl.ident, decl.class)),
227210
_ => n,
@@ -968,3 +951,22 @@ fn escape_keywords(mut e: Box<Expr>) -> Box<Expr> {
968951

969952
e
970953
}
954+
955+
#[derive(Default)]
956+
struct ClassFinder {
957+
found: bool,
958+
}
959+
960+
impl Visit for ClassFinder {
961+
noop_visit_type!();
962+
963+
fn visit_class(&mut self, _: &Class, _: &dyn Node) {
964+
self.found = true
965+
}
966+
}
967+
968+
impl Check for ClassFinder {
969+
fn should_handle(&self) -> bool {
970+
self.found
971+
}
972+
}

ecmascript/transforms/compat/src/es2015/destructuring.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::iter;
33
use swc_common::{Spanned, SyntaxContext, DUMMY_SP};
44
use swc_ecma_ast::*;
55
use swc_ecma_transforms_base::helper;
6+
use swc_ecma_transforms_base::perf::Check;
7+
use swc_ecma_transforms_macros::fast_path;
68
use swc_ecma_utils::alias_ident_for;
79
use swc_ecma_utils::alias_if_required;
810
use swc_ecma_utils::has_rest_pat;
@@ -454,6 +456,7 @@ impl AssignFolder {
454456
}
455457
}
456458

459+
#[fast_path(DestructuringVisitor)]
457460
impl Fold for Destructuring {
458461
noop_fold_type!();
459462

@@ -527,6 +530,7 @@ struct AssignFolder {
527530
ignore_return_value: Option<()>,
528531
}
529532

533+
#[fast_path(DestructuringVisitor)]
530534
impl Fold for AssignFolder {
531535
noop_fold_type!();
532536

@@ -1129,6 +1133,7 @@ where
11291133
v.found
11301134
}
11311135

1136+
#[derive(Default)]
11321137
struct DestructuringVisitor {
11331138
found: bool,
11341139
}
@@ -1144,3 +1149,9 @@ impl Visit for DestructuringVisitor {
11441149
}
11451150
}
11461151
}
1152+
1153+
impl Check for DestructuringVisitor {
1154+
fn should_handle(&self) -> bool {
1155+
self.found
1156+
}
1157+
}

ecmascript/transforms/compat/src/es2015/duplicate_keys.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashSet;
1+
use fxhash::FxHashSet;
22
use swc_atoms::JsWord;
33
use swc_common::Spanned;
44
use swc_ecma_ast::*;
@@ -34,8 +34,8 @@ impl Fold for DuplicateKeys {
3434

3535
#[derive(Default)]
3636
struct PropFolder {
37-
getter_props: HashSet<JsWord>,
38-
setter_props: HashSet<JsWord>,
37+
getter_props: FxHashSet<JsWord>,
38+
setter_props: FxHashSet<JsWord>,
3939
}
4040

4141
impl Fold for PropFolder {
@@ -85,7 +85,7 @@ impl Fold for PropFolder {
8585
}
8686

8787
struct PropNameFolder<'a> {
88-
props: &'a mut HashSet<JsWord>,
88+
props: &'a mut FxHashSet<JsWord>,
8989
}
9090
impl<'a> Fold for PropNameFolder<'a> {
9191
noop_fold_type!();

0 commit comments

Comments
 (0)