diff options
Diffstat (limited to 'serde_v8/src')
-rw-r--r-- | serde_v8/src/de.rs | 484 | ||||
-rw-r--r-- | serde_v8/src/error.rs | 43 | ||||
-rw-r--r-- | serde_v8/src/keys.rs | 32 | ||||
-rw-r--r-- | serde_v8/src/lib.rs | 13 | ||||
-rw-r--r-- | serde_v8/src/magic.rs | 78 | ||||
-rw-r--r-- | serde_v8/src/payload.rs | 33 | ||||
-rw-r--r-- | serde_v8/src/ser.rs | 632 | ||||
-rw-r--r-- | serde_v8/src/utils.rs | 33 |
8 files changed, 1348 insertions, 0 deletions
diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs new file mode 100644 index 000000000..22d66f7ce --- /dev/null +++ b/serde_v8/src/de.rs @@ -0,0 +1,484 @@ +use rusty_v8 as v8; +use serde::de::{self, Visitor}; +use serde::Deserialize; + +use std::convert::TryFrom; + +use crate::error::{Error, Result}; +use crate::keys::{v8_struct_key, KeyCache}; +use crate::payload::ValueType; + +use crate::magic; + +pub struct Deserializer<'a, 'b, 's> { + input: v8::Local<'a, v8::Value>, + scope: &'b mut v8::HandleScope<'s>, + _key_cache: Option<&'b mut KeyCache>, +} + +impl<'a, 'b, 's> Deserializer<'a, 'b, 's> { + pub fn new( + scope: &'b mut v8::HandleScope<'s>, + input: v8::Local<'a, v8::Value>, + key_cache: Option<&'b mut KeyCache>, + ) -> Self { + Deserializer { + input, + scope, + _key_cache: key_cache, + } + } +} + +// from_v8 deserializes a v8::Value into a Deserializable / rust struct +pub fn from_v8<'de, 'a, 'b, 's, T>( + scope: &'b mut v8::HandleScope<'s>, + input: v8::Local<'a, v8::Value>, +) -> Result<T> +where + T: Deserialize<'de>, +{ + let mut deserializer = Deserializer::new(scope, input, None); + let t = T::deserialize(&mut deserializer)?; + Ok(t) +} + +// like from_v8 except accepts a KeyCache to optimize struct key decoding +pub fn from_v8_cached<'de, 'a, 'b, 's, T>( + scope: &'b mut v8::HandleScope<'s>, + input: v8::Local<'a, v8::Value>, + key_cache: &mut KeyCache, +) -> Result<T> +where + T: Deserialize<'de>, +{ + let mut deserializer = Deserializer::new(scope, input, Some(key_cache)); + let t = T::deserialize(&mut deserializer)?; + Ok(t) +} + +macro_rules! wip { + ($method:ident) => { + fn $method<V>(self, _v: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + unimplemented!() + } + }; +} + +macro_rules! deserialize_signed { + ($dmethod:ident, $vmethod:ident, $t:tt) => { + fn $dmethod<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.$vmethod(self.input.integer_value(&mut self.scope).unwrap() as $t) + } + }; +} + +impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> + for &'x mut Deserializer<'a, 'b, 's> +{ + type Error = Error; + + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + match ValueType::from_v8(self.input) { + ValueType::Null => self.deserialize_unit(visitor), + ValueType::Bool => self.deserialize_bool(visitor), + ValueType::Number => self.deserialize_f64(visitor), + ValueType::String => self.deserialize_string(visitor), + ValueType::Array => self.deserialize_seq(visitor), + ValueType::Object => self.deserialize_map(visitor), + } + } + + fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + if self.input.is_boolean() { + visitor.visit_bool(self.input.boolean_value(&mut self.scope)) + } else { + Err(Error::ExpectedBoolean) + } + } + + deserialize_signed!(deserialize_i8, visit_i8, i8); + deserialize_signed!(deserialize_i16, visit_i16, i16); + deserialize_signed!(deserialize_i32, visit_i32, i32); + deserialize_signed!(deserialize_i64, visit_i64, i64); + // TODO: maybe handle unsigned by itself ? + deserialize_signed!(deserialize_u8, visit_u8, u8); + deserialize_signed!(deserialize_u16, visit_u16, u16); + deserialize_signed!(deserialize_u32, visit_u32, u32); + deserialize_signed!(deserialize_u64, visit_u64, u64); + + fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_f32(self.input.number_value(&mut self.scope).unwrap() as f32) + } + + fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_f64(self.input.number_value(&mut self.scope).unwrap()) + } + + wip!(deserialize_char); + + fn deserialize_str<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_string(visitor) + } + + fn deserialize_string<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + if self.input.is_string() { + let string = self.input.to_rust_string_lossy(self.scope); + visitor.visit_string(string) + } else { + Err(Error::ExpectedString) + } + } + + wip!(deserialize_bytes); + wip!(deserialize_byte_buf); + + fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + if self.input.is_null_or_undefined() { + visitor.visit_none() + } else { + visitor.visit_some(self) + } + } + + fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + if self.input.is_null_or_undefined() { + visitor.visit_unit() + } else { + Err(Error::ExpectedNull) + } + } + + fn deserialize_unit_struct<V>( + self, + _name: &'static str, + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + + // As is done here, serializers are encouraged to treat newtype structs as + // insignificant wrappers around the data they contain. That means not + // parsing anything other than the contained value. + fn deserialize_newtype_struct<V>( + self, + _name: &'static str, + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + let arr = v8::Local::<v8::Array>::try_from(self.input).unwrap(); + let len = arr.length(); + let obj = v8::Local::<v8::Object>::from(arr); + let seq = SeqAccess { + pos: 0, + len, + obj, + scope: self.scope, + }; + visitor.visit_seq(seq) + } + + // Like deserialize_seq except it prefers tuple's length over input array's length + fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + // TODO: error on length mismatch + let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap(); + let seq = SeqAccess { + pos: 0, + len: len as u32, + obj, + scope: self.scope, + }; + visitor.visit_seq(seq) + } + + // Tuple structs look just like sequences in JSON. + fn deserialize_tuple_struct<V>( + self, + _name: &'static str, + len: usize, + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_tuple(len, visitor) + } + + fn deserialize_map<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Assume object, then get_own_property_names + let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap(); + let prop_names = obj.get_own_property_names(self.scope); + let mut keys: Vec<magic::Value> = match prop_names { + Some(names) => from_v8(self.scope, names.into()).unwrap(), + None => vec![], + }; + let keys: Vec<v8::Local<v8::Value>> = + keys.drain(..).map(|x| x.into()).collect(); + + let map = MapAccess { + obj, + keys, + pos: 0, + scope: self.scope, + }; + visitor.visit_map(map) + } + + fn deserialize_struct<V>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + // Magic for serde_v8::magic::Value, to passthrough v8::Value + // TODO: ensure this is cross-platform and there's no alternative + if name == magic::NAME { + let mv = magic::Value { + v8_value: self.input, + }; + let hack: u64 = unsafe { std::mem::transmute(mv) }; + return visitor.visit_u64(hack); + } + + // Regular struct + let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap(); + let map = ObjectAccess { + fields, + obj, + pos: 0, + scope: self.scope, + _cache: None, + }; + + visitor.visit_map(map) + } + + fn deserialize_enum<V>( + self, + _name: &str, + _variants: &'static [&'static str], + _visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + unimplemented!(); + } + + // An identifier in Serde is the type that identifies a field of a struct or + // the variant of an enum. In JSON, struct fields and enum variants are + // represented as strings. In other formats they may be represented as + // numeric indices. + fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_none() + } +} + +struct MapAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + keys: Vec<v8::Local<'a, v8::Value>>, + pos: usize, +} + +impl<'de> de::MapAccess<'de> for MapAccess<'_, '_, '_> { + type Error = Error; + + fn next_key_seed<K: de::DeserializeSeed<'de>>( + &mut self, + seed: K, + ) -> Result<Option<K::Value>> { + Ok(match self.keys.get(self.pos) { + Some(key) => { + let mut deserializer = Deserializer::new(self.scope, *key, None); + Some(seed.deserialize(&mut deserializer)?) + } + None => None, + }) + } + + fn next_value_seed<V: de::DeserializeSeed<'de>>( + &mut self, + seed: V, + ) -> Result<V::Value> { + if self.pos >= self.keys.len() { + return Err(Error::LengthMismatch); + } + let key = self.keys[self.pos]; + self.pos += 1; + let v8_val = self.obj.get(self.scope, key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + seed.deserialize(&mut deserializer) + } + + fn next_entry_seed< + K: de::DeserializeSeed<'de>, + V: de::DeserializeSeed<'de>, + >( + &mut self, + kseed: K, + vseed: V, + ) -> Result<Option<(K::Value, V::Value)>> { + if self.pos >= self.keys.len() { + return Ok(None); + } + let v8_key = self.keys[self.pos]; + self.pos += 1; + let mut kdeserializer = Deserializer::new(self.scope, v8_key, None); + Ok(Some((kseed.deserialize(&mut kdeserializer)?, { + let v8_val = self.obj.get(self.scope, v8_key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + vseed.deserialize(&mut deserializer)? + }))) + } +} + +struct ObjectAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + fields: &'static [&'static str], + pos: usize, + _cache: Option<&'b mut KeyCache>, +} + +fn str_deserializer(s: &str) -> de::value::StrDeserializer<Error> { + de::IntoDeserializer::into_deserializer(s) +} + +impl<'de, 'a, 'b, 's> de::MapAccess<'de> for ObjectAccess<'a, 'b, 's> { + type Error = Error; + + fn next_key_seed<K: de::DeserializeSeed<'de>>( + &mut self, + seed: K, + ) -> Result<Option<K::Value>> { + Ok(match self.fields.get(self.pos) { + Some(&field) => Some(seed.deserialize(str_deserializer(field))?), + None => None, + }) + } + + fn next_value_seed<V: de::DeserializeSeed<'de>>( + &mut self, + seed: V, + ) -> Result<V::Value> { + if self.pos >= self.fields.len() { + return Err(Error::LengthMismatch); + } + let field = self.fields[self.pos]; + self.pos += 1; + let key = v8_struct_key(self.scope, field).into(); + let v8_val = self.obj.get(self.scope, key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + seed.deserialize(&mut deserializer) + } + + fn next_entry_seed< + K: de::DeserializeSeed<'de>, + V: de::DeserializeSeed<'de>, + >( + &mut self, + kseed: K, + vseed: V, + ) -> Result<Option<(K::Value, V::Value)>> { + if self.pos >= self.fields.len() { + return Ok(None); + } + let field = self.fields[self.pos]; + self.pos += 1; + Ok(Some((kseed.deserialize(str_deserializer(field))?, { + let key = v8_struct_key(self.scope, field).into(); + let v8_val = self.obj.get(self.scope, key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + vseed.deserialize(&mut deserializer)? + }))) + } +} + +struct SeqAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + len: u32, + pos: u32, +} + +impl<'de> de::SeqAccess<'de> for SeqAccess<'_, '_, '_> { + type Error = Error; + + fn next_element_seed<T: de::DeserializeSeed<'de>>( + &mut self, + seed: T, + ) -> Result<Option<T::Value>> { + let pos = self.pos; + self.pos += 1; + + if pos < self.len { + let val = self.obj.get_index(self.scope, pos).unwrap(); + let mut deserializer = Deserializer::new(self.scope, val, None); + Ok(Some(seed.deserialize(&mut deserializer)?)) + } else { + Ok(None) + } + } +} diff --git a/serde_v8/src/error.rs b/serde_v8/src/error.rs new file mode 100644 index 000000000..effec4138 --- /dev/null +++ b/serde_v8/src/error.rs @@ -0,0 +1,43 @@ +use std::fmt::{self, Display}; + +use serde::{de, ser}; + +pub type Result<T> = std::result::Result<T, Error>; + +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + Message(String), + + ExpectedBoolean, + ExpectedInteger, + ExpectedString, + ExpectedNull, + ExpectedArray, + ExpectedMap, + ExpectedEnum, + + LengthMismatch, +} + +impl ser::Error for Error { + fn custom<T: Display>(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl de::Error for Error { + fn custom<T: Display>(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Message(msg) => formatter.write_str(msg), + err => formatter.write_str(format!("serde_v8 error: {:?}", err).as_ref()), + } + } +} + +impl std::error::Error for Error {} diff --git a/serde_v8/src/keys.rs b/serde_v8/src/keys.rs new file mode 100644 index 000000000..3e17e1c52 --- /dev/null +++ b/serde_v8/src/keys.rs @@ -0,0 +1,32 @@ +use rusty_v8 as v8; + +use std::collections::HashMap; + +// KeyCache stores a pool struct keys mapped to v8, +// to minimize allocs and speed up decoding/encoding `v8::Object`s +// TODO: experiment with in from_v8/to_v8 +pub struct KeyCache(HashMap<&'static str, v8::Global<v8::String>>); + +// creates an optimized v8::String for a struct field +// TODO: experiment with external strings +// TODO: evaluate if own KeyCache is better than v8's dedupe +pub fn v8_struct_key<'s>( + scope: &mut v8::HandleScope<'s>, + field: &'static str, +) -> v8::Local<'s, v8::String> { + // Internalized v8 strings are significantly faster than "normal" v8 strings + // since v8 deduplicates re-used strings minimizing new allocations + // see: https://github.com/v8/v8/blob/14ac92e02cc3db38131a57e75e2392529f405f2f/include/v8.h#L3165-L3171 + v8::String::new_from_utf8( + scope, + field.as_ref(), + v8::NewStringType::Internalized, + ) + .unwrap() + + // TODO: consider external strings later + // right now non-deduped external strings (without KeyCache) + // are slower than the deduped internalized strings by ~2.5x + // since they're a new string in v8's eyes and needs to be hashed, etc... + // v8::String::new_external_onebyte_static(scope, field).unwrap() +} diff --git a/serde_v8/src/lib.rs b/serde_v8/src/lib.rs new file mode 100644 index 000000000..cd20d6d17 --- /dev/null +++ b/serde_v8/src/lib.rs @@ -0,0 +1,13 @@ +mod de; +mod error; +mod keys; +mod magic; +mod payload; +mod ser; +pub mod utils; + +pub use de::{from_v8, from_v8_cached, Deserializer}; +pub use error::{Error, Result}; +pub use keys::KeyCache; +pub use magic::Value; +pub use ser::{to_v8, Serializer}; diff --git a/serde_v8/src/magic.rs b/serde_v8/src/magic.rs new file mode 100644 index 000000000..8e6b2dfe5 --- /dev/null +++ b/serde_v8/src/magic.rs @@ -0,0 +1,78 @@ +use rusty_v8 as v8; + +use std::fmt; +use std::marker::PhantomData; + +pub const FIELD: &str = "$__v8_magic_value"; +pub const NAME: &str = "$__v8_magic_Value"; + +/// serde_v8::Value allows passing through `v8::Value`s untouched +/// when encoding/decoding and allows mixing rust & v8 values in +/// structs, tuples... +/// The implementation mainly breaks down to: +/// 1. Transmuting between u64 <> serde_v8::Value +/// 2. Using special struct/field names to detect these values +/// 3. Then serde "boilerplate" +pub struct Value<'s> { + pub v8_value: v8::Local<'s, v8::Value>, +} + +impl<'s> From<v8::Local<'s, v8::Value>> for Value<'s> { + fn from(v8_value: v8::Local<'s, v8::Value>) -> Self { + Self { v8_value } + } +} + +impl<'s> From<Value<'s>> for v8::Local<'s, v8::Value> { + fn from(v: Value<'s>) -> Self { + v.v8_value + } +} + +impl serde::Serialize for Value<'_> { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + let mut s = serializer.serialize_struct(NAME, 1)?; + let mv = Value { + v8_value: self.v8_value, + }; + let hack: u64 = unsafe { std::mem::transmute(mv) }; + s.serialize_field(FIELD, &hack)?; + s.end() + } +} + +impl<'de, 's> serde::Deserialize<'de> for Value<'s> { + fn deserialize<D>(deserializer: D) -> Result<Value<'s>, D::Error> + where + D: serde::Deserializer<'de>, + { + struct ValueVisitor<'s> { + p1: PhantomData<&'s ()>, + } + + impl<'de, 's> serde::de::Visitor<'de> for ValueVisitor<'s> { + type Value = Value<'s>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a v8::Value") + } + + fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + let mv: Value<'s> = unsafe { std::mem::transmute(v) }; + Ok(mv) + } + } + + static FIELDS: [&str; 1] = [FIELD]; + let visitor = ValueVisitor { p1: PhantomData }; + deserializer.deserialize_struct(NAME, &FIELDS, visitor) + } +} diff --git a/serde_v8/src/payload.rs b/serde_v8/src/payload.rs new file mode 100644 index 000000000..63562ac34 --- /dev/null +++ b/serde_v8/src/payload.rs @@ -0,0 +1,33 @@ +use rusty_v8 as v8; + +// TODO: maybe add a Payload type that holds scope & v8::Value +// so it can implement Deserialize by itself + +// Classifies v8::Values into sub-types +pub enum ValueType { + Null, + Bool, + Number, + String, + Array, + Object, +} + +impl ValueType { + pub fn from_v8(v: v8::Local<v8::Value>) -> ValueType { + if v.is_boolean() { + return Self::Bool; + } else if v.is_number() { + return Self::Number; + } else if v.is_string() { + return Self::String; + } else if v.is_array() { + return Self::Array; + } else if v.is_object() { + return Self::Object; + } else if v.is_null_or_undefined() { + return Self::Null; + } + panic!("serde_v8: unknown ValueType for v8::Value") + } +} diff --git a/serde_v8/src/ser.rs b/serde_v8/src/ser.rs new file mode 100644 index 000000000..3970ddaab --- /dev/null +++ b/serde_v8/src/ser.rs @@ -0,0 +1,632 @@ +use rusty_v8 as v8; +use serde::ser; +use serde::ser::{Impossible, Serialize}; + +use std::cell::RefCell; +use std::rc::Rc; + +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> = Rc<RefCell<v8::EscapableHandleScope<'a, 'b>>>; + +pub fn to_v8<'a, T>(scope: &mut v8::HandleScope<'a>, input: T) -> JsResult<'a> +where + T: Serialize, +{ + let subscope = v8::EscapableHandleScope::new(scope); + let scopeptr = Rc::new(RefCell::new(subscope)); + let serializer = Serializer::new(scopeptr.clone()); + let x = input.serialize(serializer)?; + let x = scopeptr.clone().borrow_mut().escape(x); + + Ok(x) +} + +/// Wraps other serializers into an enum tagged variant form. +/// Uses {"Variant": ...payload...} for compatibility with serde-json. +pub struct VariantSerializer<'a, 'b, S> { + variant: &'static str, + inner: S, + scope: ScopePtr<'a, 'b>, +} + +impl<'a, 'b, S> VariantSerializer<'a, 'b, S> { + pub fn new(scope: ScopePtr<'a, 'b>, variant: &'static str, inner: S) -> Self { + Self { + scope, + variant, + inner, + } + } + + fn end(self, inner: impl FnOnce(S) -> JsResult<'a>) -> JsResult<'a> { + let value = inner(self.inner)?; + let scope = &mut *self.scope.borrow_mut(); + let obj = v8::Object::new(scope); + let key = v8_struct_key(scope, self.variant).into(); + obj.set(scope, key, value); + Ok(obj.into()) + } +} + +impl<'a, 'b, S> ser::SerializeTupleVariant for VariantSerializer<'a, 'b, 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, S> ser::SerializeStructVariant for VariantSerializer<'a, 'b, 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> { + // serializer: Serializer<'a, 'b>, + pending: Vec<JsValue<'a>>, + scope: ScopePtr<'a, 'b>, +} + +impl<'a, 'b> ArraySerializer<'a, 'b> { + pub fn new(scope: ScopePtr<'a, 'b>) -> Self { + // let serializer = Serializer::new(scope); + Self { + scope, + // serializer, + pending: vec![], + } + } +} + +impl<'a, 'b> ser::SerializeSeq for ArraySerializer<'a, 'b> { + 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.clone()))?; + 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> ser::SerializeTuple for ArraySerializer<'a, 'b> { + 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> ser::SerializeTupleStruct for ArraySerializer<'a, 'b> { + 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> { + scope: ScopePtr<'a, 'b>, + obj: v8::Local<'a, v8::Object>, +} + +impl<'a, 'b> ObjectSerializer<'a, 'b> { + pub fn new(scope: ScopePtr<'a, 'b>) -> Self { + let obj = v8::Object::new(&mut *scope.borrow_mut()); + Self { scope, obj } + } +} + +impl<'a, 'b> ser::SerializeStruct for ObjectSerializer<'a, 'b> { + 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.clone()))?; + let scope = &mut *self.scope.borrow_mut(); + let key = v8_struct_key(scope, key).into(); + self.obj.set(scope, key, value); + Ok(()) + } + + fn end(self) -> JsResult<'a> { + Ok(self.obj.into()) + } +} + +pub struct MagicSerializer<'a, 'b> { + scope: ScopePtr<'a, 'b>, + v8_value: Option<v8::Local<'a, v8::Value>>, +} + +impl<'a, 'b> ser::SerializeStruct for MagicSerializer<'a, 'b> { + 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 v8_value = value.serialize(MagicTransmuter { + _scope: self.scope.clone(), + })?; + self.v8_value = Some(v8_value); + Ok(()) + } + + fn end(self) -> JsResult<'a> { + Ok(self.v8_value.unwrap()) + } +} + +// Dispatches between magic and regular struct serializers +pub enum StructSerializers<'a, 'b> { + Magic(MagicSerializer<'a, 'b>), + Regular(ObjectSerializer<'a, 'b>), +} + +impl<'a, 'b> ser::SerializeStruct for StructSerializers<'a, 'b> { + 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::Regular(s) => s.serialize_field(key, value), + } + } + + fn end(self) -> JsResult<'a> { + match self { + StructSerializers::Magic(s) => s.end(), + StructSerializers::Regular(s) => s.end(), + } + } +} + +// Serializes to JS Objects, NOT JS Maps ... +pub struct MapSerializer<'a, 'b> { + scope: ScopePtr<'a, 'b>, + obj: v8::Local<'a, v8::Object>, + next_key: Option<JsValue<'a>>, +} + +impl<'a, 'b> MapSerializer<'a, 'b> { + pub fn new(scope: ScopePtr<'a, 'b>) -> Self { + let obj = v8::Object::new(&mut *scope.borrow_mut()); + Self { + scope, + obj, + next_key: None, + } + } +} + +impl<'a, 'b> ser::SerializeMap for MapSerializer<'a, 'b> { + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<()> { + debug_assert!(self.next_key.is_none()); + self.next_key = Some(key.serialize(Serializer::new(self.scope.clone()))?); + Ok(()) + } + + fn serialize_value<T: ?Sized + Serialize>( + &mut self, + value: &T, + ) -> Result<()> { + let v8_value = value.serialize(Serializer::new(self.scope.clone()))?; + let scope = &mut *self.scope.borrow_mut(); + self.obj.set(scope, self.next_key.take().unwrap(), v8_value); + Ok(()) + } + + fn end(self) -> JsResult<'a> { + debug_assert!(self.next_key.is_none()); + Ok(self.obj.into()) + } +} + +#[derive(Clone)] +pub struct Serializer<'a, 'b> { + scope: ScopePtr<'a, 'b>, +} + +impl<'a, 'b> Serializer<'a, 'b> { + pub fn new(scope: ScopePtr<'a, 'b>) -> 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> ser::Serializer for Serializer<'a, 'b> { + type Ok = v8::Local<'a, v8::Value>; + type Error = Error; + + type SerializeSeq = ArraySerializer<'a, 'b>; + type SerializeTuple = ArraySerializer<'a, 'b>; + type SerializeTupleStruct = ArraySerializer<'a, 'b>; + type SerializeTupleVariant = + VariantSerializer<'a, 'b, ArraySerializer<'a, 'b>>; + type SerializeMap = MapSerializer<'a, 'b>; + type SerializeStruct = StructSerializers<'a, 'b>; + type SerializeStructVariant = + VariantSerializer<'a, 'b, StructSerializers<'a, 'b>>; + + 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.clone(); + 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)) + } + + 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.clone(), + 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)) + } + + /// Serialises Rust typed structs into plain JS objects. + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result<Self::SerializeStruct> { + if name == magic::NAME { + let m = MagicSerializer { + scope: self.scope, + v8_value: None, + }; + return Ok(StructSerializers::Magic(m)); + } + let o = ObjectSerializer::new(self.scope); + 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.clone(); + let x = self.serialize_struct(variant, len)?; + Ok(VariantSerializer::new(scope, variant, x)) + } +} + +macro_rules! not_reachable { + ($($name:ident($ty:ty, $lt:lifetime);)*) => { + $(fn $name(self, _v: $ty) -> JsResult<$lt> { + unreachable!(); + })* + }; +} + +/// A VERY hackish serde::Serializer +/// that exists solely to transmute a u64 to a serde_v8::Value +struct MagicTransmuter<'a, 'b> { + _scope: ScopePtr<'a, 'b>, +} + +impl<'a, 'b> ser::Serializer for MagicTransmuter<'a, 'b> { + type Ok = v8::Local<'a, v8::Value>; + type Error = Error; + + type SerializeSeq = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeTuple = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeTupleStruct = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeTupleVariant = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeMap = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeStruct = Impossible<v8::Local<'a, v8::Value>, Error>; + type SerializeStructVariant = Impossible<v8::Local<'a, v8::Value>, Error>; + + // The only serialize method for this hackish struct + fn serialize_u64(self, v: u64) -> JsResult<'a> { + let mv: magic::Value = unsafe { std::mem::transmute(v) }; + Ok(mv.v8_value) + } + + not_reachable! { + serialize_i8(i8, 'a); + serialize_i16(i16, 'a); + serialize_i32(i32, 'a); + serialize_i64(i64, 'a); + serialize_u8(u8, 'a); + serialize_u16(u16, 'a); + serialize_u32(u32, 'a); + // serialize_u64(u64, 'a); the chosen one + serialize_f32(f32, 'a); + serialize_f64(f64, 'a); + serialize_bool(bool, 'a); + serialize_char(char, 'a); + serialize_str(&str, 'a); + serialize_bytes(&[u8], 'a); + } + + fn serialize_none(self) -> JsResult<'a> { + unreachable!(); + } + + fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> JsResult<'a> { + unreachable!(); + } + + fn serialize_unit(self) -> JsResult<'a> { + unreachable!(); + } + + fn serialize_unit_struct(self, _name: &'static str) -> JsResult<'a> { + unreachable!(); + } + + /// 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> { + unreachable!(); + } + + fn serialize_newtype_struct<T: ?Sized + Serialize>( + self, + _name: &'static str, + _value: &T, + ) -> JsResult<'a> { + unreachable!(); + } + + fn serialize_newtype_variant<T: ?Sized + Serialize>( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> JsResult<'a> { + unreachable!(); + } + fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> { + unreachable!(); + } + + fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> { + unreachable!(); + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result<Self::SerializeTupleStruct> { + unreachable!(); + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result<Self::SerializeTupleVariant> { + unreachable!(); + } + + fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> { + unreachable!(); + } + + /// Serialises Rust typed structs into plain JS objects. + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result<Self::SerializeStruct> { + unreachable!(); + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result<Self::SerializeStructVariant> { + unreachable!(); + } +} diff --git a/serde_v8/src/utils.rs b/serde_v8/src/utils.rs new file mode 100644 index 000000000..f9f81837c --- /dev/null +++ b/serde_v8/src/utils.rs @@ -0,0 +1,33 @@ +use rusty_v8 as v8; +use std::sync::Once; + +pub fn js_exec<'s>( + scope: &mut v8::HandleScope<'s>, + src: &str, +) -> v8::Local<'s, v8::Value> { + let code = v8::String::new(scope, src).unwrap(); + let script = v8::Script::compile(scope, code, None).unwrap(); + script.run(scope).unwrap() +} + +pub fn v8_init() { + let platform = v8::new_default_platform().unwrap(); + v8::V8::initialize_platform(platform); + v8::V8::initialize(); +} + +pub fn v8_shutdown() { + unsafe { + v8::V8::dispose(); + } + v8::V8::shutdown_platform(); +} + +pub fn v8_do(f: impl FnOnce()) { + static V8_INIT: Once = Once::new(); + V8_INIT.call_once(|| { + v8_init(); + }); + f(); + // v8_shutdown(); +} |