Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(swc/common): bikeshed Serialized struct #5120

Merged
merged 10 commits into from
Jul 6, 2022
20 changes: 10 additions & 10 deletions crates/swc/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl RustPlugins {
use std::{path::PathBuf, sync::Arc};

use anyhow::Context;
use swc_common::{plugin::Serialized, FileName};
use swc_common::{plugin::PluginSerializedBytes, 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 +92,7 @@ impl RustPlugins {
inner: self.comments.clone(),
},
|| {
let mut serialized = Serialized::serialize(&n)?;
let mut serialized = PluginSerializedBytes::try_serialize(&n)?;

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

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

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

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

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

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

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

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| Serialized::serialize(&value))?;
.and_then(|value| PluginSerializedBytes::try_serialize(&value))?;

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

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

Serialized::deserialize(&serialized)
serialized.deserialize()
},
)
}
Expand Down
175 changes: 89 additions & 86 deletions crates/swc_common/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,48 +41,38 @@ pub enum PluginError {
Serialize(String),
}

/// Wraps internal representation of serialized data. Consumers should not
/// rely on specific details of byte format struct contains: it is
/// strictly implementation detail which can change anytime.
pub struct Serialized {
field: rkyv::AlignedVec,
/// Wraps internal representation of serialized data for exchanging data between
/// plugin to the host. Consumers should not rely on specific details of byte
/// format struct contains: it is strict implementation detail which can
/// change anytime.
pub struct PluginSerializedBytes {
pub(crate) field: rkyv::AlignedVec,
}

#[cfg(feature = "plugin-base")]
impl Serialized {
pub fn new_for_plugin(bytes: &[u8], len: i32) -> Serialized {
let mut vec = rkyv::AlignedVec::with_capacity(
len.try_into()
.expect("Cannot determine size of the serialized bytes"),
);
vec.extend_from_slice(bytes);
Serialized { field: vec }
impl PluginSerializedBytes {
/**
* Constructs an instance from already serialized byte
* slices.
*/
pub fn from_slice(bytes: &[u8]) -> PluginSerializedBytes {
let mut field = rkyv::AlignedVec::new();
field.extend_from_slice(bytes);
PluginSerializedBytes { field }
}

pub fn from(vec: rkyv::AlignedVec) -> Serialized {
Serialized { field: vec }
}

/// Not an actual trait Into impl: simple wrapper to deserialize<T>:expect()
pub fn into<T>(self) -> T
where
T: rkyv::Archive,
T::Archived: rkyv::Deserialize<T, rkyv::de::deserializers::SharedDeserializeMap>,
{
Serialized::deserialize(&self).expect("Should able to deserialize")
}

#[allow(clippy::should_implement_trait)]
pub fn as_ref(&self) -> &rkyv::AlignedVec {
&self.field
}

pub fn serialize<W>(t: &W) -> Result<Serialized, Error>
/**
* Constructs an instance from given 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>
where
W: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
{
rkyv::to_bytes::<_, 512>(t)
.map(Serialized::from)
.map(|field| PluginSerializedBytes { field })
.map_err(|err| match err {
rkyv::ser::serializers::CompositeSerializerError::SerializerError(e) => e.into(),
rkyv::ser::serializers::CompositeSerializerError::ScratchSpaceError(e) => {
Expand All @@ -94,73 +84,86 @@ impl Serialized {
})
}

pub fn deserialize<W>(bytes: &Serialized) -> Result<W, Error>
/*
* Internal fn to constructs an instance from raw bytes ptr.
*/
fn from_raw_ptr(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: usize,
) -> PluginSerializedBytes {
let raw_ptr_bytes =
unsafe { std::slice::from_raw_parts(raw_allocated_ptr, raw_allocated_ptr_len) };

PluginSerializedBytes::from_slice(raw_ptr_bytes)
}

pub fn as_slice(&self) -> &[u8] {
self.field.as_slice()
}

pub fn as_ptr(&self) -> (*const u8, usize) {
(self.field.as_ptr(), self.field.len())
}

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

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

archived
.deserialize(&mut rkyv::de::deserializers::SharedDeserializeMap::new())
.with_context(|| format!("failed to deserialize `{}`", type_name::<W>()))
}
}

/// Convenient wrapper to Serialized::* to construct actual struct from raw
/// ptr. This is common workflow on both of runtime (host / plugin) to
/// deserialize struct from allocated / copied ptr.
///
/// # Safety
/// This is naturally unsafe by constructing bytes slice from raw ptr.
pub unsafe fn deserialize_from_ptr<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
// Create serialized bytes slice from ptr
let raw_ptr_bytes = unsafe {
std::slice::from_raw_parts(raw_allocated_ptr, raw_allocated_ptr_len.try_into()?)
};

let serialized = Serialized::new_for_plugin(raw_ptr_bytes, raw_allocated_ptr_len);
Serialized::deserialize(&serialized)
}

/// Deserialize `Fallible` struct from raw ptr. This is similar to
/// `deserialize_from_ptr` but for the struct requires bounds to the
/// SharedSerializeRegistry which cannot be Infallible. Internally this does
/// not call deserialize with Infallible deserializer, use
/// SharedDeserializeMap instead.
///
/// # Safety
/// This is unsafe by construting bytes slice from raw ptr also deserialize
/// it without slice bound check.
pub unsafe fn deserialize_from_ptr_fallible<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
// Create serialized bytes slice from ptr
let raw_ptr_bytes = unsafe {
std::slice::from_raw_parts(raw_allocated_ptr, raw_allocated_ptr_len.try_into()?)
};

let serialized = Serialized::new_for_plugin(raw_ptr_bytes, raw_allocated_ptr_len);
/// Simple wrapper around constructing PluginSerializedBytes from raw
/// ptr to call deserialize to support common workflow on both of runtime
/// (host / plugin) to instantiate a struct from allocated / copied ptr.
///
/// # Safety
/// This is naturally unsafe by constructing bytes slice from raw ptr.
pub unsafe fn deserialize_from_ptr<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
let serialized =
PluginSerializedBytes::from_raw_ptr(raw_allocated_ptr, raw_allocated_ptr_len as usize);

serialized.deserialize()
}

