swc_common/plugin/serialized.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
use std::any::type_name;
use anyhow::Error;
#[cfg(feature = "__rkyv")]
use rkyv::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(
feature = "__plugin",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "__plugin", archive(check_bytes))]
#[cfg_attr(feature = "__plugin", archive_attr(repr(u32)))]
/// Enum for possible errors while running transform via plugin.
///
/// This error indicates internal operation failure either in plugin_runner
/// or plugin_macro. Plugin's transform fn itself does not allow to return
/// error - instead it should use provided `handler` to emit corresponding error
/// to the host.
pub enum PluginError {
/// Occurs when failed to convert size passed from host / guest into usize
/// or similar for the conversion. This is an internal error rasied via
/// plugin_macro, normally plugin author should not raise this manually.
SizeInteropFailure(String),
/// Occurs when failed to reconstruct a struct from `Serialized`.
Deserialize(String),
/// Occurs when failed to serialize a struct into `Serialized`.
/// Unlike deserialize error, this error cannot forward any context for the
/// raw bytes: when serialize failed, there's nothing we can pass between
/// runtime.
Serialize(String),
}
/// A wrapper type for the internal representation of serialized data.
///
/// 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")]
impl PluginSerializedBytes {
/**
* Constructs an instance from already serialized byte
* slices.
*/
#[tracing::instrument(level = "info", skip_all)]
pub fn from_slice(bytes: &[u8]) -> PluginSerializedBytes {
let mut field = rkyv::AlignedVec::new();
field.extend_from_slice(bytes);
PluginSerializedBytes { field }
}
/**
* 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
*/
#[tracing::instrument(level = "info", skip_all)]
pub fn try_serialize<W>(t: &VersionedSerializable<W>) -> Result<Self, Error>
where
W: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
{
rkyv::to_bytes::<_, 512>(t)
.map(|field| PluginSerializedBytes { field })
.map_err(|err| match err {
rkyv::ser::serializers::CompositeSerializerError::SerializerError(e) => e.into(),
rkyv::ser::serializers::CompositeSerializerError::ScratchSpaceError(_e) => {
Error::msg("AllocScratchError")
}
rkyv::ser::serializers::CompositeSerializerError::SharedError(_e) => {
Error::msg("SharedSerializeMapError")
}
})
}
/*
* Internal fn to constructs an instance from raw bytes ptr.
*/
#[tracing::instrument(level = "info", skip_all)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub 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())
}
#[tracing::instrument(level = "info", skip_all)]
pub fn deserialize<W>(&self) -> Result<VersionedSerializable<W>, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>
+ for<'a> rkyv::CheckBytes<rkyv::validation::validators::DefaultValidator<'a>>,
{
use anyhow::Context;
let archived = rkyv::check_archived_root::<VersionedSerializable<W>>(&self.field[..])
.map_err(|err| {
anyhow::format_err!("wasm plugin bytecheck failed {:?}", err.to_string())
})?;
archived
.deserialize(&mut rkyv::de::deserializers::SharedDeserializeMap::new())
.with_context(|| format!("failed to deserialize `{}`", type_name::<W>()))
}
}
/// A wrapper type for the structures to be passed into plugins
/// serializes the contained value out-of-line so that newer
/// versions can be viewed as the older version.
///
/// First field indicate version of struct type (schema). Any consumers like
/// swc_plugin_macro can use this to validate compatiblility before attempt to
/// serialize.
#[cfg_attr(
feature = "__plugin",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[repr(transparent)]
#[cfg_attr(feature = "__plugin", archive(check_bytes))]
#[cfg_attr(feature = "__plugin", archive_attr(repr(transparent)))]
#[derive(Debug)]
pub struct VersionedSerializable<T>(
// [NOTE]: https://github.com/rkyv/rkyv/issues/373#issuecomment-1546360897
//#[cfg_attr(feature = "__plugin", with(rkyv::with::AsBox))]
pub T,
);
impl<T> VersionedSerializable<T> {
pub fn new(value: T) -> Self {
Self(value)
}
pub fn inner(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> T {
self.0
}
}