Skip to content

Commit 03dffb5

Browse files
authoredJan 26, 2025··
perf(preset-env): Store Versions in Arc (#9950)
**Description:** The `Versions` type is too big to use as a type with `Copy` implementation.
1 parent 1bf837e commit 03dffb5

File tree

11 files changed

+202
-182
lines changed

11 files changed

+202
-182
lines changed
 

‎.changeset/friendly-gifts-retire.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
preset_env_base: major
3+
---
4+
5+
perf(preset-env): Reduce memory usage of `preset-env`

‎crates/preset_env_base/src/query.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Module for `browserslist` queries.
22
#![deny(clippy::all)]
33

4-
use std::collections::HashMap;
4+
use std::{collections::HashMap, sync::Arc};
55

66
use anyhow::{Context, Error};
77
use dashmap::DashMap;
@@ -44,7 +44,7 @@ pub enum Query {
4444
Multiple(Vec<String>),
4545
}
4646

47-
type QueryResult = Result<Versions, Error>;
47+
type QueryResult = Result<Arc<Versions>, Error>;
4848

4949
impl Query {
5050
fn exec(&self) -> QueryResult {
@@ -70,14 +70,14 @@ impl Query {
7070
let versions =
7171
BrowserData::parse_versions(distribs).expect("failed to parse browser version");
7272

73-
Ok(versions)
73+
Ok(Arc::new(versions))
7474
}
7575

76-
static CACHE: Lazy<DashMap<Query, Versions, ahash::RandomState>> =
76+
static CACHE: Lazy<DashMap<Query, Arc<Versions>, ahash::RandomState>> =
7777
Lazy::new(Default::default);
7878

7979
if let Some(v) = CACHE.get(self) {
80-
return Ok(*v);
80+
return Ok(v.clone());
8181
}
8282

8383
let result = match *self {
@@ -92,16 +92,16 @@ impl Query {
9292
}
9393
.context("failed to execute query")?;
9494

95-
CACHE.insert(self.clone(), result);
95+
CACHE.insert(self.clone(), result.clone());
9696

9797
Ok(result)
9898
}
9999
}
100100

101-
pub fn targets_to_versions(v: Option<Targets>) -> Result<Versions, Error> {
101+
pub fn targets_to_versions(v: Option<Targets>) -> Result<Arc<Versions>, Error> {
102102
match v {
103103
None => Ok(Default::default()),
104-
Some(Targets::Versions(v)) => Ok(v),
104+
Some(Targets::Versions(v)) => Ok(Arc::new(v)),
105105
Some(Targets::Query(q)) => q
106106
.exec()
107107
.context("failed to convert target query to version data"),
@@ -117,9 +117,10 @@ pub fn targets_to_versions(v: Option<Targets>) -> Result<Versions, Error> {
117117
});
118118

119119
if map.is_empty() {
120-
if let Some(mut q) = q {
120+
if let Some(q) = q {
121+
let mut q = *q;
121122
q.node = node;
122-
return Ok(q);
123+
return Ok(Arc::new(q));
123124
}
124125
}
125126

@@ -129,8 +130,8 @@ pub fn targets_to_versions(v: Option<Targets>) -> Result<Versions, Error> {
129130
QueryOrVersion::Query(q) => {
130131
let v = q.exec().context("failed to run query")?;
131132

132-
for (k, v) in v {
133-
result.insert(k, v);
133+
for (k, v) in v.iter() {
134+
result.insert(k, *v);
134135
}
135136
}
136137
QueryOrVersion::Version(v) => {

‎crates/preset_env_base/src/version.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl<'de> Deserialize<'de> for Version {
158158
}
159159
}
160160

161-
pub fn should_enable(target: Versions, feature: Versions, default: bool) -> bool {
161+
pub fn should_enable(target: &Versions, feature: &Versions, default: bool) -> bool {
162162
if target
163163
.iter()
164164
.zip(feature.iter())
@@ -193,11 +193,11 @@ mod tests {
193193
#[test]
194194
fn should_enable_android_falls_back_to_chrome() {
195195
assert!(!should_enable(
196-
BrowserData {
196+
&BrowserData {
197197
android: Some("51.0.0".parse().unwrap()),
198198
..Default::default()
199199
},
200-
BrowserData {
200+
&BrowserData {
201201
chrome: Some("51.0.0".parse().unwrap()),
202202
..Default::default()
203203
},

‎crates/swc_css_prefixer/src/prefixer.rs

+125-122
Large diffs are not rendered by default.

‎crates/swc_ecma_preset_env/src/corejs2/entry.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use indexmap::IndexSet;
24
use preset_env_base::{version::should_enable, Versions};
35
use swc_atoms::js_word;
@@ -10,12 +12,12 @@ use super::builtin::BUILTINS;
1012
#[derive(Debug)]
1113
pub struct Entry {
1214
is_any_target: bool,
13-
target: Versions,
15+
target: Arc<Versions>,
1416
pub imports: IndexSet<&'static str, ARandomState>,
1517
}
1618

1719
impl Entry {
18-
pub fn new(target: Versions, regenerator: bool) -> Self {
20+
pub fn new(target: Arc<Versions>, regenerator: bool) -> Self {
1921
let is_any_target = target.is_any_target();
2022
let is_web_target = target.into_iter().any(|(k, v)| {
2123
if k == "node" {
@@ -51,14 +53,14 @@ impl Entry {
5153
}
5254

5355
for (feature, version) in BUILTINS.iter() {
54-
self.add_inner(feature, *version);
56+
self.add_inner(feature, version);
5557
}
5658

5759
true
5860
}
5961

60-
fn add_inner(&mut self, feature: &'static str, version: Versions) {
61-
if self.is_any_target || should_enable(self.target, version, true) {
62+
fn add_inner(&mut self, feature: &'static str, version: &Versions) {
63+
if self.is_any_target || should_enable(&self.target, version, true) {
6264
self.imports.insert(feature);
6365
}
6466
}

‎crates/swc_ecma_preset_env/src/corejs2/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use indexmap::IndexSet;
24
use preset_env_base::{version::should_enable, Versions};
35
use swc_atoms::JsWord;
@@ -18,12 +20,12 @@ mod entry;
1820

1921
pub(crate) struct UsageVisitor {
2022
is_any_target: bool,
21-
target: Versions,
23+
target: Arc<Versions>,
2224
pub required: IndexSet<&'static str, ARandomState>,
2325
}
2426

2527
impl UsageVisitor {
26-
pub fn new(target: Versions) -> Self {
28+
pub fn new(target: Arc<Versions>) -> Self {
2729
// let mut v = Self { required: Vec::new() };
2830
//
2931
//
@@ -66,7 +68,7 @@ impl UsageVisitor {
6668
if !*is_any_target {
6769
if let Some(v) = BUILTINS.get(&***f) {
6870
// Skip
69-
if !should_enable(*target, *v, true) {
71+
if !should_enable(target, v, true) {
7072
return false;
7173
}
7274
}

‎crates/swc_ecma_preset_env/src/corejs3/entry.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use indexmap::IndexSet;
24
use once_cell::sync::Lazy;
35
use preset_env_base::{
@@ -34,14 +36,14 @@ static ENTRIES: Lazy<AHashMap<String, Vec<&'static str>>> = Lazy::new(|| {
3436
#[derive(Debug)]
3537
pub struct Entry {
3638
is_any_target: bool,
37-
target: Versions,
39+
target: Arc<Versions>,
3840
corejs_version: Version,
3941
pub imports: IndexSet<&'static str, ARandomState>,
4042
remove_regenerator: bool,
4143
}
4244

4345
impl Entry {
44-
pub fn new(target: Versions, corejs_version: Version, remove_regenerator: bool) -> Self {
46+
pub fn new(target: Arc<Versions>, corejs_version: Version, remove_regenerator: bool) -> Self {
4547
assert_eq!(corejs_version.major, 3);
4648

4749
Entry {
@@ -74,7 +76,7 @@ impl Entry {
7476

7577
if !*is_any_target {
7678
if let Some(feature) = feature {
77-
if !should_enable(*target, *feature, true) {
79+
if !should_enable(target, feature, true) {
7880
return false;
7981
}
8082
}

‎crates/swc_ecma_preset_env/src/corejs3/usage.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use indexmap::IndexSet;
24
use preset_env_base::version::{should_enable, Version};
35
use swc_atoms::JsWord;
@@ -18,13 +20,13 @@ use crate::{
1820
pub(crate) struct UsageVisitor {
1921
shipped_proposals: bool,
2022
is_any_target: bool,
21-
target: Versions,
23+
target: Arc<Versions>,
2224
corejs_version: Version,
2325
pub required: IndexSet<&'static str, ARandomState>,
2426
}
2527

2628
impl UsageVisitor {
27-
pub fn new(target: Versions, shipped_proposals: bool, corejs_version: Version) -> Self {
29+
pub fn new(target: Arc<Versions>, shipped_proposals: bool, corejs_version: Version) -> Self {
2830
// let mut v = Self { required: Vec::new() };
2931
//
3032
//
@@ -80,7 +82,7 @@ impl UsageVisitor {
8082

8183
if !*is_any_target {
8284
if let Some(feature) = feature {
83-
if !should_enable(*target, *feature, true) {
85+
if !should_enable(target, feature, true) {
8486
return false;
8587
}
8688
}

‎crates/swc_ecma_preset_env/src/lib.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![allow(dead_code)]
33
#![recursion_limit = "256"]
44

5-
use std::path::PathBuf;
5+
use std::{path::PathBuf, sync::Arc};
66

77
use preset_env_base::query::targets_to_versions;
88
pub use preset_env_base::{query::Targets, version::Version, BrowserData, Versions};
@@ -47,7 +47,7 @@ where
4747
C: Comments + Clone,
4848
{
4949
let loose = c.loose;
50-
let targets: Versions = targets_to_versions(c.targets).expect("failed to parse targets");
50+
let targets = targets_to_versions(c.targets).expect("failed to parse targets");
5151
let is_any_target = targets.is_any_target();
5252

5353
let (include, included_modules) = FeatureOrModule::split(c.include);
@@ -62,7 +62,7 @@ where
6262
&& (c.force_all_transforms
6363
|| (is_any_target
6464
|| include.contains(&f)
65-
|| f.should_enable(targets, c.bugfixes, $default)))
65+
|| f.should_enable(&targets, c.bugfixes, $default)))
6666
}};
6767
}
6868

@@ -355,7 +355,7 @@ where
355355
#[derive(Debug)]
356356
struct Polyfills {
357357
mode: Option<Mode>,
358-
targets: Versions,
358+
targets: Arc<Versions>,
359359
shipped_proposals: bool,
360360
corejs: Version,
361361
regenerator: bool,
@@ -376,14 +376,14 @@ impl Polyfills {
376376
Some(Mode::Usage) => {
377377
let mut r = match self.corejs {
378378
Version { major: 2, .. } => {
379-
let mut v = corejs2::UsageVisitor::new(self.targets);
379+
let mut v = corejs2::UsageVisitor::new(self.targets.clone());
380380
m.visit_with(&mut v);
381381

382382
v.required
383383
}
384384
Version { major: 3, .. } => {
385385
let mut v = corejs3::UsageVisitor::new(
386-
self.targets,
386+
self.targets.clone(),
387387
self.shipped_proposals,
388388
self.corejs,
389389
);
@@ -402,13 +402,14 @@ impl Polyfills {
402402
}
403403
Some(Mode::Entry) => match self.corejs {
404404
Version { major: 2, .. } => {
405-
let mut v = corejs2::Entry::new(self.targets, self.regenerator);
405+
let mut v = corejs2::Entry::new(self.targets.clone(), self.regenerator);
406406
m.visit_mut_with(&mut v);
407407
v.imports
408408
}
409409

410410
Version { major: 3, .. } => {
411-
let mut v = corejs3::Entry::new(self.targets, self.corejs, !self.regenerator);
411+
let mut v =
412+
corejs3::Entry::new(self.targets.clone(), self.corejs, !self.regenerator);
412413
m.visit_mut_with(&mut v);
413414
v.imports
414415
}

‎crates/swc_ecma_preset_env/src/transform_data.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use string_enum::StringEnum;
77
use swc_common::collections::AHashMap;
88

99
impl Feature {
10-
pub fn should_enable(self, target: Versions, bugfixes: bool, default: bool) -> bool {
10+
pub fn should_enable(self, target: &Versions, bugfixes: bool, default: bool) -> bool {
1111
let f = if bugfixes {
1212
&BUGFIX_FEATURES[&self]
1313
} else {
@@ -17,7 +17,7 @@ impl Feature {
1717
&FEATURES[&self]
1818
};
1919

20-
should_enable(target, *f, default)
20+
should_enable(target, f, default)
2121
}
2222
}
2323

@@ -256,7 +256,7 @@ mod tests {
256256
#[test]
257257
fn arrow() {
258258
assert!(Feature::ArrowFunctions.should_enable(
259-
BrowserData {
259+
&BrowserData {
260260
ie: Some("11.0.0".parse().unwrap()),
261261
..Default::default()
262262
},
@@ -268,7 +268,7 @@ mod tests {
268268
#[test]
269269
fn tpl_lit() {
270270
assert!(!Feature::TemplateLiterals.should_enable(
271-
BrowserData {
271+
&BrowserData {
272272
chrome: Some("71.0.0".parse().unwrap()),
273273
..Default::default()
274274
},
@@ -281,7 +281,7 @@ mod tests {
281281
fn tpl_lit_bugfixes() {
282282
// Enable template literals pass in Safari 9 without bugfixes option
283283
assert!(Feature::TemplateLiterals.should_enable(
284-
BrowserData {
284+
&BrowserData {
285285
safari: Some("9.0.0".parse().unwrap()),
286286
..Default::default()
287287
},
@@ -290,7 +290,7 @@ mod tests {
290290
));
291291

292292
assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
293-
BrowserData {
293+
&BrowserData {
294294
safari: Some("10.0.0".parse().unwrap()),
295295
..Default::default()
296296
},
@@ -300,7 +300,7 @@ mod tests {
300300

301301
// Don't enable it with the bugfixes option. Bugfix pass enabled instead.
302302
assert!(!Feature::TemplateLiterals.should_enable(
303-
BrowserData {
303+
&BrowserData {
304304
safari: Some("9.0.0".parse().unwrap()),
305305
..Default::default()
306306
},
@@ -309,7 +309,7 @@ mod tests {
309309
));
310310

311311
assert!(Feature::BugfixTaggedTemplateCaching.should_enable(
312-
BrowserData {
312+
&BrowserData {
313313
safari: Some("9.0.0".parse().unwrap()),
314314
..Default::default()
315315
},
@@ -318,7 +318,7 @@ mod tests {
318318
));
319319

320320
assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
321-
BrowserData {
321+
&BrowserData {
322322
safari: Some("13.0.0".parse().unwrap()),
323323
..Default::default()
324324
},
@@ -331,7 +331,7 @@ mod tests {
331331
fn edge_default_param_bug() {
332332
// Enable params pass in Edge 17 without bugfixes option
333333
assert!(Feature::Parameters.should_enable(
334-
BrowserData {
334+
&BrowserData {
335335
edge: Some("17.0.0".parse().unwrap()),
336336
..Default::default()
337337
},
@@ -340,7 +340,7 @@ mod tests {
340340
));
341341

342342
assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
343-
BrowserData {
343+
&BrowserData {
344344
edge: Some("17.0.0".parse().unwrap()),
345345
..Default::default()
346346
},
@@ -350,7 +350,7 @@ mod tests {
350350

351351
// Don't enable it with the bugfixes option. Bugfix pass enabled instead.
352352
assert!(!Feature::Parameters.should_enable(
353-
BrowserData {
353+
&BrowserData {
354354
edge: Some("17.0.0".parse().unwrap()),
355355
..Default::default()
356356
},
@@ -359,7 +359,7 @@ mod tests {
359359
));
360360

361361
assert!(Feature::BugfixEdgeDefaultParam.should_enable(
362-
BrowserData {
362+
&BrowserData {
363363
edge: Some("17.0.0".parse().unwrap()),
364364
..Default::default()
365365
},
@@ -368,7 +368,7 @@ mod tests {
368368
));
369369

370370
assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
371-
BrowserData {
371+
&BrowserData {
372372
edge: Some("18.0.0".parse().unwrap()),
373373
..Default::default()
374374
},
@@ -381,7 +381,7 @@ mod tests {
381381
fn async_arrows_in_class_bug() {
382382
// Enable async to generator pass in Safari 10.1 without bugfixes option
383383
assert!(Feature::AsyncToGenerator.should_enable(
384-
BrowserData {
384+
&BrowserData {
385385
safari: Some("10.1.0".parse().unwrap()),
386386
..Default::default()
387387
},
@@ -390,7 +390,7 @@ mod tests {
390390
));
391391

392392
assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
393-
BrowserData {
393+
&BrowserData {
394394
safari: Some("10.1.0".parse().unwrap()),
395395
..Default::default()
396396
},
@@ -400,7 +400,7 @@ mod tests {
400400

401401
// Don't enable it with the bugfixes option. Bugfix pass enabled instead.
402402
assert!(!Feature::AsyncToGenerator.should_enable(
403-
BrowserData {
403+
&BrowserData {
404404
safari: Some("10.1.0".parse().unwrap()),
405405
..Default::default()
406406
},
@@ -409,7 +409,7 @@ mod tests {
409409
));
410410

411411
assert!(Feature::BugfixAsyncArrowsInClass.should_enable(
412-
BrowserData {
412+
&BrowserData {
413413
safari: Some("10.1.0".parse().unwrap()),
414414
..Default::default()
415415
},
@@ -418,7 +418,7 @@ mod tests {
418418
));
419419

420420
assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
421-
BrowserData {
421+
&BrowserData {
422422
safari: Some("11.1.0".parse().unwrap()),
423423
..Default::default()
424424
},
@@ -431,7 +431,7 @@ mod tests {
431431
fn block_scoping() {
432432
// Enable block scoping pass in Safari 10 without bugfixes option
433433
assert!(Feature::BlockScoping.should_enable(
434-
BrowserData {
434+
&BrowserData {
435435
safari: Some("10.0.0".parse().unwrap()),
436436
..Default::default()
437437
},
@@ -441,7 +441,7 @@ mod tests {
441441

442442
// Don't enable it with the bugfixes option.
443443
assert!(!Feature::BlockScoping.should_enable(
444-
BrowserData {
444+
&BrowserData {
445445
safari: Some("10.0.0".parse().unwrap()),
446446
..Default::default()
447447
},

‎crates/swc_ecma_utils/src/function/fn_env_hoister.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::mem;
22

33
use indexmap::IndexMap;
44
use swc_atoms::JsWord;
5-
use swc_common::{util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP};
5+
use swc_common::{
6+
collections::ARandomState, util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP,
7+
};
68
use swc_ecma_ast::*;
79
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
810

@@ -11,7 +13,7 @@ use crate::ExprFactory;
1113
#[derive(Default)]
1214
struct SuperField {
1315
computed: Option<Ident>,
14-
ident: IndexMap<JsWord, Ident>,
16+
ident: IndexMap<JsWord, Ident, ARandomState>,
1517
}
1618

1719
/// Don't use it against function, it will stop if come across any function

0 commit comments

Comments
 (0)
Please sign in to comment.