swc_common/plugin/serialized.rs
1use std::any::type_name;
2
3use anyhow::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6#[non_exhaustive]
7#[cfg_attr(
8 feature = "__plugin",
9 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
10)]
11#[cfg_attr(feature = "__plugin", derive(bytecheck::CheckBytes))]
12#[cfg_attr(feature = "__plugin", repr(u32))]
13/// Enum for possible errors while running transform via plugin.
14///
15/// This error indicates internal operation failure either in plugin_runner
16/// or plugin_macro. Plugin's transform fn itself does not allow to return
17/// error - instead it should use provided `handler` to emit corresponding error
18/// to the host.
19pub enum PluginError {
20 /// Occurs when failed to convert size passed from host / guest into usize
21 /// or similar for the conversion. This is an internal error rasied via
22 /// plugin_macro, normally plugin author should not raise this manually.
23 SizeInteropFailure(String),
24 /// Occurs when failed to reconstruct a struct from `Serialized`.
25 Deserialize(String),
26 /// Occurs when failed to serialize a struct into `Serialized`.
27 /// Unlike deserialize error, this error cannot forward any context for the
28 /// raw bytes: when serialize failed, there's nothing we can pass between
29 /// runtime.
30 Serialize(String),
31}
32
33/// A wrapper type for the internal representation of serialized data.
34///
35/// Wraps internal representation of serialized data for exchanging data between
36/// plugin to the host. Consumers should not rely on specific details of byte
37/// format struct contains: it is strict implementation detail which can
38/// change anytime.
39pub struct PluginSerializedBytes {
40 pub(crate) field: rkyv::util::AlignedVec,
41}
42
43#[cfg(feature = "__plugin")]
44impl PluginSerializedBytes {
45 /**
46 * Constructs an instance from already serialized byte
47 * slices.
48 */
49 #[tracing::instrument(level = "info", skip_all)]
50 pub fn from_slice(bytes: &[u8]) -> PluginSerializedBytes {
51 let mut field = rkyv::util::AlignedVec::new();
52 field.extend_from_slice(bytes);
53 PluginSerializedBytes { field }
54 }
55
56 /**
57 * Constructs an instance from versioned struct by serializing it.
58 *
59 * This is sort of mimic TryFrom behavior, since we can't use generic
60 * to implement TryFrom trait
61 */
62 /*
63 #[tracing::instrument(level = "info", skip_all)]
64 pub fn try_serialize<W>(t: &VersionedSerializable<W>) -> Result<Self, Error>
65 where
66 W: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
67 {
68 rkyv::to_bytes::<_, 512>(t)
69 .map(|field| PluginSerializedBytes { field })
70 .map_err(|err| match err {
71 rkyv::ser::serializers::CompositeSerializerError::SerializerError(e) => e.into(),
72 rkyv::ser::serializers::CompositeSerializerError::ScratchSpaceError(_e) => {
73 Error::msg("AllocScratchError")
74 }
75 rkyv::ser::serializers::CompositeSerializerError::SharedError(_e) => {
76 Error::msg("SharedSerializeMapError")
77 }
78 })
79 }
80 */
81
82 #[tracing::instrument(level = "info", skip_all)]
83 pub fn try_serialize<W>(t: &VersionedSerializable<W>) -> Result<Self, Error>
84 where
85 W: for<'a> rkyv::Serialize<
86 rancor::Strategy<
87 rkyv::ser::Serializer<
88 rkyv::util::AlignedVec,
89 rkyv::ser::allocator::ArenaHandle<'a>,
90 rkyv::ser::sharing::Share,
91 >,
92 rancor::Error,
93 >,
94 >,
95 {
96 rkyv::to_bytes::<rancor::Error>(t)
97 .map(|field| PluginSerializedBytes { field })
98 .map_err(|err| err.into())
99 }
100
101 /*
102 * Internal fn to constructs an instance from raw bytes ptr.
103 */
104 #[tracing::instrument(level = "info", skip_all)]
105 #[allow(clippy::not_unsafe_ptr_arg_deref)]
106 pub fn from_raw_ptr(
107 raw_allocated_ptr: *const u8,
108 raw_allocated_ptr_len: usize,
109 ) -> PluginSerializedBytes {
110 let raw_ptr_bytes =
111 unsafe { std::slice::from_raw_parts(raw_allocated_ptr, raw_allocated_ptr_len) };
112
113 PluginSerializedBytes::from_slice(raw_ptr_bytes)
114 }
115
116 pub fn as_slice(&self) -> &[u8] {
117 self.field.as_slice()
118 }
119
120 pub fn as_ptr(&self) -> (*const u8, usize) {
121 (self.field.as_ptr(), self.field.len())
122 }
123
124 #[tracing::instrument(level = "info", skip_all)]
125 pub fn deserialize<W>(&self) -> Result<VersionedSerializable<W>, Error>
126 where
127 W: rkyv::Archive,
128 W::Archived: rkyv::Deserialize<W, rancor::Strategy<rkyv::de::Pool, rancor::Error>>,
129 {
130 use anyhow::Context;
131
132 let archived = unsafe {
133 // Safety: We intentionally skip bytecheck because it makes Wasm plugins not
134 // backward compatible.
135 rkyv::access_unchecked::<W::Archived>(&self.field[..])
136 };
137
138 let deserialized = rkyv::deserialize(archived)
139 .with_context(|| format!("failed to deserialize `{}`", type_name::<W>()))?;
140
141 Ok(VersionedSerializable(deserialized))
142 }
143
144 /*
145 #[tracing::instrument(level = "info", skip_all)]
146 pub fn deserialize<W>(&self) -> Result<VersionedSerializable<W>, Error>
147 where
148 W: rkyv::Archive,
149 W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>
150 + for<'a> rkyv::CheckBytes<rkyv::validation::validators::DefaultValidator<'a>>,
151 {
152 use anyhow::Context;
153
154 let archived = rkyv::check_archived_root::<VersionedSerializable<W>>(&self.field[..])
155 .map_err(|err| {
156 anyhow::format_err!("wasm plugin bytecheck failed {:?}", err.to_string())
157 })?;
158
159 archived
160 .deserialize(&mut rkyv::de::deserializers::SharedDeserializeMap::new())
161 .with_context(|| format!("failed to deserialize `{}`", type_name::<W>()))
162 } */
163}
164
165/// A wrapper type for the structures to be passed into plugins
166/// serializes the contained value out-of-line so that newer
167/// versions can be viewed as the older version.
168///
169/// First field indicate version of struct type (schema). Any consumers like
170/// swc_plugin_macro can use this to validate compatiblility before attempt to
171/// serialize.
172#[cfg_attr(
173 feature = "__plugin",
174 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
175)]
176#[repr(transparent)]
177#[cfg_attr(feature = "__plugin", derive(bytecheck::CheckBytes))]
178#[derive(Debug)]
179pub struct VersionedSerializable<T>(
180 // [NOTE]: https://github.com/rkyv/rkyv/issues/373#issuecomment-1546360897
181 //#[cfg_attr(feature = "__plugin", with(rkyv::with::AsBox))]
182 pub T,
183);
184
185impl<T> VersionedSerializable<T> {
186 pub fn new(value: T) -> Self {
187 Self(value)
188 }
189
190 pub fn inner(&self) -> &T {
191 &self.0
192 }
193
194 pub fn into_inner(self) -> T {
195 self.0
196 }
197}