Skip to content

Commit

Permalink
refactor(binding/wasm): Use binding_macro (#6487)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwonoj authored Nov 21, 2022
1 parent 0e67eaf commit d957aac
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 241 deletions.
247 changes: 12 additions & 235 deletions bindings/binding_core_wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,10 @@
use anyhow::Error;
use serde::Serialize;
use serde_wasm_bindgen::Serializer;
use swc_core::{
base::HandlerOpts,
binding_macros::wasm::{
compiler, convert_err, future_to_promise,
js_sys::{JsString, Promise},
noop, Options, ParseOptions, SourceMapsConfig,
},
common::{
comments::{self, SingleThreadedComments},
errors::Handler,
sync::Lrc,
FileName, Mark, SourceMap, GLOBALS,
},
ecma::{
ast::{EsVersion, Program},
transforms::base::resolver,
visit::VisitMutWith,
},
use swc_core::binding_macros::{
build_minify, build_minify_sync, build_parse, build_parse_sync, build_print, build_print_sync,
build_transform, build_transform_sync,
};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen::prelude::*;
mod types;

// A serializer with options to provide backward compat for the input / output
// from the bindgen generated swc interfaces.
const COMPAT_SERIALIZER: Serializer = Serializer::new()
.serialize_maps_as_objects(true)
.serialize_missing_as_null(true);

/// Custom interface definitions for the @swc/wasm's public interface instead of
/// auto generated one, which is not reflecting most of types in detail.
#[wasm_bindgen(typescript_custom_section)]
Expand Down Expand Up @@ -68,210 +44,11 @@ export function transform(
export function transformSync(code: string | Program, opts?: Options, experimental_plugin_bytes_resolver?: any): Output;
"#;

fn try_with_handler<F, Ret>(cm: Lrc<SourceMap>, config: HandlerOpts, op: F) -> Result<Ret, Error>
where
F: FnOnce(&Handler) -> Result<Ret, Error>,
{
GLOBALS.set(&Default::default(), || {
swc_core::base::try_with_handler(cm, config, op)
})
}

#[wasm_bindgen(
js_name = "minifySync",
typescript_type = "minifySync",
skip_typescript
)]
pub fn minify_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |handler| {
c.run(|| {
let opts = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
serde_wasm_bindgen::from_value(opts)
.map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))?
};
let fm = c.cm.new_source_file(FileName::Anon, s.into());
let program =
anyhow::Context::context(c.minify(fm, handler, &opts), "failed to minify file")?;

program
.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e))
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "minify", typescript_type = "minify", skip_typescript)]
pub fn minify(s: JsString, opts: JsValue) -> Promise {
future_to_promise(async { minify_sync(s, opts) })
}

#[wasm_bindgen(js_name = "parseSync", typescript_type = "parseSync", skip_typescript)]
pub fn parse_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |handler| {
c.run(|| {
GLOBALS.set(&Default::default(), || {
let opts: ParseOptions = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
serde_wasm_bindgen::from_value(opts)
.map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))?
};
let fm = c.cm.new_source_file(FileName::Anon, s.into());
let cmts = c.comments().clone();
let comments = if opts.comments {
Some(&cmts as &dyn comments::Comments)
} else {
None
};
let mut program = anyhow::Context::context(
c.parse_js(
fm,
handler,
opts.target,
opts.syntax,
opts.is_module,
comments,
),
"failed to parse code",
)?;

program.visit_mut_with(&mut resolver(
Mark::new(),
Mark::new(),
opts.syntax.typescript(),
));

program
.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e))
})
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "parse", typescript_type = "parse", skip_typescript)]
pub fn parse(s: JsString, opts: JsValue) -> Promise {
future_to_promise(async { parse_sync(s, opts) })
}

#[wasm_bindgen(
js_name = "transformSync",
typescript_type = "transformSync",
skip_typescript
)]
#[allow(unused_variables)]
pub fn transform_sync(
s: JsValue,
opts: JsValue,
experimental_plugin_bytes_resolver: JsValue,
) -> Result<JsValue, JsValue> {
let c = compiler();
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
serde_wasm_bindgen::from_value(opts)?
};

