Skip to content

Commit

Permalink
feat(plugin): Wrap serialized struct with a version (#5128)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwonoj authored Jul 7, 2022
1 parent 09d4aa4 commit 82fbe15
Show file tree
Hide file tree
Showing 19 changed files with 200 additions and 129 deletions.
44 changes: 34 additions & 10 deletions crates/swc/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ impl RustPlugins {
use std::{path::PathBuf, sync::Arc};

use anyhow::Context;
use swc_common::{plugin::PluginSerializedBytes, FileName};
use swc_common::{
plugin::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
use swc_ecma_loader::resolve::Resolve;

// swc_plugin_macro will not inject proxy to the comments if comments is empty
Expand All @@ -92,7 +95,8 @@ impl RustPlugins {
inner: self.comments.clone(),
},
|| {
let mut serialized = PluginSerializedBytes::try_serialize(&n)?;
let program = VersionedSerializable::new(n);
let mut serialized = PluginSerializedBytes::try_serialize(&program)?;

// Run plugin transformation against current program.
// We do not serialize / deserialize between each plugin execution but
Expand All @@ -111,11 +115,19 @@ impl RustPlugins {

let config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| PluginSerializedBytes::try_serialize(&value))?;
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

let context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| PluginSerializedBytes::try_serialize(&value))?;
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

let resolved_path = self
.resolver
Expand Down Expand Up @@ -152,7 +164,7 @@ impl RustPlugins {

// Plugin transformation is done. Deserialize transformed bytes back
// into Program
serialized.deserialize()
serialized.deserialize().map(|v| v.into_inner())
},
)
}
Expand All @@ -162,7 +174,10 @@ impl RustPlugins {
use std::{path::PathBuf, sync::Arc};

use anyhow::Context;
use swc_common::{plugin::PluginSerializedBytes, FileName};
use swc_common::{
plugin::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
use swc_ecma_loader::resolve::Resolve;

let should_enable_comments_proxy = self.comments.is_some();
Expand All @@ -172,17 +187,26 @@ impl RustPlugins {
inner: self.comments.clone(),
},
|| {
let mut serialized = PluginSerializedBytes::try_serialize(&n)?;
let program = VersionedSerializable::new(n);
let mut serialized = PluginSerializedBytes::try_serialize(&program)?;

if let Some(plugins) = &self.plugins {
for p in plugins {
let config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| PluginSerializedBytes::try_serialize(&value))?;
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

let context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| PluginSerializedBytes::try_serialize(&value))?;
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

serialized = swc_plugin_runner::apply_transform_plugin(
&p.0,
Expand All @@ -197,7 +221,7 @@ impl RustPlugins {
}
}

serialized.deserialize()
serialized.deserialize().map(|v| v.into_inner())
},
)
}
Expand Down
19 changes: 8 additions & 11 deletions crates/swc_common/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ impl PluginSerializedBytes {
}

/**
* Constructs an instance from given struct by serializing it.
* Constructs an instance from versioned struct by serializing it.
*
* This is sort of mimic TryFrom behavior, since we can't use generic
* to implement TryFrom trait
*/
pub fn try_serialize<W>(t: &W) -> Result<Self, Error>
pub fn try_serialize<W>(t: &VersionedSerializable<W>) -> Result<Self, Error>
where
W: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
{
Expand Down Expand Up @@ -105,15 +105,15 @@ impl PluginSerializedBytes {
(self.field.as_ptr(), self.field.len())
}

pub fn deserialize<W>(&self) -> Result<W, Error>
pub fn deserialize<W>(&self) -> Result<VersionedSerializable<W>, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
use anyhow::Context;
use rkyv::Deserialize;

let archived = unsafe { rkyv::archived_root::<W>(&self.field[..]) };
let archived = unsafe { rkyv::archived_root::<VersionedSerializable<W>>(&self.field[..]) };

archived
.deserialize(&mut rkyv::de::deserializers::SharedDeserializeMap::new())
Expand All @@ -130,7 +130,7 @@ impl PluginSerializedBytes {
pub unsafe fn deserialize_from_ptr<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
) -> Result<VersionedSerializable<W>, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
Expand All @@ -153,7 +153,7 @@ where
pub unsafe fn deserialize_from_ptr_into_fallible<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
) -> Result<VersionedSerializable<W>, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
Expand Down Expand Up @@ -194,10 +194,7 @@ impl<T> VersionedSerializable<T> {
&self.0 .1
}

pub fn take(&mut self) -> T
where
T: Default,
{
mem::take(&mut self.0 .1)
pub fn into_inner(self) -> T {
self.0 .1
}
}
19 changes: 12 additions & 7 deletions crates/swc_common/src/syntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ impl Mark {
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
// This code path executed inside of the guest memory context.
// In here, preallocate memory for the context.
let serialized =
crate::plugin::PluginSerializedBytes::try_serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let serialized = crate::plugin::PluginSerializedBytes::try_serialize(
&crate::plugin::VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();

// Calling host proxy fn. Inside of host proxy, host will
Expand All @@ -197,6 +198,7 @@ impl Mark {
len.try_into().expect("Should able to convert ptr length"),
)
.expect("Should able to deserialize")
.into_inner()
};
self = Mark::from_u32(context.0);

Expand All @@ -219,9 +221,10 @@ impl Mark {
#[allow(unused_mut, unused_assignments)]
#[cfg(all(feature = "plugin-mode", target_arch = "wasm32"))]
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
let serialized =
crate::plugin::PluginSerializedBytes::try_serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let serialized = crate::plugin::PluginSerializedBytes::try_serialize(
&crate::plugin::VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();

unsafe {
Expand All @@ -234,6 +237,7 @@ impl Mark {
len.try_into().expect("Should able to convert ptr length"),
)
.expect("Should able to deserialize")
.into_inner()
};
a = Mark::from_u32(context.0);
b = Mark::from_u32(context.1);
Expand Down Expand Up @@ -383,7 +387,7 @@ impl SyntaxContext {

#[cfg(all(feature = "plugin-mode", target_arch = "wasm32"))]
pub fn remove_mark(&mut self) -> Mark {
let context = MutableMarkContext(0, 0, 0);
let context = crate::plugin::VersionedSerializable::new(MutableMarkContext(0, 0, 0));
let serialized = crate::plugin::PluginSerializedBytes::try_serialize(&context)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
Expand All @@ -398,6 +402,7 @@ impl SyntaxContext {
len.try_into().expect("Should able to convert ptr length"),
)
.expect("Should able to deserialize")
.into_inner()
};

*self = SyntaxContext(context.0);
Expand Down
6 changes: 0 additions & 6 deletions crates/swc_ecma_ast/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ pub enum Program {
Script(Script),
}

impl Default for Program {
fn default() -> Self {
Program::Module(Module::dummy())
}
}

#[ast_node("Module")]
#[derive(Eq, Hash, EqIgnoreSpan)]
pub struct Module {
Expand Down
8 changes: 6 additions & 2 deletions crates/swc_plugin/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use swc_common::{plugin::PluginSerializedBytes, sync::OnceCell};
use swc_common::{
plugin::{PluginSerializedBytes, VersionedSerializable},
sync::OnceCell,
};

use crate::pseudo_scoped_key::PseudoScopedKey;

Expand All @@ -18,7 +21,8 @@ pub struct PluginDiagnosticsEmitter;
impl swc_common::errors::Emitter for PluginDiagnosticsEmitter {
#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
fn emit(&mut self, db: &swc_common::errors::DiagnosticBuilder<'_>) {
let diag = PluginSerializedBytes::try_serialize(&*db.diagnostic)
let diagnostic = VersionedSerializable::new((*db.diagnostic).clone());
let diag = PluginSerializedBytes::try_serialize(&diagnostic)
.expect("Should able to serialize Diagnostic");
let (ptr, len) = diag.as_ptr();

Expand Down
2 changes: 1 addition & 1 deletion crates/swc_plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Reexports
pub use swc_common::{
chain,
plugin::{deserialize_from_ptr, PluginError, PluginSerializedBytes},
plugin::{deserialize_from_ptr, PluginError, PluginSerializedBytes, VersionedSerializable},
};

pub mod comments {
Expand Down
9 changes: 5 additions & 4 deletions crates/swc_plugin_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fn handle_func(func: ItemFn) -> TokenStream {

/// Internal function plugin_macro uses to create ptr to PluginError.
fn construct_error_ptr(plugin_error: swc_plugin::PluginError) -> i32 {
let plugin_error = swc_plugin::VersionedSerializable::new(plugin_error);
let ret = swc_plugin::PluginSerializedBytes::try_serialize(&plugin_error).expect("Should able to serialize PluginError");
let (ptr, len) = ret.as_ptr();

Expand All @@ -61,14 +62,14 @@ fn handle_func(func: ItemFn) -> TokenStream {
pub fn #process_impl_ident(ast_ptr: *const u8, ast_ptr_len: i32, config_str_ptr: *const u8, config_str_ptr_len: i32, context_str_ptr: *const u8, context_str_ptr_len: i32, should_enable_comments_proxy: i32) -> i32 {
// Reconstruct `Program` & config string from serialized program
// Host (SWC) should allocate memory, copy bytes and pass ptr to plugin.
let program = unsafe { swc_plugin::deserialize_from_ptr(ast_ptr, ast_ptr_len) };
let program = unsafe { swc_plugin::deserialize_from_ptr(ast_ptr, ast_ptr_len).map(|v| v.into_inner()) };
if program.is_err() {
let err = swc_plugin::PluginError::Deserialize("Failed to deserialize program received from host".to_string());
return construct_error_ptr(err);
}
let program: Program = program.expect("Should be a program");

let config = unsafe { swc_plugin::deserialize_from_ptr(config_str_ptr, config_str_ptr_len) };
let config = unsafe { swc_plugin::deserialize_from_ptr(config_str_ptr, config_str_ptr_len).map(|v| v.into_inner()) };
if config.is_err() {
let err = swc_plugin::PluginError::Deserialize(
"Failed to deserialize config string received from host".to_string()
Expand All @@ -77,7 +78,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
}
let config: String = config.expect("Should be a string");

let context = unsafe { swc_plugin::deserialize_from_ptr(context_str_ptr, context_str_ptr_len) };
let context = unsafe { swc_plugin::deserialize_from_ptr(context_str_ptr, context_str_ptr_len).map(|v| v.into_inner()) };
if context.is_err() {
let err = swc_plugin::PluginError::Deserialize("Failed to deserialize context string received from host".to_string());
return construct_error_ptr(err);
Expand Down Expand Up @@ -112,7 +113,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
let transformed_program = #ident(program, metadata);

// Serialize transformed result, return back to the host.
let serialized_result = swc_plugin::PluginSerializedBytes::try_serialize(&transformed_program);
let serialized_result = swc_plugin::PluginSerializedBytes::try_serialize(&swc_plugin::VersionedSerializable::new(transformed_program));

if serialized_result.is_err() {
let err = swc_plugin::PluginError::Serialize("Failed to serialize transformed program".to_string());
Expand Down
13 changes: 7 additions & 6 deletions crates/swc_plugin_proxy/src/comments/plugin_comments_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ impl PluginCommentsProxy {
/// comment_buffer as serialized to pass param from guest to the host for
/// the fn like add_leading*.
#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
fn allocate_comments_buffer_to_host<T>(&self, value: &T)
fn allocate_comments_buffer_to_host<T>(&self, value: T)
where
T: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
{
#[cfg(target_arch = "wasm32")]
{
let serialized = swc_common::plugin::PluginSerializedBytes::try_serialize(value)
let value = swc_common::plugin::VersionedSerializable::new(value);
let serialized = swc_common::plugin::PluginSerializedBytes::try_serialize(&value)
.expect("Should able to serialize value");
let (serialized_comment_ptr, serialized_comment_ptr_len) = serialized.as_ptr();
unsafe {
Expand All @@ -73,15 +74,15 @@ impl PluginCommentsProxy {
#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
impl Comments for PluginCommentsProxy {
fn add_leading(&self, pos: BytePos, cmt: Comment) {
self.allocate_comments_buffer_to_host(&cmt);
self.allocate_comments_buffer_to_host(cmt);
#[cfg(target_arch = "wasm32")]
unsafe {
__add_leading_comment_proxy(pos.0);
}
}

fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
self.allocate_comments_buffer_to_host(&comments);
self.allocate_comments_buffer_to_host(comments);
#[cfg(target_arch = "wasm32")]
unsafe {
__add_leading_comments_proxy(pos.0);
Expand Down Expand Up @@ -129,15 +130,15 @@ impl Comments for PluginCommentsProxy {
}

fn add_trailing(&self, pos: BytePos, cmt: Comment) {
self.allocate_comments_buffer_to_host(&cmt);
self.allocate_comments_buffer_to_host(cmt);
#[cfg(target_arch = "wasm32")]
unsafe {
__add_trailing_comment_proxy(pos.0);
}
}

fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
self.allocate_comments_buffer_to_host(&comments);
self.allocate_comments_buffer_to_host(comments);
#[cfg(target_arch = "wasm32")]
unsafe {
__add_trailing_comments_proxy(pos.0);
Expand Down
Loading

1 comment on commit 82fbe15

@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: 82fbe15 Previous: 9d4be11 Ratio
es/full/minify/libraries/antd 1737159978 ns/iter (± 69327068) 1604690027 ns/iter (± 21877353) 1.08
es/full/minify/libraries/d3 419834462 ns/iter (± 8273207) 419550188 ns/iter (± 9686912) 1.00
es/full/minify/libraries/echarts 1696483437 ns/iter (± 174884323) 1606309174 ns/iter (± 20209985) 1.06
es/full/minify/libraries/jquery 92602832 ns/iter (± 2958594) 98666903 ns/iter (± 7566405) 0.94
es/full/minify/libraries/lodash 122684567 ns/iter (± 9149360) 122443327 ns/iter (± 7234706) 1.00
es/full/minify/libraries/moment 54183640 ns/iter (± 8538205) 54804421 ns/iter (± 1834405) 0.99
es/full/minify/libraries/react 17984425 ns/iter (± 2213013) 18328040 ns/iter (± 447607) 0.98
es/full/minify/libraries/terser 605034387 ns/iter (± 27987400) 611931141 ns/iter (± 10962813) 0.99
es/full/minify/libraries/three 557528510 ns/iter (± 74777222) 526627256 ns/iter (± 13586154) 1.06
es/full/minify/libraries/typescript 3602783791 ns/iter (± 174014157) 3354849325 ns/iter (± 44619772) 1.07
es/full/minify/libraries/victory 793929168 ns/iter (± 121445451) 702863191 ns/iter (± 10438857) 1.13
es/full/minify/libraries/vue 153692490 ns/iter (± 19867177) 131950688 ns/iter (± 5848287) 1.16
es/full/codegen/es3 32827 ns/iter (± 806) 31695 ns/iter (± 580) 1.04
es/full/codegen/es5 32624 ns/iter (± 1208) 30961 ns/iter (± 651) 1.05
es/full/codegen/es2015 32661 ns/iter (± 1662) 31956 ns/iter (± 752) 1.02
es/full/codegen/es2016 32521 ns/iter (± 527) 31816 ns/iter (± 808) 1.02
es/full/codegen/es2017 32515 ns/iter (± 358) 31658 ns/iter (± 2147) 1.03
es/full/codegen/es2018 32420 ns/iter (± 955) 31644 ns/iter (± 2244) 1.02
es/full/codegen/es2019 32417 ns/iter (± 567) 31613 ns/iter (± 533) 1.03
es/full/codegen/es2020 32411 ns/iter (± 873) 31808 ns/iter (± 583) 1.02
es/full/all/es3 188959507 ns/iter (± 10805735) 182365825 ns/iter (± 3334606) 1.04
es/full/all/es5 176082204 ns/iter (± 10193715) 169825599 ns/iter (± 3154395) 1.04
es/full/all/es2015 139433201 ns/iter (± 8035580) 138537195 ns/iter (± 10054107) 1.01
es/full/all/es2016 139174267 ns/iter (± 8712111) 137852501 ns/iter (± 2711493) 1.01
es/full/all/es2017 138140943 ns/iter (± 4167970) 137679133 ns/iter (± 2803056) 1.00
es/full/all/es2018 136891244 ns/iter (± 4855019) 136078689 ns/iter (± 2907176) 1.01
es/full/all/es2019 136313185 ns/iter (± 5791381) 135119526 ns/iter (± 2137670) 1.01
es/full/all/es2020 136054460 ns/iter (± 10280012) 130745686 ns/iter (± 2751864) 1.04
es/full/parser 730875 ns/iter (± 34231) 696969 ns/iter (± 19466) 1.05
es/full/base/fixer 30424 ns/iter (± 2825) 28655 ns/iter (± 235) 1.06
es/full/base/resolver_and_hygiene 88909 ns/iter (± 3715) 86484 ns/iter (± 2887) 1.03
serialization of ast node 208 ns/iter (± 4) 207 ns/iter (± 4) 1.00
serialization of serde 215 ns/iter (± 8) 212 ns/iter (± 2) 1.01

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

Please sign in to comment.