Skip to content

Commit

Permalink
perf(visit): Introduce Pass API and adjust visitor APIs for it (#9680)
Browse files Browse the repository at this point in the history
**Description:**

 - `Pass`: `FnMut(&mut Program)`.

**Breaking Changes:**

- `chain!`: Use a tuple instead. You can replace all `chain!(` with `(` with IDE feature and it will work.
- `chain!` with 13 or more args: Use nested tuples for items after 13th element.


**Related issue:**

 - Related to #9601
  • Loading branch information
kdy1 authored Oct 29, 2024
1 parent b32a533 commit 581aafb
Show file tree
Hide file tree
Showing 209 changed files with 2,496 additions and 2,045 deletions.
7 changes: 7 additions & 0 deletions .changeset/few-pigs-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
swc_visit: major
swc_common: major
swc_plugin_proxy: major
---

perf(visit): Introduce `Pass` API and adjust visitor APIs for it
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bindings/binding_minifier_node/src/minify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ fn do_work(
let is_mangler_enabled = min_opts.mangle.is_some();

let module = {
let module = module.fold_with(&mut resolver(unresolved_mark, top_level_mark, false));
let module = module.apply(resolver(unresolved_mark, top_level_mark, false));

let mut module = swc_core::ecma::minifier::optimize(
module,
Expand Down
6 changes: 4 additions & 2 deletions crates/binding_macros/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ pub use swc_common::{
};
use swc_common::{sync::Lrc, FilePathMapping, SourceMap};
#[doc(hidden)]
pub use swc_ecma_ast::noop_pass;
#[doc(hidden)]
pub use swc_ecma_ast::{EsVersion, Program};
#[doc(hidden)]
pub use swc_ecma_transforms::{pass::noop, resolver};
pub use swc_ecma_transforms::resolver;
#[doc(hidden)]
pub use swc_ecma_visit::VisitMutWith;
#[doc(hidden)]
Expand Down Expand Up @@ -293,7 +295,7 @@ macro_rules! build_print {
#[macro_export]
macro_rules! build_transform_sync {
($(#[$m:meta])*) => {
build_transform_sync!($(#[$m])*, |_| $crate::wasm::noop(), |_| $crate::wasm::noop(), Default::default());
build_transform_sync!($(#[$m])*, |_| $crate::wasm::noop_pass(), |_| $crate::wasm::noop_pass(), Default::default());
};
($(#[$m:meta])*, $before_pass: expr, $after_pass: expr) => {
build_transform_sync!($(#[$m])*, $before_pass, $after_pass, Default::default());
Expand Down
7 changes: 3 additions & 4 deletions crates/swc/benches/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use swc_compiler_base::PrintArgs;
use swc_ecma_ast::{EsVersion, Program};
use swc_ecma_parser::Syntax;
use swc_ecma_transforms::{fixer, resolver, typescript};
use swc_ecma_visit::FoldWith;

static SOURCE: &str = include_str!("assets/Observable.ts");

Expand Down Expand Up @@ -52,8 +51,8 @@ fn as_es(c: &swc::Compiler) -> Program {
let top_level_mark = Mark::new();

program
.fold_with(&mut resolver(unresolved_mark, top_level_mark, true))
.fold_with(&mut typescript::strip(unresolved_mark, top_level_mark))
.apply(&mut resolver(unresolved_mark, top_level_mark, true))
.apply(&mut typescript::strip(unresolved_mark, top_level_mark))
}

fn base_tr_group(c: &mut Criterion) {
Expand All @@ -73,7 +72,7 @@ fn base_tr_fixer(b: &mut Bencher) {
GLOBALS.set(&Default::default(), || {
let handler = Handler::with_emitter_writer(Box::new(stderr()), Some(c.cm.clone()));
black_box(c.run_transform(&handler, true, || {
module.clone().fold_with(&mut fixer(Some(c.comments())))
module.clone().apply(&mut fixer(Some(c.comments())))
}))
})
});
Expand Down
96 changes: 46 additions & 50 deletions crates/swc/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ use either::Either;
use rustc_hash::FxHashMap;
use swc_atoms::JsWord;
use swc_common::{
chain, comments::Comments, errors::Handler, sync::Lrc, util::take::Take, FileName, Mark,
SourceMap,
comments::Comments, errors::Handler, sync::Lrc, util::take::Take, FileName, Mark, SourceMap,
};
use swc_ecma_ast::{EsVersion, Module, Script};
use swc_ecma_ast::{EsVersion, Module, Pass, Script};
use swc_ecma_minifier::option::{terser::TerserTopLevelOptions, MinifyOptions};
use swc_ecma_parser::Syntax;
use swc_ecma_transforms::{
Expand All @@ -19,15 +18,15 @@ use swc_ecma_transforms::{
hygiene::{self, hygiene_with_config},
modules::{self, path::ImportResolver},
optimization::const_modules,
pass::Optional,
resolver, Assumptions,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
use swc_visit::Optional;

use crate::config::{GlobalPassOption, JsMinifyOptions, ModuleConfig};

/// Builder is used to create a high performance `Compiler`.
pub struct PassBuilder<'a, 'b, P: swc_ecma_visit::Fold> {
pub struct PassBuilder<'a, 'b, P: Pass> {
cm: &'a Arc<SourceMap>,
handler: &'b Handler,
env: Option<swc_ecma_preset_env::Config>,
Expand All @@ -48,7 +47,7 @@ pub struct PassBuilder<'a, 'b, P: swc_ecma_visit::Fold> {
regenerator: regenerator::Config,
}

impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
impl<'a, 'b, P: Pass> PassBuilder<'a, 'b, P> {
pub fn new(
cm: &'a Arc<SourceMap>,
handler: &'b Handler,
Expand Down Expand Up @@ -76,11 +75,11 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
}
}

pub fn then<N>(self, next: N) -> PassBuilder<'a, 'b, swc_visit::AndThen<P, N>>
pub fn then<N>(self, next: N) -> PassBuilder<'a, 'b, (P, N)>
where
N: swc_ecma_visit::Fold,
N: Pass,
{
let pass = chain!(self.pass, next);
let pass = (self.pass, next);
PassBuilder {
cm: self.cm,
handler: self.handler,
Expand Down Expand Up @@ -126,15 +125,12 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
pub fn const_modules(
self,
globals: FxHashMap<JsWord, FxHashMap<JsWord, String>>,
) -> PassBuilder<'a, 'b, impl swc_ecma_visit::Fold> {
) -> PassBuilder<'a, 'b, (P, impl Pass)> {
let cm = self.cm.clone();
self.then(const_modules(cm, globals))
}

pub fn inline_globals(
self,
c: GlobalPassOption,
) -> PassBuilder<'a, 'b, impl swc_ecma_visit::Fold> {
pub fn inline_globals(self, c: GlobalPassOption) -> PassBuilder<'a, 'b, (P, impl Pass)> {
let pass = c.build(self.cm, self.handler);
self.then(pass)
}
Expand Down Expand Up @@ -172,7 +168,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
module: Option<ModuleConfig>,
comments: Option<&'cmt dyn Comments>,
resolver: Option<(FileName, Arc<dyn ImportResolver>)>,
) -> impl 'cmt + swc_ecma_visit::Fold
) -> impl 'cmt + Pass
where
P: 'cmt,
{
Expand Down Expand Up @@ -206,7 +202,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {

feature_flag = enable_available_feature_from_es_version(self.target);

Either::Right(chain!(
Either::Right((
Optional::new(
compat::class_fields_use_set::class_fields_use_set(assumptions.pure_getters),
assumptions.set_public_class_fields,
Expand All @@ -221,60 +217,60 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
no_document_all: assumptions.no_document_all,
static_blocks_mark: Mark::new(),
pure_getter: assumptions.pure_getters,
}
},
},
self.unresolved_mark
self.unresolved_mark,
),
should_enable(self.target, EsVersion::Es2022)
should_enable(self.target, EsVersion::Es2022),
),
Optional::new(
compat::es2021::es2021(),
should_enable(self.target, EsVersion::Es2021)
should_enable(self.target, EsVersion::Es2021),
),
Optional::new(
compat::es2020::es2020(
compat::es2020::Config {
nullish_coalescing: compat::es2020::nullish_coalescing::Config {
no_document_all: assumptions.no_document_all
no_document_all: assumptions.no_document_all,
},
optional_chaining: compat::es2020::optional_chaining::Config {
no_document_all: assumptions.no_document_all,
pure_getter: assumptions.pure_getters
}
pure_getter: assumptions.pure_getters,
},
},
self.unresolved_mark
self.unresolved_mark,
),
should_enable(self.target, EsVersion::Es2020)
should_enable(self.target, EsVersion::Es2020),
),
Optional::new(
compat::es2019::es2019(),
should_enable(self.target, EsVersion::Es2019)
should_enable(self.target, EsVersion::Es2019),
),
Optional::new(
compat::es2018(compat::es2018::Config {
object_rest_spread: compat::es2018::object_rest_spread::Config {
no_symbol: assumptions.object_rest_no_symbols,
set_property: assumptions.set_spread_properties,
pure_getters: assumptions.pure_getters
}
pure_getters: assumptions.pure_getters,
},
}),
should_enable(self.target, EsVersion::Es2018)
should_enable(self.target, EsVersion::Es2018),
),
Optional::new(
compat::es2017(
compat::es2017::Config {
async_to_generator: compat::es2017::async_to_generator::Config {
ignore_function_name: assumptions.ignore_function_name,
ignore_function_length: assumptions.ignore_function_length
ignore_function_length: assumptions.ignore_function_length,
},
},
self.unresolved_mark
self.unresolved_mark,
),
should_enable(self.target, EsVersion::Es2017)
should_enable(self.target, EsVersion::Es2017),
),
Optional::new(
compat::es2016(),
should_enable(self.target, EsVersion::Es2016)
should_enable(self.target, EsVersion::Es2016),
),
Optional::new(
compat::es2015(
Expand All @@ -286,36 +282,36 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
no_class_calls: assumptions.no_class_calls,
set_class_methods: assumptions.set_class_methods,
super_is_callable_constructor: assumptions
.super_is_callable_constructor
.super_is_callable_constructor,
},
computed_props: compat::es2015::computed_props::Config {
loose: self.loose
loose: self.loose,
},
for_of: compat::es2015::for_of::Config {
assume_array: false,
loose: self.loose
loose: self.loose,
},
spread: compat::es2015::spread::Config { loose: self.loose },
destructuring: compat::es2015::destructuring::Config {
loose: self.loose
loose: self.loose,
},
regenerator: self.regenerator,
template_literal: compat::es2015::template_literal::Config {
ignore_to_primitive: assumptions.ignore_to_primitive_hint,
mutable_template: assumptions.mutable_template_object
mutable_template: assumptions.mutable_template_object,
},
parameters: compat::es2015::parameters::Config {
ignore_function_length: assumptions.ignore_function_length,
},
typescript: syntax.typescript()
}
typescript: syntax.typescript(),
},
),
should_enable(self.target, EsVersion::Es2015)
should_enable(self.target, EsVersion::Es2015),
),
Optional::new(
compat::es3(true),
cfg!(feature = "es3") && self.target == EsVersion::Es3
)
cfg!(feature = "es3") && self.target == EsVersion::Es3,
),
))
};

Expand All @@ -325,21 +321,21 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
.map(|v| v.mangle.is_obj() || v.mangle.is_true())
.unwrap_or(false);

chain!(
(
self.pass,
Optional::new(
paren_remover(comments.map(|v| v as &dyn Comments)),
self.fixer
self.fixer,
),
compat_pass,
// module / helper
Optional::new(
modules::import_analysis::import_analyzer(import_interop, ignore_dynamic),
need_analyzer
need_analyzer,
),
Optional::new(
helpers::inject_helpers(self.unresolved_mark),
self.inject_helpers
self.inject_helpers,
),
ModuleConfig::build(
self.cm.clone(),
Expand All @@ -349,7 +345,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
feature_flag,
resolver,
),
as_folder(MinifierPass {
visit_mut_pass(MinifierPass {
options: self.minify,
cm: self.cm.clone(),
comments,
Expand All @@ -360,7 +356,7 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
top_level_mark: self.top_level_mark,
..self.hygiene.clone().unwrap_or_default()
}),
self.hygiene.is_some() && !is_mangler_enabled
self.hygiene.is_some() && !is_mangler_enabled,
),
Optional::new(fixer(comments.map(|v| v as &dyn Comments)), self.fixer),
)
Expand Down
Loading

0 comments on commit 581aafb

Please sign in to comment.