summaryrefslogtreecommitdiff
path: root/serde_v8/ser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'serde_v8/ser.rs')
-rw-r--r--serde_v8/ser.rs554
1 files changed, 554 insertions, 0 deletions
diff --git a/serde_v8/ser.rs b/serde_v8/ser.rs
new file mode 100644
index 000000000..5241aeaef
--- /dev/null
+++ b/serde_v8/ser.rs
@@ -0,0 +1,554 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use serde::ser;
+use serde::ser::Serialize;
+
+use std::cell::RefCell;
+
+use crate::error::{Error, Result};
+use crate::keys::v8_struct_key;
+use crate::magic::transl8::MAGIC_FIELD;
+use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8};
+use crate::{magic, Buffer, ByteString, U16String};
+
+type JsValue<'s> = v8::Local<'s, v8::Value>;
+type JsResult<'s> = Result<JsValue<'s>>;
+
+type ScopePtr<'a, 'b, 'c> = &'c RefCell<&'b mut v8::HandleScope<'a>>;
+
+pub fn to_v8<'a, T>(scope: &mut v8::HandleScope<'a>, input: T) -> JsResult<'a>
+where
+ T: Serialize,
+{
+ let scopeptr = RefCell::new(scope);
+ let serializer = Serializer::new(&scopeptr);
+
+ input.serialize(serializer)
+}
+
+/// Wraps other serializers into an enum tagged variant form.
+/// Uses {"Variant": ...payload...} for compatibility with serde-json.
+pub struct VariantSerializer<'a, 'b, 'c, S> {
+ inner: S,
+ scope: ScopePtr<'a, 'b, 'c>,
+ variant: &'static str,
+}
+
+impl<'a, 'b, 'c, S> VariantSerializer<'a, 'b, 'c, S> {
+ pub fn new(
+ scope: ScopePtr<'a, 'b, 'c>,
+ variant: &'static str,
+ inner: S,
+ ) -> Self {
+ Self {
+ inner,
+ scope,
+ variant,
+ }
+ }
+
+ fn end(self, inner: impl FnOnce(S) -> JsResult<'a>) -> JsResult<'a> {
+ let value = inner(self.inner)?;
+ let scope = &mut *self.scope.borrow_mut();
+ let null = v8::null(scope).into();
+ let key = v8_struct_key(scope, self.variant).into();
+ let obj =
+ v8::Object::with_prototype_and_properties(scope, null, &[key], &[value]);
+ Ok(obj.into())
+ }
+}
+
+impl<'a, 'b, 'c, S> ser::SerializeTupleVariant
+ for VariantSerializer<'a, 'b, 'c, S>
+where
+ S: ser::SerializeTupleStruct<Ok = JsValue<'a>, Error = Error>,
+{
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ value: &T,
+ ) -> Result<()> {
+ self.inner.serialize_field(value)
+ }
+
+ fn end(self) -> JsResult<'a> {
+ self.end(S::end)
+ }
+}
+
+impl<'a, 'b, 'c, S> ser::SerializeStructVariant
+ for VariantSerializer<'a, 'b, 'c, S>
+where
+ S: ser::SerializeStruct<Ok = JsValue<'a>, Error = Error>,
+{
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ key: &'static str,
+ value: &T,
+ ) -> Result<()> {
+ self.inner.serialize_field(key, value)
+ }
+
+ fn end(self) -> JsResult<'a> {
+ self.end(S::end)
+ }
+}
+
+pub struct ArraySerializer<'a, 'b, 'c> {
+ pending: Vec<JsValue<'a>>,
+ scope: ScopePtr<'a, 'b, 'c>,
+}
+
+impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> {
+ pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option<usize>) -> Self {
+ let pending = match len {
+ Some(len) => Vec::with_capacity(len),
+ None => vec![],
+ };
+ Self { pending, scope }
+ }
+}
+
+impl<'a, 'b, 'c> ser::SerializeSeq for ArraySerializer<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized + Serialize>(
+ &mut self,
+ value: &T,
+ ) -> Result<()> {
+ let x = value.serialize(Serializer::new(self.scope))?;
+ self.pending.push(x);
+ Ok(())
+ }
+
+ fn end(self) -> JsResult<'a> {
+ let elements = self.pending.iter().as_slice();
+ let scope = &mut *self.scope.borrow_mut();
+ let arr = v8::Array::new_with_elements(scope, elements);
+ Ok(arr.into())
+ }
+}
+
+impl<'a, 'b, 'c> ser::SerializeTuple for ArraySerializer<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized + Serialize>(
+ &mut self,
+ value: &T,
+ ) -> Result<()> {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> JsResult<'a> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl<'a, 'b, 'c> ser::SerializeTupleStruct for ArraySerializer<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ value: &T,
+ ) -> Result<()> {
+ ser::SerializeTuple::serialize_element(self, value)
+ }
+
+ fn end(self) -> JsResult<'a> {
+ ser::SerializeTuple::end(self)
+ }
+}
+
+pub struct ObjectSerializer<'a, 'b, 'c> {
+ scope: ScopePtr<'a, 'b, 'c>,
+ keys: Vec<v8::Local<'a, v8::Name>>,
+ values: Vec<JsValue<'a>>,
+}
+
+impl<'a, 'b, 'c> ObjectSerializer<'a, 'b, 'c> {
+ pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: usize) -> Self {
+ let keys = Vec::with_capacity(len);
+ let values = Vec::with_capacity(len);
+ Self {
+ scope,
+ keys,
+ values,
+ }
+ }
+}
+
+impl<'a, 'b, 'c> ser::SerializeStruct for ObjectSerializer<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ key: &'static str,
+ value: &T,
+ ) -> Result<()> {
+ let value = value.serialize(Serializer::new(self.scope))?;
+ let scope = &mut *self.scope.borrow_mut();
+ let key = v8_struct_key(scope, key).into();
+ self.keys.push(key);
+ self.values.push(value);
+ Ok(())
+ }
+
+ fn end(self) -> JsResult<'a> {
+ let scope = &mut *self.scope.borrow_mut();
+ let null = v8::null(scope);
+ let obj = v8::Object::with_prototype_and_properties(
+ scope,
+ null.into(),
+ &self.keys[..],
+ &self.values[..],
+ );
+ Ok(obj.into())
+ }
+}
+
+pub struct MagicalSerializer<'a, 'b, 'c, T> {
+ scope: ScopePtr<'a, 'b, 'c>,
+ opaque: u64,
+ p1: std::marker::PhantomData<T>,
+}
+
+impl<'a, 'b, 'c, T> MagicalSerializer<'a, 'b, 'c, T> {
+ pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> MagicalSerializer<'a, 'b, 'c, T> {
+ Self {
+ scope,
+ opaque: 0,
+ p1: std::marker::PhantomData::<T> {},
+ }
+ }
+}
+
+impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct
+ for MagicalSerializer<'a, 'b, 'c, T>
+{
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<U: ?Sized + Serialize>(
+ &mut self,
+ key: &'static str,
+ value: &U,
+ ) -> Result<()> {
+ assert_eq!(key, MAGIC_FIELD);
+ let ptr: &U = value;
+ // SAFETY: MagicalSerializer only ever receives single field u64s,
+ // type-safety is ensured by MAGIC_NAME checks in `serialize_struct()`
+ self.opaque = unsafe { opaque_recv(ptr) };
+ Ok(())
+ }
+
+ fn end(self) -> JsResult<'a> {
+ // SAFETY: transerialization assumptions imply `T` is still alive.
+ let x: &T = unsafe { opaque_deref(self.opaque) };
+ let scope = &mut *self.scope.borrow_mut();
+ x.to_v8(scope)
+ }
+}
+
+// Dispatches between magic and regular struct serializers
+pub enum StructSerializers<'a, 'b, 'c> {
+ Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>),
+ MagicBuffer(MagicalSerializer<'a, 'b, 'c, Buffer>),
+ MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>),
+ MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>),
+ Regular(ObjectSerializer<'a, 'b, 'c>),
+}
+
+impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ key: &'static str,
+ value: &T,
+ ) -> Result<()> {
+ match self {
+ StructSerializers::Magic(s) => s.serialize_field(key, value),
+ StructSerializers::MagicBuffer(s) => s.serialize_field(key, value),
+ StructSerializers::MagicByteString(s) => s.serialize_field(key, value),
+ StructSerializers::MagicU16String(s) => s.serialize_field(key, value),
+ StructSerializers::Regular(s) => s.serialize_field(key, value),
+ }
+ }
+
+ fn end(self) -> JsResult<'a> {
+ match self {
+ StructSerializers::Magic(s) => s.end(),
+ StructSerializers::MagicBuffer(s) => s.end(),
+ StructSerializers::MagicByteString(s) => s.end(),
+ StructSerializers::MagicU16String(s) => s.end(),
+ StructSerializers::Regular(s) => s.end(),
+ }
+ }
+}
+
+// Serializes to JS Objects, NOT JS Maps ...
+pub struct MapSerializer<'a, 'b, 'c> {
+ scope: ScopePtr<'a, 'b, 'c>,
+ keys: Vec<v8::Local<'a, v8::Name>>,
+ values: Vec<JsValue<'a>>,
+}
+
+impl<'a, 'b, 'c> MapSerializer<'a, 'b, 'c> {
+ pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option<usize>) -> Self {
+ let keys = Vec::with_capacity(len.unwrap_or_default());
+ let values = Vec::with_capacity(len.unwrap_or_default());
+ Self {
+ scope,
+ keys,
+ values,
+ }
+ }
+}
+
+impl<'a, 'b, 'c> ser::SerializeMap for MapSerializer<'a, 'b, 'c> {
+ type Ok = JsValue<'a>;
+ type Error = Error;
+
+ fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<()> {
+ let key = key.serialize(Serializer::new(self.scope))?;
+ self.keys.push(key.try_into().map_err(|_| {
+ Error::Message("Serialized Maps expect String keys".into())
+ })?);
+ Ok(())
+ }
+
+ fn serialize_value<T: ?Sized + Serialize>(
+ &mut self,
+ value: &T,
+ ) -> Result<()> {
+ let v8_value = value.serialize(Serializer::new(self.scope))?;
+ self.values.push(v8_value);
+ Ok(())
+ }
+
+ fn end(self) -> JsResult<'a> {
+ debug_assert!(self.keys.len() == self.values.len());
+ let scope = &mut *self.scope.borrow_mut();
+ let null = v8::null(scope).into();
+ let obj = v8::Object::with_prototype_and_properties(
+ scope,
+ null,
+ &self.keys[..],
+ &self.values[..],
+ );
+ Ok(obj.into())
+ }
+}
+
+pub struct Serializer<'a, 'b, 'c> {
+ scope: ScopePtr<'a, 'b, 'c>,
+}
+
+impl<'a, 'b, 'c> Serializer<'a, 'b, 'c> {
+ pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self {
+ Serializer { scope }
+ }
+}
+
+macro_rules! forward_to {
+ ($($name:ident($ty:ty, $to:ident, $lt:lifetime);)*) => {
+ $(fn $name(self, v: $ty) -> JsResult<$lt> {
+ self.$to(v as _)
+ })*
+ };
+}
+
+impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
+ type Ok = v8::Local<'a, v8::Value>;
+ type Error = Error;
+
+ type SerializeSeq = ArraySerializer<'a, 'b, 'c>;
+ type SerializeTuple = ArraySerializer<'a, 'b, 'c>;
+ type SerializeTupleStruct = ArraySerializer<'a, 'b, 'c>;
+ type SerializeTupleVariant =
+ VariantSerializer<'a, 'b, 'c, ArraySerializer<'a, 'b, 'c>>;
+ type SerializeMap = MapSerializer<'a, 'b, 'c>;
+ type SerializeStruct = StructSerializers<'a, 'b, 'c>;
+ type SerializeStructVariant =
+ VariantSerializer<'a, 'b, 'c, StructSerializers<'a, 'b, 'c>>;
+
+ forward_to! {
+ serialize_i8(i8, serialize_i32, 'a);
+ serialize_i16(i16, serialize_i32, 'a);
+
+ serialize_u8(u8, serialize_u32, 'a);
+ serialize_u16(u16, serialize_u32, 'a);
+
+ serialize_f32(f32, serialize_f64, 'a);
+ serialize_u64(u64, serialize_f64, 'a);
+ serialize_i64(i64, serialize_f64, 'a);
+ }
+
+ fn serialize_i32(self, v: i32) -> JsResult<'a> {
+ Ok(v8::Integer::new(&mut self.scope.borrow_mut(), v).into())
+ }
+
+ fn serialize_u32(self, v: u32) -> JsResult<'a> {
+ Ok(v8::Integer::new_from_unsigned(&mut self.scope.borrow_mut(), v).into())
+ }
+
+ fn serialize_f64(self, v: f64) -> JsResult<'a> {
+ Ok(v8::Number::new(&mut self.scope.borrow_mut(), v).into())
+ }
+
+ fn serialize_bool(self, v: bool) -> JsResult<'a> {
+ Ok(v8::Boolean::new(&mut self.scope.borrow_mut(), v).into())
+ }
+
+ fn serialize_char(self, _v: char) -> JsResult<'a> {
+ unimplemented!();
+ }
+
+ fn serialize_str(self, v: &str) -> JsResult<'a> {
+ v8::String::new(&mut self.scope.borrow_mut(), v)
+ .map(|v| v.into())
+ .ok_or(Error::ExpectedString)
+ }
+
+ fn serialize_bytes(self, _v: &[u8]) -> JsResult<'a> {
+ // TODO: investigate using Uint8Arrays
+ unimplemented!()
+ }
+
+ fn serialize_none(self) -> JsResult<'a> {
+ Ok(v8::null(&mut self.scope.borrow_mut()).into())
+ }
+
+ fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> JsResult<'a> {
+ value.serialize(self)
+ }
+
+ fn serialize_unit(self) -> JsResult<'a> {
+ Ok(v8::null(&mut self.scope.borrow_mut()).into())
+ }
+
+ fn serialize_unit_struct(self, _name: &'static str) -> JsResult<'a> {
+ Ok(v8::null(&mut self.scope.borrow_mut()).into())
+ }
+
+ /// For compatibility with serde-json, serialises unit variants as "Variant" strings.
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> JsResult<'a> {
+ Ok(v8_struct_key(&mut self.scope.borrow_mut(), variant).into())
+ }
+
+ fn serialize_newtype_struct<T: ?Sized + Serialize>(
+ self,
+ _name: &'static str,
+ value: &T,
+ ) -> JsResult<'a> {
+ value.serialize(self)
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + Serialize>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ value: &T,
+ ) -> JsResult<'a> {
+ let scope = self.scope;
+ let x = self.serialize_newtype_struct(variant, value)?;
+ VariantSerializer::new(scope, variant, x).end(Ok)
+ }
+
+ /// Serialises any Rust iterable into a JS Array
+ fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+ Ok(ArraySerializer::new(self.scope, len))
+ }
+
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ self.serialize_tuple(len)
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ Ok(VariantSerializer::new(
+ self.scope,
+ variant,
+ self.serialize_tuple_struct(variant, len)?,
+ ))
+ }
+
+ fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+ // Serializes a rust Map (e.g: BTreeMap, HashMap) to a v8 Object
+ // TODO: consider allowing serializing to v8 Maps (e.g: via a magic type)
+ // since they're lighter and better suited for K/V data
+ // and maybe restrict keys (e.g: strings and numbers)
+ Ok(MapSerializer::new(self.scope, len))
+ }
+
+ /// Serialises Rust typed structs into plain JS objects.
+ fn serialize_struct(
+ self,
+ name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeStruct> {
+ match name {
+ ByteString::MAGIC_NAME => {
+ let m = MagicalSerializer::<ByteString>::new(self.scope);
+ Ok(StructSerializers::MagicByteString(m))
+ }
+ U16String::MAGIC_NAME => {
+ let m = MagicalSerializer::<U16String>::new(self.scope);
+ Ok(StructSerializers::MagicU16String(m))
+ }
+ Buffer::MAGIC_NAME => {
+ let m = MagicalSerializer::<Buffer>::new(self.scope);
+ Ok(StructSerializers::MagicBuffer(m))
+ }
+ magic::Value::MAGIC_NAME => {
+ let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope);
+ Ok(StructSerializers::Magic(m))
+ }
+ _ => {
+ // Regular structs
+ let o = ObjectSerializer::new(self.scope, len);
+ Ok(StructSerializers::Regular(o))
+ }
+ }
+ }
+
+ fn serialize_struct_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ let scope = self.scope;
+ let x = self.serialize_struct(variant, len)?;
+ Ok(VariantSerializer::new(scope, variant, x))
+ }
+}