unsafe {
rkyv::from_bytes_unchecked(serialized.as_ref())
.map_err(|err| Error::msg("Failed to deserialize given ptr"))
}
/// Deserialize `Fallible` struct from raw ptr. This is similar to
/// `deserialize_from_ptr` but for the struct requires bounds to the
/// SharedSerializeRegistry which cannot be Infallible. Internally this does
/// not call deserialize with Infallible deserializer, use
/// SharedDeserializeMap instead.
///
/// # Safety
/// This is unsafe by construting bytes slice from raw ptr also deserialize
/// it without slice bound check.
pub unsafe fn deserialize_from_ptr_into_fallible<W>(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: i32,
) -> Result<W, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
let serialized =
PluginSerializedBytes::from_raw_ptr(raw_allocated_ptr, raw_allocated_ptr_len as usize);

unsafe {
rkyv::from_bytes_unchecked(&serialized.field)
.map_err(|err| Error::msg("Failed to deserialize given ptr"))
}
}

Expand Down
32 changes: 14 additions & 18 deletions crates/swc_common/src/syntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +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::Serialized::serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let serialized_ref = serialized.as_ref();
let ptr = serialized_ref.as_ptr();
let len = serialized_ref.len();
let serialized =
crate::plugin::PluginSerializedBytes::try_serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();

// Calling host proxy fn. Inside of host proxy, host will
// write the result into allocated context in the guest memory space.
Expand All @@ -193,7 +192,7 @@ impl Mark {

// Deserialize result, assign / return values as needed.
let context: MutableMarkContext = unsafe {
crate::plugin::Serialized::deserialize_from_ptr(
crate::plugin::deserialize_from_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
Expand All @@ -220,18 +219,17 @@ 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::Serialized::serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let serialized_ref = serialized.as_ref();
let ptr = serialized_ref.as_ptr();
let len = serialized_ref.len();
let serialized =
crate::plugin::PluginSerializedBytes::try_serialize(&MutableMarkContext(0, 0, 0))
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();

unsafe {
__mark_least_ancestor(a.0, b.0, ptr as _);
}

let context: MutableMarkContext = unsafe {
crate::plugin::Serialized::deserialize_from_ptr(
crate::plugin::deserialize_from_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
Expand Down Expand Up @@ -386,18 +384,16 @@ impl SyntaxContext {
#[cfg(all(feature = "plugin-mode", target_arch = "wasm32"))]
pub fn remove_mark(&mut self) -> Mark {
let context = MutableMarkContext(0, 0, 0);
let serialized =
crate::plugin::Serialized::serialize(&context).expect("Should be serializable");
let serialized_ref = serialized.as_ref();
let ptr = serialized_ref.as_ptr();
let len = serialized_ref.len();
let serialized = crate::plugin::PluginSerializedBytes::try_serialize(&context)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();

unsafe {
__syntax_context_remove_mark_proxy(self.0, ptr as _);
}

let context: MutableMarkContext = unsafe {
crate::plugin::Serialized::deserialize_from_ptr(
crate::plugin::deserialize_from_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
Expand Down
13 changes: 5 additions & 8 deletions crates/swc_plugin/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use swc_common::{plugin::Serialized, sync::OnceCell};
use swc_common::{plugin::PluginSerializedBytes, sync::OnceCell};

use crate::pseudo_scoped_key::PseudoScopedKey;

Expand All @@ -18,16 +18,13 @@ 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 =
Serialized::serialize(&*db.diagnostic).expect("Should able to serialize Diagnostic");
let diag_ref = diag.as_ref();

let ptr = diag_ref.as_ptr() as i32;
let len = diag_ref.len() as i32;
let diag = PluginSerializedBytes::try_serialize(&*db.diagnostic)
.expect("Should able to serialize Diagnostic");
let (ptr, len) = diag.as_ptr();

#[cfg(target_arch = "wasm32")] // Allow testing
unsafe {
__emit_diagnostics(ptr, len);
__emit_diagnostics(ptr as i32, len as i32);
}
}
}
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::{PluginError, Serialized},
plugin::{deserialize_from_ptr, PluginError, PluginSerializedBytes},
};

pub mod comments {
Expand Down
Loading