let error_format = opts.experimental.error_format.unwrap_or_default();
try_with_handler(c.cm.clone(), Default::default(), |handler| {
c.run(|| {
let s = JsCast::dyn_into::<JsString>(s);
let out = match s {
Ok(s) => {
let fm = c.cm.new_source_file(
if opts.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(opts.filename.clone().into())
},
s.into(),
);
let cm = c.cm.clone();
let file = fm.clone();
let comments = SingleThreadedComments::default();
anyhow::Context::context(
c.process_js_with_custom_pass(
fm,
None,
handler,
&opts,
comments,
|_| noop(),
|_| noop(),
),
"failed to process js file",
)?
}
Err(v) => {
c.process_js(handler, serde_wasm_bindgen::from_value(v).expect("Should able to deserialize into program"), &opts)?
}
};

out.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize transform result: {}", e))
})
})
.map_err(|e| convert_err(e, Some(error_format)))
}

#[wasm_bindgen(js_name = "transform", typescript_type = "transform", skip_typescript)]
pub fn transform(
s: JsValue,
opts: JsValue,
experimental_plugin_bytes_resolver: JsValue,
) -> Promise {
future_to_promise(async { transform_sync(s, opts, experimental_plugin_bytes_resolver) })
}

#[wasm_bindgen(js_name = "printSync", typescript_type = "printSync", skip_typescript)]
pub fn print_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |_handler| {
c.run(|| {
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
serde_wasm_bindgen::from_value(opts)
.map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))?
};

let program: Program = serde_wasm_bindgen::from_value(s)
.map_err(|e| anyhow::anyhow!("failed to deserialize program: {}", e))?;

let s = anyhow::Context::context(
c.print(
&program,
None,
None,
true,
opts.codegen_target().unwrap_or(EsVersion::Es2020),
opts.source_maps
.clone()
.unwrap_or(SourceMapsConfig::Bool(false)),
&Default::default(),
None,
opts.config.minify.into(),
None,
opts.config.emit_source_map_columns.into_bool(),
false,
),
"failed to print code",
)?;

