summaryrefslogtreecommitdiff
path: root/serde_v8/src
diff options
context:
space:
mode:
authorAaron O'Mullan <aaron.omullan@gmail.com>2021-10-20 00:00:45 +0200
committerGitHub <noreply@github.com>2021-10-20 00:00:45 +0200
commit4f48efcc55b9e6cc0dd212ebd8e729909efed1ab (patch)
tree78f06eff5e21bddc9c6be0a3fc809d2b4a5932cb /serde_v8/src
parentad20e52c27a88b8481b2d8e169bde4a9b4952cb6 (diff)
chore: return serde_v8 to main repo (#12500)
Reduces fragmentation, avoids version drift and facilitates coordinating serde_v8 and op-layer changes
Diffstat (limited to 'serde_v8/src')
-rw-r--r--serde_v8/src/de.rs672
-rw-r--r--serde_v8/src/error.rs47
-rw-r--r--serde_v8/src/keys.rs33
-rw-r--r--serde_v8/src/lib.rs18
-rw-r--r--serde_v8/src/magic/buffer.rs146
-rw-r--r--serde_v8/src/magic/bytestring.rs125
-rw-r--r--serde_v8/src/magic/field.rs144
-rw-r--r--serde_v8/src/magic/mod.rs9
-rw-r--r--serde_v8/src/magic/value.rs79
-rw-r--r--serde_v8/src/magic/zero_copy_buf.rs115
-rw-r--r--serde_v8/src/payload.rs34
-rw-r--r--serde_v8/src/ser.rs648
-rw-r--r--serde_v8/src/serializable.rs118
-rw-r--r--serde_v8/src/utils.rs34
14 files changed, 2222 insertions, 0 deletions
diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs
new file mode 100644
index 000000000..3668a4fd5
--- /dev/null
+++ b/serde_v8/src/de.rs
@@ -0,0 +1,672 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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!()
+ }
+ };
+}
+
+// TODO: maybe check for BigInt truncation ?
+// (i.e: values larger than i64/u64 can hold)
+macro_rules! deserialize_signed {
+ ($dmethod:ident, $vmethod:ident, $t:tt) => {
+ fn $dmethod<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ let value: $t = match self.input.is_big_int() {
+ true => {
+ let bigint = v8::Local::<v8::BigInt>::try_from(self.input);
+ bigint.unwrap().i64_value().0 as $t
+ }
+ false => self.input.integer_value(&mut self.scope).unwrap() as $t,
+ };
+ visitor.$vmethod(value)
+ }
+ };
+}
+
+macro_rules! deserialize_unsigned {
+ ($dmethod:ident, $vmethod:ident, $t:tt) => {
+ fn $dmethod<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ let value: $t = match self.input.is_big_int() {
+ true => {
+ let bigint = v8::Local::<v8::BigInt>::try_from(self.input);
+ bigint.unwrap().u64_value().0 as $t
+ }
+ false => self.input.integer_value(&mut self.scope).unwrap() as $t,
+ };
+ visitor.$vmethod(value)
+ }
+ };
+}
+
+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),
+ // Handle floats & ints separately to work with loosely-typed serde_json
+ ValueType::Number => {
+ if self.input.is_uint32() {
+ self.deserialize_u32(visitor)
+ } else if self.input.is_int32() {
+ self.deserialize_i32(visitor)
+ } else {
+ 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>,
+ {
+ // Relaxed typechecking, will map all non-true vals to false
+ visitor.visit_bool(self.input.is_true())
+ }
+
+ // signed
+ 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);
+ // unsigned
+ deserialize_unsigned!(deserialize_u8, visit_u8, u8);
+ deserialize_unsigned!(deserialize_u16, visit_u16, u16);
+ deserialize_unsigned!(deserialize_u32, visit_u32, u32);
+ deserialize_unsigned!(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 v8_string = v8::Local::<v8::String>::try_from(self.input).unwrap();
+ let string = v8_string.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)
+ .map_err(|_| Error::ExpectedArray)?;
+ 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)
+ .map_err(|_| Error::ExpectedObject)?;
+ 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())
+ // Filter keys to drop keys whose value is undefined
+ // TODO: optimize, since this doubles our get calls
+ .filter(|key| !obj.get(self.scope, *key).unwrap().is_undefined())
+ .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);
+ }
+
+ // Magic Buffer
+ if name == magic::buffer::BUF_NAME {
+ let zero_copy_buf =
+ v8::Local::<v8::ArrayBufferView>::try_from(self.input)
+ .and_then(|view| {
+ magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view)
+ })
+ .map_err(|_| Error::ExpectedArray)?;
+ let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) };
+ return visitor.visit_bytes(&data);
+ }
+
+ // Magic ByteString
+ if name == magic::bytestring::NAME {
+ if let Some(v8_string) = self.input.to_string(self.scope) {
+ if v8_string.contains_only_onebyte() {
+ let mut buffer: Vec<u8> = vec![0u8; v8_string.length()];
+ let written = v8_string.write_one_byte(
+ self.scope,
+ &mut buffer,
+ 0,
+ v8::WriteOptions::NO_NULL_TERMINATION,
+ );
+ assert!(written == v8_string.length());
+ return visitor.visit_byte_buf(buffer);
+ } else {
+ return Err(Error::Message(
+ "Expected a valid ByteString.".to_string(),
+ ));
+ }
+ } else {
+ return Err(Error::ExpectedString);
+ }
+ }
+
+ // Regular struct
+ let obj = v8::Local::<v8::Object>::try_from(self.input)
+ .map_err(|_| Error::ExpectedObject)?;
+ let map = ObjectAccess {
+ fields,
+ obj,
+ pos: 0,
+ scope: self.scope,
+ _cache: None,
+ };
+
+ visitor.visit_map(map)
+ }
+
+ /// To be compatible with `serde-json`, we expect enums to be:
+ /// - `"Variant"`: strings for unit variants, i.e: Enum::Variant
+ /// - `{ Variant: payload }`: single K/V pairs, converted to `Enum::Variant { payload }`
+ fn deserialize_enum<V>(
+ self,
+ _name: &str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value>
+ where
+ V: Visitor<'de>,
+ {
+ // Unit variant
+ if self.input.is_string() {
+ let payload = v8::undefined(self.scope).into();
+ visitor.visit_enum(EnumAccess {
+ scope: self.scope,
+ tag: self.input,
+ payload,
+ })
+ }
+ // Struct or tuple variant
+ else if self.input.is_object() {
+ // Assume object
+ let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap();
+ // Unpack single-key
+ let tag = {
+ let prop_names = obj.get_own_property_names(self.scope);
+ let prop_names = prop_names.ok_or(Error::ExpectedEnum)?;
+ if prop_names.length() != 1 {
+ return Err(Error::LengthMismatch);
+ }
+ prop_names.get_index(self.scope, 0).unwrap()
+ };
+
+ let payload = obj.get(self.scope, tag).unwrap();
+ visitor.visit_enum(EnumAccess {
+ scope: self.scope,
+ tag,
+ payload,
+ })
+ } else {
+ // TODO: improve error
+ Err(Error::ExpectedEnum)
+ }
+ }
+
+ // 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)
+ }
+ }
+
+ fn size_hint(&self) -> Option<usize> {
+ Some((self.len - self.pos) as usize)
+ }
+}
+
+struct EnumAccess<'a, 'b, 's> {
+ tag: v8::Local<'a, v8::Value>,
+ payload: v8::Local<'a, v8::Value>,
+ scope: &'b mut v8::HandleScope<'s>,
+ // p1: std::marker::PhantomData<&'x ()>,
+}
+
+impl<'de, 'a, 'b, 's, 'x> de::EnumAccess<'de> for EnumAccess<'a, 'b, 's> {
+ type Error = Error;
+ type Variant = VariantDeserializer<'a, 'b, 's>;
+
+ fn variant_seed<V: de::DeserializeSeed<'de>>(
+ self,
+ seed: V,
+ ) -> Result<(V::Value, Self::Variant)> {
+ let seed = {
+ let mut dtag = Deserializer::new(self.scope, self.tag, None);
+ seed.deserialize(&mut dtag)
+ };
+ let dpayload = VariantDeserializer::<'a, 'b, 's> {
+ scope: self.scope,
+ value: self.payload,
+ };
+
+ Ok((seed?, dpayload))
+ }
+}
+
+struct VariantDeserializer<'a, 'b, 's> {
+ value: v8::Local<'a, v8::Value>,
+ scope: &'b mut v8::HandleScope<'s>,
+}
+
+impl<'de, 'a, 'b, 's> de::VariantAccess<'de>
+ for VariantDeserializer<'a, 'b, 's>
+{
+ type Error = Error;
+
+ fn unit_variant(self) -> Result<()> {
+ let mut d = Deserializer::new(self.scope, self.value, None);
+ de::Deserialize::deserialize(&mut d)
+ }
+
+ fn newtype_variant_seed<T: de::DeserializeSeed<'de>>(
+ self,
+ seed: T,
+ ) -> Result<T::Value> {
+ let mut d = Deserializer::new(self.scope, self.value, None);
+ seed.deserialize(&mut d)
+ }
+
+ fn tuple_variant<V: de::Visitor<'de>>(
+ self,
+ len: usize,
+ visitor: V,
+ ) -> Result<V::Value> {
+ let mut d = Deserializer::new(self.scope, self.value, None);
+ de::Deserializer::deserialize_tuple(&mut d, len, visitor)
+ }
+
+ fn struct_variant<V: de::Visitor<'de>>(
+ self,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value> {
+ let mut d = Deserializer::new(self.scope, self.value, None);
+ de::Deserializer::deserialize_struct(&mut d, "", fields, visitor)
+ }
+}
diff --git a/serde_v8/src/error.rs b/serde_v8/src/error.rs
new file mode 100644
index 000000000..39625da13
--- /dev/null
+++ b/serde_v8/src/error.rs
@@ -0,0 +1,47 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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,
+ ExpectedObject,
+
+ ExpectedUtf8,
+
+ 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..ea989086c
--- /dev/null
+++ b/serde_v8/src/keys.rs
@@ -0,0 +1,33 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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..374a27ce2
--- /dev/null
+++ b/serde_v8/src/lib.rs
@@ -0,0 +1,18 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+mod de;
+mod error;
+mod keys;
+mod magic;
+mod payload;
+mod ser;
+mod serializable;
+pub mod utils;
+
+pub use de::{from_v8, from_v8_cached, Deserializer};
+pub use error::{Error, Result};
+pub use keys::KeyCache;
+pub use magic::buffer::MagicBuffer as Buffer;
+pub use magic::bytestring::ByteString;
+pub use magic::Value;
+pub use ser::{to_v8, Serializer};
+pub use serializable::{Serializable, SerializablePkg};
diff --git a/serde_v8/src/magic/buffer.rs b/serde_v8/src/magic/buffer.rs
new file mode 100644
index 000000000..ef1f395fe
--- /dev/null
+++ b/serde_v8/src/magic/buffer.rs
@@ -0,0 +1,146 @@
+use rusty_v8 as v8;
+
+use std::fmt;
+use std::ops::Deref;
+use std::ops::DerefMut;
+use std::sync::Mutex;
+
+use super::zero_copy_buf::ZeroCopyBuf;
+
+// An asymmetric wrapper around ZeroCopyBuf,
+// allowing us to use a single type for familiarity
+pub enum MagicBuffer {
+ FromV8(ZeroCopyBuf),
+ ToV8(Mutex<Option<Box<[u8]>>>),
+}
+
+impl MagicBuffer {
+ pub fn new<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ view: v8::Local<v8::ArrayBufferView>,
+ ) -> Self {
+ Self::try_new(scope, view).unwrap()
+ }
+
+ pub fn try_new<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ view: v8::Local<v8::ArrayBufferView>,
+ ) -> Result<Self, v8::DataError> {
+ Ok(Self::FromV8(ZeroCopyBuf::try_new(scope, view)?))
+ }
+
+ pub fn empty() -> Self {
+ MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
+ }
+}
+
+impl Clone for MagicBuffer {
+ fn clone(&self) -> Self {
+ match self {
+ Self::FromV8(zbuf) => Self::FromV8(zbuf.clone()),
+ Self::ToV8(_) => panic!("Don't Clone a MagicBuffer sent to v8"),
+ }
+ }
+}
+
+impl AsRef<[u8]> for MagicBuffer {
+ fn as_ref(&self) -> &[u8] {
+ &*self
+ }
+}
+
+impl AsMut<[u8]> for MagicBuffer {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut *self
+ }
+}
+
+impl Deref for MagicBuffer {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ match self {
+ Self::FromV8(buf) => &*buf,
+ Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"),
+ }
+ }
+}
+
+impl DerefMut for MagicBuffer {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ match self {
+ Self::FromV8(buf) => &mut *buf,
+ Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"),
+ }
+ }
+}
+
+impl From<Box<[u8]>> for MagicBuffer {
+ fn from(buf: Box<[u8]>) -> Self {
+ MagicBuffer::ToV8(Mutex::new(Some(buf)))
+ }
+}
+
+impl From<Vec<u8>> for MagicBuffer {
+ fn from(vec: Vec<u8>) -> Self {
+ vec.into_boxed_slice().into()
+ }
+}
+
+pub const BUF_NAME: &str = "$__v8_magic_Buffer";
+pub const BUF_FIELD_1: &str = "$__v8_magic_buffer_1";
+pub const BUF_FIELD_2: &str = "$__v8_magic_buffer_2";
+
+impl serde::Serialize for MagicBuffer {
+ 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(BUF_NAME, 1)?;
+ let boxed: Box<[u8]> = match self {
+ Self::FromV8(buf) => {
+ let value: &[u8] = buf;
+ value.into()
+ }
+ Self::ToV8(x) => x.lock().unwrap().take().expect("MagicBuffer was empty"),
+ };
+ let hack: [usize; 2] = unsafe { std::mem::transmute(boxed) };
+ let f1: u64 = hack[0] as u64;
+ let f2: u64 = hack[1] as u64;
+ s.serialize_field(BUF_FIELD_1, &f1)?;
+ s.serialize_field(BUF_FIELD_2, &f2)?;
+ s.end()
+ }
+}
+
+impl<'de, 's> serde::Deserialize<'de> for MagicBuffer {
+ fn deserialize<D>(deserializer: D) -> Result<MagicBuffer, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct ValueVisitor {}
+
+ impl<'de> serde::de::Visitor<'de> for ValueVisitor {
+ type Value = MagicBuffer;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("a serde_v8::MagicBuffer")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ let p1: &[usize] = unsafe { &*(v as *const [u8] as *const [usize]) };
+ let p2: [usize; 4] = [p1[0], p1[1], p1[2], p1[3]];
+ let zero_copy: ZeroCopyBuf = unsafe { std::mem::transmute(p2) };
+ Ok(MagicBuffer::FromV8(zero_copy))
+ }
+ }
+
+ static FIELDS: [&str; 0] = [];
+ let visitor = ValueVisitor {};
+ deserializer.deserialize_struct(BUF_NAME, &FIELDS, visitor)
+ }
+}
diff --git a/serde_v8/src/magic/bytestring.rs b/serde_v8/src/magic/bytestring.rs
new file mode 100644
index 000000000..e90b7528e
--- /dev/null
+++ b/serde_v8/src/magic/bytestring.rs
@@ -0,0 +1,125 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use std::ops::{Deref, DerefMut};
+
+use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
+
+pub const NAME: &str = "$__v8_magic_bytestring";
+pub const FIELD_PTR: &str = "$__v8_magic_bytestring_ptr";
+pub const FIELD_LEN: &str = "$__v8_magic_bytestring_len";
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct ByteString(pub Vec<u8>);
+
+impl ByteString {
+ pub fn new() -> ByteString {
+ ByteString(Vec::new())
+ }
+
+ pub fn with_capacity(capacity: usize) -> ByteString {
+ ByteString(Vec::with_capacity(capacity))
+ }
+
+ pub fn capacity(&self) -> usize {
+ self.0.capacity()
+ }
+
+ pub fn reserve(&mut self, additional: usize) {
+ self.0.reserve(additional)
+ }
+
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.0.reserve_exact(additional)
+ }
+
+ pub fn shrink_to_fit(&mut self) {
+ self.0.shrink_to_fit()
+ }
+
+ pub fn truncate(&mut self, len: usize) {
+ self.0.truncate(len)
+ }
+
+ pub fn push(&mut self, value: u8) {
+ self.0.push(value)
+ }
+
+ pub fn pop(&mut self) -> Option<u8> {
+ self.0.pop()
+ }
+}
+
+impl Default for ByteString {
+ fn default() -> Self {
+ ByteString::new()
+ }
+}
+
+impl Deref for ByteString {
+ type Target = [u8];
+
+ fn deref(&self) -> &[u8] {
+ self.0.deref()
+ }
+}
+
+impl DerefMut for ByteString {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ self.0.deref_mut()
+ }
+}
+
+impl AsRef<[u8]> for ByteString {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_ref()
+ }
+}
+
+impl AsMut<[u8]> for ByteString {
+ fn as_mut(&mut self) -> &mut [u8] {
+ self.0.as_mut()
+ }
+}
+
+impl Serialize for ByteString {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ use serde::ser::SerializeStruct;
+
+ let mut s = serializer.serialize_struct(NAME, 1)?;
+ s.serialize_field(FIELD_PTR, &(self.0.as_ptr() as usize))?;
+ s.serialize_field(FIELD_LEN, &self.0.len())?;
+ s.end()
+ }
+}
+
+impl<'de> Deserialize<'de> for ByteString {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct ValueVisitor {}
+
+ impl<'de> Visitor<'de> for ValueVisitor {
+ type Value = ByteString;
+
+ fn expecting(
+ &self,
+ formatter: &mut std::fmt::Formatter,
+ ) -> std::fmt::Result {
+ formatter.write_str("a serde_v8::ByteString")
+ }
+
+ fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(ByteString(v))
+ }
+ }
+
+ deserializer.deserialize_struct(NAME, &[], ValueVisitor {})
+ }
+}
diff --git a/serde_v8/src/magic/field.rs b/serde_v8/src/magic/field.rs
new file mode 100644
index 000000000..e6bb9ee54
--- /dev/null
+++ b/serde_v8/src/magic/field.rs
@@ -0,0 +1,144 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use crate::error::{Error, Result};
+use serde::ser::{Impossible, Serialize, Serializer};
+
+/// All serde_v8 "magic" values are reduced to structs with 1 or 2 u64 fields
+/// assuming usize==u64, most types are simply a pointer or pointer+len (e.g: Box<T>)
+pub type TransmutedField = u64;
+pub type FieldResult = Result<TransmutedField>;
+
+macro_rules! not_reachable {
+ ($($name:ident($ty:ty);)*) => {
+ $(fn $name(self, _v: $ty) -> FieldResult {
+ unreachable!();
+ })*
+ };
+}
+
+/// FieldSerializer is a simple serde::Serializer that only returns u64s
+/// it allows the "magic" struct serializers to obtain the transmuted field values
+pub struct FieldSerializer {}
+
+impl Serializer for FieldSerializer {
+ type Ok = TransmutedField;
+ type Error = Error;
+
+ type SerializeSeq = Impossible<TransmutedField, Error>;
+ type SerializeTuple = Impossible<TransmutedField, Error>;
+ type SerializeTupleStruct = Impossible<TransmutedField, Error>;
+ type SerializeTupleVariant = Impossible<TransmutedField, Error>;
+ type SerializeMap = Impossible<TransmutedField, Error>;
+ type SerializeStruct = Impossible<TransmutedField, Error>;
+ type SerializeStructVariant = Impossible<TransmutedField, Error>;
+
+ fn serialize_u64(self, transmuted_field: u64) -> FieldResult {
+ Ok(transmuted_field)
+ }
+
+ not_reachable! {
+ serialize_i8(i8);
+ serialize_i16(i16);
+ serialize_i32(i32);
+ serialize_i64(i64);
+ serialize_u8(u8);
+ serialize_u16(u16);
+ serialize_u32(u32);
+ // serialize_u64(TransmutedField); the chosen one
+ serialize_f32(f32);
+ serialize_f64(f64);
+ serialize_bool(bool);
+ serialize_char(char);
+ serialize_str(&str);
+ serialize_bytes(&[u8]);
+ }
+
+ fn serialize_none(self) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_unit(self) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_unit_struct(self, _name: &'static str) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ ) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_newtype_struct<T: ?Sized + Serialize>(
+ self,
+ _name: &'static str,
+ _value: &T,
+ ) -> FieldResult {
+ unreachable!();
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + Serialize>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _value: &T,
+ ) -> FieldResult {
+ 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!();
+ }
+
+ 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/magic/mod.rs b/serde_v8/src/magic/mod.rs
new file mode 100644
index 000000000..00c06fd4c
--- /dev/null
+++ b/serde_v8/src/magic/mod.rs
@@ -0,0 +1,9 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+pub mod buffer;
+pub mod bytestring;
+mod field;
+mod value;
+pub mod zero_copy_buf;
+
+pub use field::FieldSerializer;
+pub use value::{Value, FIELD, NAME};
diff --git a/serde_v8/src/magic/value.rs b/serde_v8/src/magic/value.rs
new file mode 100644
index 000000000..2cb622466
--- /dev/null
+++ b/serde_v8/src/magic/value.rs
@@ -0,0 +1,79 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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/magic/zero_copy_buf.rs b/serde_v8/src/magic/zero_copy_buf.rs
new file mode 100644
index 000000000..1e911c329
--- /dev/null
+++ b/serde_v8/src/magic/zero_copy_buf.rs
@@ -0,0 +1,115 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use rusty_v8 as v8;
+use std::cell::Cell;
+use std::ops::Deref;
+use std::ops::DerefMut;
+
+/// A ZeroCopyBuf encapsulates a slice that's been borrowed from a JavaScript
+/// ArrayBuffer object. JavaScript objects can normally be garbage collected,
+/// but the existence of a ZeroCopyBuf inhibits this until it is dropped. It
+/// behaves much like an Arc<[u8]>.
+///
+/// # Cloning
+/// Cloning a ZeroCopyBuf does not clone the contents of the buffer,
+/// it creates a new reference to that buffer.
+///
+/// To actually clone the contents of the buffer do
+/// `let copy = Vec::from(&*zero_copy_buf);`
+#[derive(Clone)]
+pub struct ZeroCopyBuf {
+ backing_store: v8::SharedRef<v8::BackingStore>,
+ byte_offset: usize,
+ byte_length: usize,
+}
+
+unsafe impl Send for ZeroCopyBuf {}
+
+impl ZeroCopyBuf {
+ pub fn new<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ view: v8::Local<v8::ArrayBufferView>,
+ ) -> Self {
+ Self::try_new(scope, view).unwrap()
+ }
+
+ pub fn try_new<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ view: v8::Local<v8::ArrayBufferView>,
+ ) -> Result<Self, v8::DataError> {
+ let backing_store = view.buffer(scope).unwrap().get_backing_store();
+ if backing_store.is_shared() {
+ return Err(v8::DataError::BadType {
+ actual: "shared ArrayBufferView",
+ expected: "non-shared ArrayBufferView",
+ });
+ }
+ let byte_offset = view.byte_offset();
+ let byte_length = view.byte_length();
+ Ok(Self {
+ backing_store,
+ byte_offset,
+ byte_length,
+ })
+ }
+}
+
+impl Deref for ZeroCopyBuf {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ get_backing_store_slice(
+ &self.backing_store,
+ self.byte_offset,
+ self.byte_length,
+ )
+ }
+ }
+}
+
+impl DerefMut for ZeroCopyBuf {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ get_backing_store_slice_mut(
+ &self.backing_store,
+ self.byte_offset,
+ self.byte_length,
+ )
+ }
+ }
+}
+
+impl AsRef<[u8]> for ZeroCopyBuf {
+ fn as_ref(&self) -> &[u8] {
+ &*self
+ }
+}
+
+impl AsMut<[u8]> for ZeroCopyBuf {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut *self
+ }
+}
+
+unsafe fn get_backing_store_slice(
+ backing_store: &v8::SharedRef<v8::BackingStore>,
+ byte_offset: usize,
+ byte_length: usize,
+) -> &[u8] {
+ let cells: *const [Cell<u8>] =
+ &backing_store[byte_offset..byte_offset + byte_length];
+ let bytes = cells as *const [u8];
+ &*bytes
+}
+
+#[allow(clippy::mut_from_ref)]
+unsafe fn get_backing_store_slice_mut(
+ backing_store: &v8::SharedRef<v8::BackingStore>,
+ byte_offset: usize,
+ byte_length: usize,
+) -> &mut [u8] {
+ let cells: *const [Cell<u8>] =
+ &backing_store[byte_offset..byte_offset + byte_length];
+ let bytes = cells as *const _ as *mut [u8];
+ &mut *bytes
+}
diff --git a/serde_v8/src/payload.rs b/serde_v8/src/payload.rs
new file mode 100644
index 000000000..816158f93
--- /dev/null
+++ b/serde_v8/src/payload.rs
@@ -0,0 +1,34 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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..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")
+}
diff --git a/serde_v8/src/serializable.rs b/serde_v8/src/serializable.rs
new file mode 100644
index 000000000..c9182b636
--- /dev/null
+++ b/serde_v8/src/serializable.rs
@@ -0,0 +1,118 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use rusty_v8 as v8;
+use std::any::TypeId;
+use std::mem::transmute_copy;
+
+/// Serializable exists to allow boxing values as "objects" to be serialized later,
+/// this is particularly useful for async op-responses. This trait is a more efficient
+/// replacement for erased-serde that makes less allocations, since it's specific to serde_v8
+/// (and thus doesn't have to have generic outputs, etc...)
+pub trait Serializable {
+ fn to_v8<'a>(
+ &self,
+ scope: &mut v8::HandleScope<'a>,
+ ) -> Result<v8::Local<'a, v8::Value>, crate::Error>;
+}
+
+/// Allows all implementors of `serde::Serialize` to implement Serializable
+impl<T: serde::Serialize> Serializable for T {
+ fn to_v8<'a>(
+ &self,
+ scope: &mut v8::HandleScope<'a>,
+ ) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
+ crate::to_v8(scope, self)
+ }
+}
+
+/// SerializablePkg exists to provide a fast path for op returns,
+/// allowing them to avoid boxing primtives (ints/floats/bool/unit/...)
+pub enum SerializablePkg {
+ Primitive(Primitive),
+ Serializable(Box<dyn Serializable>),
+}
+
+impl SerializablePkg {
+ pub fn to_v8<'a>(
+ &self,
+ scope: &mut v8::HandleScope<'a>,
+ ) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
+ match &*self {
+ Self::Primitive(x) => crate::to_v8(scope, x),
+ Self::Serializable(x) => x.to_v8(scope),
+ }
+ }
+}
+
+/// Primitive serves as a lightweight serializable wrapper around primitives
+/// so that we can use them for async values
+#[derive(Clone, Copy)]
+pub enum Primitive {
+ Unit,
+ Bool(bool),
+ Int8(i8),
+ Int16(i16),
+ Int32(i32),
+ Int64(i64),
+ UInt8(u8),
+ UInt16(u16),
+ UInt32(u32),
+ UInt64(u64),
+ Float32(f32),
+ Float64(f64),
+}
+
+impl serde::Serialize for Primitive {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ match *self {
+ Self::Unit => serializer.serialize_unit(),
+ Self::Bool(x) => serializer.serialize_bool(x),
+ Self::Int8(x) => serializer.serialize_i8(x),
+ Self::Int16(x) => serializer.serialize_i16(x),
+ Self::Int32(x) => serializer.serialize_i32(x),
+ Self::Int64(x) => serializer.serialize_i64(x),
+ Self::UInt8(x) => serializer.serialize_u8(x),
+ Self::UInt16(x) => serializer.serialize_u16(x),
+ Self::UInt32(x) => serializer.serialize_u32(x),
+ Self::UInt64(x) => serializer.serialize_u64(x),
+ Self::Float32(x) => serializer.serialize_f32(x),
+ Self::Float64(x) => serializer.serialize_f64(x),
+ }
+ }
+}
+
+impl<T: serde::Serialize + 'static> From<T> for SerializablePkg {
+ fn from(x: T) -> Self {
+ let tid = TypeId::of::<T>();
+
+ if tid == TypeId::of::<()>() {
+ Self::Primitive(Primitive::Unit)
+ } else if tid == TypeId::of::<bool>() {
+ Self::Primitive(Primitive::Bool(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<i8>() {
+ Self::Primitive(Primitive::Int8(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<i16>() {
+ Self::Primitive(Primitive::Int16(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<i32>() {
+ Self::Primitive(Primitive::Int32(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<i64>() {
+ Self::Primitive(Primitive::Int64(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<u8>() {
+ Self::Primitive(Primitive::UInt8(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<u16>() {
+ Self::Primitive(Primitive::UInt16(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<u32>() {
+ Self::Primitive(Primitive::UInt32(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<u64>() {
+ Self::Primitive(Primitive::UInt64(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<f32>() {
+ Self::Primitive(Primitive::Float32(unsafe { transmute_copy(&x) }))
+ } else if tid == TypeId::of::<f64>() {
+ Self::Primitive(Primitive::Float64(unsafe { transmute_copy(&x) }))
+ } else {
+ Self::Serializable(Box::new(x))
+ }
+ }
+}
diff --git a/serde_v8/src/utils.rs b/serde_v8/src/utils.rs
new file mode 100644
index 000000000..6f638f7b3
--- /dev/null
+++ b/serde_v8/src/utils.rs
@@ -0,0 +1,34 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+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(0, false).make_shared();
+ 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();
+}