Skip to content

Commit

Permalink
feat(webpack): Add ast reducer (#2875)
Browse files Browse the repository at this point in the history
swc_estree_compat:
 - Fix stack overflow related to object patterns.

swc_webpack_}ast:
 - Implement AST reducer.
  • Loading branch information
kdy1 authored Nov 26, 2021
1 parent a38889b commit c2bbdbe
Show file tree
Hide file tree
Showing 40 changed files with 1,700 additions and 7 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ jobs:
- swc_timer
- swc_visit
- swc_visit_macros
- swc_webpack_ast
- testing
- testing_macros
- wasm
Expand Down
25 changes: 24 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/swc_plugin_testing",
"crates/swc_stylis",
"crates/swc_timer",
"crates/swc_webpack_ast",
"crates/wasm",
]

Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_transforms_testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ fn test_fixture_inner<P>(
as_folder(::swc_ecma_utils::DropSpan {
preserve_ctxt: true,
}),
"output.js",
"expected.js",
syntax,
&expected,
)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_estree_compat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_estree_compat"
repository = "https://github.com/swc-project/swc.git"
version = "0.3.0"
version = "0.3.1"

[package.metadata.docs.rs]
all-features = true
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_estree_compat/src/babelify/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl Babelify for ModuleItem {
type Output = ModuleItemOutput;

fn parallel(cnt: usize) -> bool {
cnt >= 4
cnt >= 32
}

fn babelify(self, ctx: &Context) -> Self::Output {
Expand Down
11 changes: 9 additions & 2 deletions crates/swc_estree_compat/src/babelify/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ impl From<PatOutput> for ObjectPropVal {
fn from(pat: PatOutput) -> Self {
match pat {
PatOutput::Expr(e) => ObjectPropVal::Expr(e),
other => ObjectPropVal::Pattern(other.into()),
PatOutput::Id(p) => ObjectPropVal::Pattern(PatternLike::Id(p)),
PatOutput::Array(p) => ObjectPropVal::Pattern(PatternLike::ArrayPat(p)),
PatOutput::Rest(p) => ObjectPropVal::Pattern(PatternLike::RestEl(p)),
PatOutput::Object(p) => ObjectPropVal::Pattern(PatternLike::ObjectPat(p)),
PatOutput::Assign(p) => ObjectPropVal::Pattern(PatternLike::AssignmentPat(p)),
}
}
}
Expand Down Expand Up @@ -119,7 +123,10 @@ impl From<PatOutput> for Param {
match pat {
PatOutput::Id(i) => Param::Id(i),
PatOutput::Rest(r) => Param::Rest(r),
other => other.into(),
PatOutput::Array(p) => Param::Pat(Pattern::Array(p)),
PatOutput::Object(p) => Param::Pat(Pattern::Object(p)),
PatOutput::Assign(p) => Param::Pat(Pattern::Assignment(p)),
PatOutput::Expr(p) => panic!("Cannot convert {:?} to Param", p),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/swc_webpack_ast/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests/**/output.json
31 changes: 31 additions & 0 deletions crates/swc_webpack_ast/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "Webpack AST optimizer"
edition = "2018"
license = "Apache-2.0"
name = "swc_webpack_ast"
repository = "https://github.com/swc-project/swc.git"
version = "0.1.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.48"
rayon = "1.5.1"
serde_json = "1.0.72"
swc_atoms = {version = "0.2.9", path = "../swc_atoms"}
swc_common = {version = "0.14.6", path = "../swc_common"}
swc_ecma_ast = {version = "0.58.0", path = "../swc_ecma_ast"}
swc_ecma_transforms_base = {version = "0.44.3", path = "../swc_ecma_transforms_base"}
swc_ecma_utils = {version = "0.52.3", path = "../swc_ecma_utils"}
swc_ecma_visit = {version = "0.44.0", path = "../swc_ecma_visit"}
swc_estree_ast = {version = "0.2.0", path = "../swc_estree_ast"}
swc_estree_compat = {version = "0.3.0", path = "../swc_estree_compat"}
swc_timer = {version = "0.1.0", path = "../swc_timer"}
tracing = "0.1.29"

[dev-dependencies]
swc_ecma_parser = {version = "0.78.9", path = "../swc_ecma_parser"}
swc_ecma_transforms_testing = {version = "0.45.1", path = "../swc_ecma_transforms_testing"}
swc_node_base = {version = "0.5.1", path = "../swc_node_base"}
testing = {version = "0.15.2", path = "../testing"}
48 changes: 48 additions & 0 deletions crates/swc_webpack_ast/benches/webpack_ast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#![feature(test)]

use std::path::Path;
use swc_ecma_parser::{EsConfig, Parser, StringInput, Syntax};
use test::Bencher;

extern crate swc_node_base;
extern crate test;

/// this benchmark requires real input, which cannot be committed into git
/// repository
#[bench]
#[cfg(not(all))]
fn total(b: &mut Bencher) {
let input = Path::new("tests/fixture/real/input.js");

b.iter(|| {
testing::run_test(false, |cm, handler| {
let fm = cm.load_file(&input).unwrap();

let module = {
let mut p = Parser::new(
Syntax::Es(EsConfig {
jsx: true,
..Default::default()
}),
StringInput::from(&*fm),
None,
);
let res = p
.parse_module()
.map_err(|e| e.into_diagnostic(&handler).emit());

for e in p.take_errors() {
e.into_diagnostic(&handler).emit()
}

res?
};

let s = swc_webpack_ast::webpack_ast(cm.clone(), fm.clone(), module).unwrap();
println!("{} bytes", s.len());

Ok(())
})
.unwrap();
});
}
66 changes: 66 additions & 0 deletions crates/swc_webpack_ast/scripts/bench.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@



const Benchmark = require('benchmark');
const acorn = require("acorn");
const jsx = require("acorn-jsx");
const parser = acorn.Parser.extend(jsx());


const fs = require('fs');
const path = require('path');


const src = fs.readFileSync(path.join(process.argv[2], "input.js"), 'utf8');
const jsonStr = fs.readFileSync(path.join(process.argv[2], "output.json"), 'utf8');


{
parser.parse(src, {
ecmaVersion: 2020,
ranges: true,
allowHashBang: true,
sourceType: 'module'
})
}
const suite = new Benchmark.Suite;

suite
.add('acorn', () => {
parser.parse(src, {
ecmaVersion: 2020,
ranges: true,
allowHashBang: true,
sourceType: 'module'
})
})
.add({
name: 'acorn-real',
fn: (deferred) => {
fs.promises.readFile(path.join(process.argv[2], "input.js"), 'utf8')
.then((src) => {
parser.parse(src, {
ecmaVersion: 2020,
ranges: true,
allowHashBang: true,
sourceType: 'module'
});
deferred.resolve()
})
},
defer: true,
async: true,
queued: true
})
.add('json', () => {
JSON.parse(jsonStr)
})
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({
async: true
})
45 changes: 45 additions & 0 deletions crates/swc_webpack_ast/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::reducer::ast_reducer;
use anyhow::{Context, Error};
use swc_common::{sync::Lrc, Mark, SourceFile, SourceMap};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::resolver::resolver_with_mark;
use swc_ecma_visit::VisitMutWith;
use swc_estree_ast::flavor::Flavor;
use swc_estree_compat::babelify::Babelify;
use swc_timer::timer;

pub mod reducer;

/// `n` is expected to be pure (`resolver` is not applied)
pub fn webpack_ast(
cm: Lrc<SourceMap>,
fm: Lrc<SourceFile>,
mut n: Module,
) -> Result<String, Error> {
let _timer = timer!("webpack_ast");
let top_level_mark = Mark::fresh(Mark::root());

Flavor::Acorn.with(|| {
{
let _timer = timer!("resolver");
n.visit_mut_with(&mut resolver_with_mark(top_level_mark));
}
{
n.visit_mut_with(&mut ast_reducer(top_level_mark));
}

let ctx = swc_estree_compat::babelify::Context {
fm,
cm: cm.clone(),
comments: Default::default(),
};

let babel_ast = {
let _timer = timer!("babelify");
n.babelify(&ctx)
};

let _timer = timer!("acorn ast to json");
serde_json::to_string(&babel_ast).context("failed to serialize babel ast")
})
}
Loading

1 comment on commit c2bbdbe

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: c2bbdbe Previous: a38889b Ratio
base_tr_fixer 25017 ns/iter (± 2825) 27240 ns/iter (± 4950) 0.92
base_tr_resolver_and_hygiene 148414 ns/iter (± 14449) 155933 ns/iter (± 40955) 0.95
codegen_es2015 60123 ns/iter (± 6418) 61924 ns/iter (± 7361) 0.97
codegen_es2016 56250 ns/iter (± 7717) 61927 ns/iter (± 5074) 0.91
codegen_es2017 55142 ns/iter (± 8090) 61013 ns/iter (± 4223) 0.90
codegen_es2018 54912 ns/iter (± 7612) 61387 ns/iter (± 4924) 0.89
codegen_es2019 55100 ns/iter (± 6677) 61281 ns/iter (± 3469) 0.90
codegen_es2020 54384 ns/iter (± 10189) 61443 ns/iter (± 12061) 0.89
codegen_es3 55464 ns/iter (± 5072) 61400 ns/iter (± 6449) 0.90
codegen_es5 56854 ns/iter (± 5251) 61298 ns/iter (± 5102) 0.93
full_es2015 201841154 ns/iter (± 12826062) 210142626 ns/iter (± 16395287) 0.96
full_es2016 160630239 ns/iter (± 8299375) 165416530 ns/iter (± 10030819) 0.97
full_es2017 166560428 ns/iter (± 16312689) 174792712 ns/iter (± 9872438) 0.95
full_es2018 166776040 ns/iter (± 11224399) 170984156 ns/iter (± 7916380) 0.98
full_es2019 164853738 ns/iter (± 9872156) 171195779 ns/iter (± 10731253) 0.96
full_es2020 163152175 ns/iter (± 14287351) 173084630 ns/iter (± 16406100) 0.94
full_es3 230771173 ns/iter (± 24018589) 256146386 ns/iter (± 18257273) 0.90
full_es5 217913868 ns/iter (± 13760611) 233437845 ns/iter (± 15901915) 0.93
parser 732598 ns/iter (± 47435) 758753 ns/iter (± 21550) 0.97
ser_ast_node 171 ns/iter (± 16) 180 ns/iter (± 16) 0.95
ser_serde 187 ns/iter (± 10) 191 ns/iter (± 31) 0.98
emit_colors 20000301 ns/iter (± 26072498) 17563873 ns/iter (± 24576480) 1.14
emit_large 124772989 ns/iter (± 184976760) 110536556 ns/iter (± 161940710) 1.13
base_clone 2546783 ns/iter (± 296290) 2573721 ns/iter (± 32121) 0.99
fold_span 4326806 ns/iter (± 393924) 4363305 ns/iter (± 64273) 0.99
fold_span_panic 4679771 ns/iter (± 473586) 4619560 ns/iter (± 102978) 1.01
visit_mut_span 3200188 ns/iter (± 261971) 3151207 ns/iter (± 111316) 1.02
visit_mut_span_panic 3429845 ns/iter (± 335126) 3239655 ns/iter (± 72135) 1.06
ast_clone 19109 ns/iter (± 1795) 19723 ns/iter (± 474) 0.97
ast_clone_to_stable 56065 ns/iter (± 4965) 59610 ns/iter (± 498) 0.94
ast_clone_to_stable_then_to_unstable 102928 ns/iter (± 10248) 109445 ns/iter (± 925) 0.94
json_deserialize 2242020 ns/iter (± 275074) 2320826 ns/iter (± 4957) 0.97
json_serialize 95277 ns/iter (± 6172) 101214 ns/iter (± 474) 0.94
boxing_boxed 162 ns/iter (± 15) 172 ns/iter (± 0) 0.94
boxing_boxed_clone 76 ns/iter (± 9) 82 ns/iter (± 0) 0.93
boxing_unboxed 148 ns/iter (± 17) 168 ns/iter (± 0) 0.88
boxing_unboxed_clone 74 ns/iter (± 4) 75 ns/iter (± 0) 0.99
time_10 355 ns/iter (± 31) 395 ns/iter (± 2) 0.90
time_15 808 ns/iter (± 79) 791 ns/iter (± 5) 1.02
time_20 1474 ns/iter (± 149) 1354 ns/iter (± 9) 1.09
time_40 4546 ns/iter (± 870) 4995 ns/iter (± 11) 0.91
time_5 112 ns/iter (± 14) 120 ns/iter (± 0) 0.93
time_60 10148 ns/iter (± 1173) 10583 ns/iter (± 57) 0.96

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.