serde_wasm_bindgen::to_value(&s)
.map_err(|e| anyhow::anyhow!("failed to serialize json: {}", e))
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "print", typescript_type = "print", skip_typescript)]
pub fn print(s: JsValue, opts: JsValue) -> Promise {
future_to_promise(async { print_sync(s, opts) })
}
build_minify_sync!(#[wasm_bindgen(js_name = "minifySync", typescript_type = "minifySync",skip_typescript)]);
build_minify!(#[wasm_bindgen(js_name = "minify", typescript_type = "minify",skip_typescript)]);
build_parse_sync!(#[wasm_bindgen(js_name = "parseSync", typescript_type = "parseSync",skip_typescript)]);
build_parse!(#[wasm_bindgen(js_name = "parse", typescript_type = "parse",skip_typescript)]);
build_print_sync!(#[wasm_bindgen(js_name = "printSync", typescript_type = "printSync",skip_typescript)]);
build_print!(#[wasm_bindgen(js_name = "print", typescript_type = "print",skip_typescript)]);
build_transform_sync!(#[wasm_bindgen(js_name = "transformSync", typescript_type = "transformSync",skip_typescript)]);
build_transform!(#[wasm_bindgen(js_name = "transform", typescript_type = "transform",skip_typescript)]);
17 changes: 11 additions & 6 deletions bindings/binding_core_wasm/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,11 @@ export interface TerserCompressOptions {
export interface TerserMangleOptions {
props?: TerserManglePropertiesOptions,
top_level?: boolean,
toplevel?: boolean,
keep_class_names?: boolean,
keep_classnames?: boolean,
keep_fn_names?: boolean,
keep_fnames?: boolean,
keep_private_props?: boolean,
Expand Down Expand Up @@ -678,7 +678,7 @@ export interface JscConfig {
baseUrl?: string
paths?: {
[from: string]: [string]
[from: string]: string[]
}
minify?: JsMinifyOptions;
Expand Down Expand Up @@ -907,7 +907,7 @@ export interface GlobalPassOption {
envs?: string[];
}
export type ModuleConfig = Es6Config | CommonJsConfig | UmdConfig | AmdConfig | NodeNextConfig;
export type ModuleConfig = Es6Config | CommonJsConfig | UmdConfig | AmdConfig | NodeNextConfig | SystemjsConfig;
export interface BaseModuleConfig {
/**
Expand Down Expand Up @@ -1068,6 +1068,8 @@ export interface BaseModuleConfig {
* If set to true, dynamic imports will be preserved.
*/
ignoreDynamic?: boolean;
allowTopLevelThis?: boolean;
preserveImportMeta?: boolean;
}
export interface Es6Config extends BaseModuleConfig {
Expand All @@ -1091,7 +1093,10 @@ export interface AmdConfig extends BaseModuleConfig {
type: "amd";
moduleId?: string;
}
export interface SystemjsConfig {
type: "systemjs";
allowTopLevelThis?: boolean;
}
export interface Output {
/**
* Transformed code
Expand Down

1 comment on commit d957aac

@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: d957aac Previous: 7fe091a Ratio
es/full/bugs-1 355644 ns/iter (± 34972) 344071 ns/iter (± 18909) 1.03
es/full/minify/libraries/antd 1924375843 ns/iter (± 24700286) 1880752872 ns/iter (± 27724911) 1.02
es/full/minify/libraries/d3 418516624 ns/iter (± 29468176) 408872327 ns/iter (± 10337906) 1.02
es/full/minify/libraries/echarts 1661882174 ns/iter (± 72001162) 1592486577 ns/iter (± 20242840) 1.04
es/full/minify/libraries/jquery 121308547 ns/iter (± 8114923) 104174694 ns/iter (± 4100286) 1.16
es/full/minify/libraries/lodash 144805257 ns/iter (± 19088462) 125012183 ns/iter (± 5091035) 1.16
es/full/minify/libraries/moment 70734769 ns/iter (± 3944496) 61905334 ns/iter (± 1629693) 1.14
es/full/minify/libraries/react 25782656 ns/iter (± 880415) 20509429 ns/iter (± 489340) 1.26
es/full/minify/libraries/terser 389602971 ns/iter (± 32330111) 326336949 ns/iter (± 28413498) 1.19
es/full/minify/libraries/three 652272739 ns/iter (± 14458354) 588771349 ns/iter (± 27419191) 1.11
es/full/minify/libraries/typescript 3754961918 ns/iter (± 140474437) 3435441737 ns/iter (± 58333195) 1.09
es/full/minify/libraries/victory 894463918 ns/iter (± 20935420) 878820429 ns/iter (± 16515001) 1.02
es/full/minify/libraries/vue 176250640 ns/iter (± 11113458) 162345753 ns/iter (± 5096746) 1.09
es/full/codegen/es3 34452 ns/iter (± 1948) 47104 ns/iter (± 1433) 0.73
es/full/codegen/es5 34702 ns/iter (± 3535) 47553 ns/iter (± 1701) 0.73
es/full/codegen/es2015 33413 ns/iter (± 939) 47288 ns/iter (± 1977) 0.71
es/full/codegen/es2016 33736 ns/iter (± 3185) 47285 ns/iter (± 1383) 0.71
es/full/codegen/es2017 35426 ns/iter (± 4759) 47005 ns/iter (± 1926) 0.75
es/full/codegen/es2018 39407 ns/iter (± 46759) 47221 ns/iter (± 1908) 0.83
es/full/codegen/es2019 34982 ns/iter (± 3113) 46986 ns/iter (± 3751) 0.74
es/full/codegen/es2020 33830 ns/iter (± 2334) 47106 ns/iter (± 1651) 0.72
es/full/all/es3 227251443 ns/iter (± 28622846) 197072875 ns/iter (± 11055693) 1.15
es/full/all/es5 207132358 ns/iter (± 20417800) 183315489 ns/iter (± 8177463) 1.13
es/full/all/es2015 164477962 ns/iter (± 15605318) 145851549 ns/iter (± 3355968) 1.13
es/full/all/es2016 154922776 ns/iter (± 10742297) 146334431 ns/iter (± 5872326) 1.06
es/full/all/es2017 153141706 ns/iter (± 13307824) 146651074 ns/iter (± 6743015) 1.04
es/full/all/es2018 152004383 ns/iter (± 11550747) 144704247 ns/iter (± 6521531) 1.05
es/full/all/es2019 150753334 ns/iter (± 12385616) 143124257 ns/iter (± 3735106) 1.05
es/full/all/es2020 146794393 ns/iter (± 9968285) 137327945 ns/iter (± 4613344) 1.07
es/full/parser 746922 ns/iter (± 37924) 718033 ns/iter (± 42779) 1.04
es/full/base/fixer 26820 ns/iter (± 1093) 26196 ns/iter (± 1087) 1.02
es/full/base/resolver_and_hygiene 94866 ns/iter (± 5480) 90766 ns/iter (± 4614) 1.05
serialization of ast node 209 ns/iter (± 21) 206 ns/iter (± 19) 1.01
serialization of serde 214 ns/iter (± 6) 233 ns/iter (± 10) 0.92

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

Please sign in to comment.