diff options
Diffstat (limited to 'serde_v8/src/ser.rs')
-rw-r--r-- | serde_v8/src/ser.rs | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/serde_v8/src/ser.rs b/serde_v8/src/ser.rs new file mode 100644 index 000000000..a2a57d62e --- /dev/null +++ b/serde_v8/src/ser.rs @@ -0,0 +1,648 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use rusty_v8 as v8; +use serde::ser; +use serde::ser::Serialize; + +use std::cell::RefCell; +use std::convert::TryInto; + +use crate::error::{Error, Result}; +use crate::keys::v8_struct_key; +use crate::magic; + +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 MagicSerializer<'a> { + v8_value: Option<v8::Local<'a, v8::Value>>, +} + +impl<'a> ser::SerializeStruct for MagicSerializer<'a> { + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field<T: ?Sized + Serialize>( + &mut self, + key: &'static str, + value: &T, + ) -> Result<()> { + if key != magic::FIELD { + unreachable!(); + } + let transmuted: u64 = value.serialize(magic::FieldSerializer {})?; + let mv: magic::Value<'a> = unsafe { std::mem::transmute(transmuted) }; + self.v8_value = Some(mv.v8_value); + Ok(()) + } + + fn end(self) -> JsResult<'a> { + Ok(self.v8_value.unwrap()) + } +} + +// TODO(@AaronO): refactor this and streamline how we transmute values +pub struct MagicBufferSerializer<'a, 'b, 'c> { + scope: ScopePtr<'a, 'b, 'c>, + f1: u64, + f2: u64, +} + +impl<'a, 'b, 'c> MagicBufferSerializer<'a, 'b, 'c> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { + Self { + scope, + f1: 0, + f2: 0, + } + } +} + +impl<'a, 'b, 'c> ser::SerializeStruct for MagicBufferSerializer<'a, 'b, 'c> { + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field<T: ?Sized + Serialize>( + &mut self, + key: &'static str, + value: &T, + ) -> Result<()> { + // Get u64 chunk + let transmuted: u64 = value.serialize(magic::FieldSerializer {})?; + match key { + magic::buffer::BUF_FIELD_1 => self.f1 = transmuted, + magic::buffer::BUF_FIELD_2 => self.f2 = transmuted, + _ => unreachable!(), + } + Ok(()) + } + + fn end(self) -> JsResult<'a> { + let x: [usize; 2] = [self.f1 as usize, self.f2 as usize]; + let buf: Box<[u8]> = unsafe { std::mem::transmute(x) }; + let scope = &mut *self.scope.borrow_mut(); + let v8_value = boxed_slice_to_uint8array(scope, buf); + Ok(v8_value.into()) + } +} + +pub struct MagicByteStringSerializer<'a, 'b, 'c> { + scope: ScopePtr<'a, 'b, 'c>, + ptr: Option<std::ptr::NonNull<u8>>, + len: Option<usize>, +} + +impl<'a, 'b, 'c> MagicByteStringSerializer<'a, 'b, 'c> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { + Self { + scope, + ptr: None, + len: None, + } + } +} + +impl<'a, 'b, 'c> ser::SerializeStruct + for MagicByteStringSerializer<'a, 'b, 'c> +{ + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field<T: ?Sized + Serialize>( + &mut self, + key: &'static str, + value: &T, + ) -> Result<()> { + // Get u64 chunk + let transmuted: u64 = value.serialize(magic::FieldSerializer {})?; + match key { + magic::bytestring::FIELD_PTR => { + self.ptr = std::ptr::NonNull::new(transmuted as *mut u8); + } + magic::bytestring::FIELD_LEN => { + self.len = Some(transmuted as usize); + } + _ => unreachable!(), + } + Ok(()) + } + + fn end(self) -> JsResult<'a> { + // SAFETY: This function is only called from ByteString::serialize(), which + // guarantees the Vec is still alive. + let bytes = unsafe { + std::slice::from_raw_parts(self.ptr.unwrap().as_ptr(), self.len.unwrap()) + }; + let scope = &mut *self.scope.borrow_mut(); + let v8_value = + v8::String::new_from_one_byte(scope, bytes, v8::NewStringType::Normal) + .unwrap(); + Ok(v8_value.into()) + } +} + +// Dispatches between magic and regular struct serializers +pub enum StructSerializers<'a, 'b, 'c> { + Magic(MagicSerializer<'a>), + MagicBuffer(MagicBufferSerializer<'a, 'b, 'c>), + MagicByteString(MagicByteStringSerializer<'a, 'b, 'c>), + 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::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::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 { + magic::NAME => { + let m: MagicSerializer<'a> = MagicSerializer { v8_value: None }; + Ok(StructSerializers::Magic(m)) + } + magic::buffer::BUF_NAME => { + let m = MagicBufferSerializer::new(self.scope); + Ok(StructSerializers::MagicBuffer(m)) + } + magic::bytestring::NAME => { + let m = MagicByteStringSerializer::new(self.scope); + Ok(StructSerializers::MagicByteString(m)) + } + _ => { + 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)) + } +} + +// Used to map MagicBuffers to v8 +pub fn boxed_slice_to_uint8array<'a>( + scope: &mut v8::HandleScope<'a>, + buf: Box<[u8]>, +) -> v8::Local<'a, v8::Uint8Array> { + if buf.is_empty() { + let ab = v8::ArrayBuffer::new(scope, 0); + return v8::Uint8Array::new(scope, ab, 0, 0) + .expect("Failed to create UintArray8"); + } + let buf_len = buf.len(); + let backing_store = v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf); + let backing_store_shared = backing_store.make_shared(); + let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared); + v8::Uint8Array::new(scope, ab, 0, buf_len) + .expect("Failed to create UintArray8") +} |