diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ba3785c55..2303ebb1c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,7 +236,7 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: 2-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} + key: 3-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} # In main branch, always creates fresh cache - name: Cache build output (main) @@ -252,7 +252,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: | - 2-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} + 3-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} # Restore cache from the latest 'main' branch build. - name: Cache build output (PR) @@ -268,7 +268,7 @@ jobs: !./target/*/*.tar.gz key: never_saved restore-keys: | - 2-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- + 3-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- # Don't save cache after building PRs or branches other than 'main'. - name: Skip save cache (PR) diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs index 318894c226..002b741cf3 100644 --- a/serde_v8/src/de.rs +++ b/serde_v8/src/de.rs @@ -4,9 +4,10 @@ use serde::Deserialize; use crate::error::{Error, Result}; use crate::keys::{v8_struct_key, KeyCache}; +use crate::magic::transl8::FromV8; +use crate::magic::transl8::{visit_magic, MagicType}; use crate::payload::ValueType; - -use crate::magic; +use crate::{magic, Buffer, ByteString, U16String}; pub struct Deserializer<'a, 'b, 's> { input: v8::Local<'a, v8::Value>, @@ -131,23 +132,8 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> ValueType::Object => self.deserialize_map(visitor), // Map to Vec when deserialized via deserialize_any // e.g: for untagged enums or StringOrBuffer - ValueType::ArrayBufferView => { - v8::Local::::try_from(self.input) - .and_then(|view| { - magic::zero_copy_buf::ZeroCopyBuf::try_from(( - &mut *self.scope, - view, - )) - }) - .map_err(|_| Error::ExpectedBuffer) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } - ValueType::ArrayBuffer => { - v8::Local::::try_from(self.input) - .and_then(|buffer| { - magic::zero_copy_buf::ZeroCopyBuf::try_from(buffer) - }) - .map_err(|_| Error::ExpectedBuffer) + ValueType::ArrayBufferView | ValueType::ArrayBuffer => { + magic::zero_copy_buf::ZeroCopyBuf::from_v8(&mut *self.scope, self.input) .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) } } @@ -338,71 +324,31 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> 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 = match self.input.is_array_buffer() { - // ArrayBuffer - true => v8::Local::::try_from(self.input) - .and_then(magic::zero_copy_buf::ZeroCopyBuf::try_from), - // maybe ArrayBufferView - false => v8::Local::::try_from(self.input) - .and_then(|view| { - magic::zero_copy_buf::ZeroCopyBuf::try_from(( - &mut *self.scope, - view, - )) - }), + match name { + Buffer::MAGIC_NAME => { + visit_magic(visitor, Buffer::from_v8(self.scope, self.input)?) } - .map_err(|_| Error::ExpectedBuffer)?; - let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) }; - return visitor.visit_bytes(&data); - } - - // Magic ByteString - if name == magic::bytestring::NAME { - let v8str = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedString)?; - if !v8str.contains_only_onebyte() { - return Err(Error::ExpectedLatin1); + ByteString::MAGIC_NAME => { + visit_magic(visitor, ByteString::from_v8(self.scope, self.input)?) } - let len = v8str.length(); - let mut buffer = Vec::with_capacity(len); - #[allow(clippy::uninit_vec)] - unsafe { - buffer.set_len(len); + U16String::MAGIC_NAME => { + visit_magic(visitor, U16String::from_v8(self.scope, self.input)?) + } + magic::Value::MAGIC_NAME => { + visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) + } + _ => { + // Regular struct + let obj = self.input.try_into().or(Err(Error::ExpectedObject))?; + visitor.visit_seq(StructAccess { + fields, + obj, + pos: 0, + scope: self.scope, + _cache: None, + }) } - let written = v8str.write_one_byte( - self.scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - return visitor.visit_byte_buf(buffer); } - - // Regular struct - let obj = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedObject)?; - let struct_access = StructAccess { - fields, - obj, - pos: 0, - scope: self.scope, - _cache: None, - }; - - visitor.visit_seq(struct_access) } /// To be compatible with `serde-json`, we expect enums to be: diff --git a/serde_v8/src/magic/buffer.rs b/serde_v8/src/magic/buffer.rs index e6f85324e3..484984ac5f 100644 --- a/serde_v8/src/magic/buffer.rs +++ b/serde_v8/src/magic/buffer.rs @@ -1,12 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use std::convert::TryFrom; -use std::fmt; use std::ops::Deref; use std::ops::DerefMut; use std::sync::Mutex; +use super::transl8::FromV8; +use super::transl8::ToV8; use super::zero_copy_buf::ZeroCopyBuf; +use crate::magic::transl8::impl_magic; // An asymmetric wrapper around ZeroCopyBuf, // allowing us to use a single type for familiarity @@ -14,6 +15,7 @@ pub enum MagicBuffer { FromV8(ZeroCopyBuf), ToV8(Mutex>>), } +impl_magic!(MagicBuffer); impl MagicBuffer { pub fn empty() -> Self { @@ -21,27 +23,6 @@ impl MagicBuffer { } } -impl<'s> TryFrom> for MagicBuffer { - type Error = v8::DataError; - fn try_from(buffer: v8::Local) -> Result { - Ok(Self::FromV8(ZeroCopyBuf::try_from(buffer)?)) - } -} - -// TODO(@AaronO): consider streamlining this as "ScopedValue" ? -type ScopedView<'a, 'b, 's> = ( - &'s mut v8::HandleScope<'a>, - v8::Local<'b, v8::ArrayBufferView>, -); -impl<'a, 'b, 's> TryFrom> for MagicBuffer { - type Error = v8::DataError; - fn try_from( - scoped_view: ScopedView<'a, 'b, 's>, - ) -> Result { - Ok(Self::FromV8(ZeroCopyBuf::try_from(scoped_view)?)) - } -} - impl Clone for MagicBuffer { fn clone(&self) -> Self { match self { @@ -94,61 +75,45 @@ impl From> for MagicBuffer { } } -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(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - - let mut s = serializer.serialize_struct(BUF_NAME, 1)?; - let boxed: Box<[u8]> = match self { +impl ToV8 for MagicBuffer { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let buf: 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(deserializer: D) -> Result - 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(self, v: &[u8]) -> Result - 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)) - } + if buf.is_empty() { + let ab = v8::ArrayBuffer::new(scope, 0); + return Ok( + v8::Uint8Array::new(scope, ab, 0, 0) + .expect("Failed to create Uint8Array") + .into(), + ); } - - static FIELDS: [&str; 0] = []; - let visitor = ValueVisitor {}; - deserializer.deserialize_struct(BUF_NAME, &FIELDS, visitor) + 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); + Ok( + v8::Uint8Array::new(scope, ab, 0, buf_len) + .expect("Failed to create Uint8Array") + .into(), + ) + } +} + +impl FromV8 for MagicBuffer { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + Ok(Self::FromV8(ZeroCopyBuf::from_v8(scope, value)?)) } } diff --git a/serde_v8/src/magic/bytestring.rs b/serde_v8/src/magic/bytestring.rs index 942aec64a0..43ac54b976 100644 --- a/serde_v8/src/magic/bytestring.rs +++ b/serde_v8/src/magic/bytestring.rs @@ -2,14 +2,13 @@ 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"; +use super::transl8::{FromV8, ToV8}; +use crate::magic::transl8::impl_magic; +use crate::Error; #[derive(PartialEq, Eq, Clone, Debug)] pub struct ByteString(pub Vec); +impl_magic!(ByteString); impl ByteString { pub fn new() -> ByteString { @@ -81,45 +80,43 @@ impl AsMut<[u8]> for ByteString { } } -impl Serialize for ByteString { - fn serialize(&self, serializer: S) -> Result - 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 ToV8 for ByteString { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let v = + v8::String::new_from_one_byte(scope, self, v8::NewStringType::Normal) + .unwrap(); + Ok(v.into()) } } -impl<'de> Deserialize<'de> for ByteString { - fn deserialize(deserializer: D) -> Result - 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(self, v: Vec) -> Result - where - E: serde::de::Error, - { - Ok(ByteString(v)) - } +impl FromV8 for ByteString { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + let v8str = v8::Local::::try_from(value) + .map_err(|_| Error::ExpectedString)?; + if !v8str.contains_only_onebyte() { + return Err(Error::ExpectedLatin1); } - - deserializer.deserialize_struct(NAME, &[], ValueVisitor {}) + let len = v8str.length(); + let mut buffer = Vec::with_capacity(len); + // SAFETY: we set length == capacity (see previous line), + // before immediately writing into that buffer and sanity check with an assert + #[allow(clippy::uninit_vec)] + unsafe { + buffer.set_len(len); + let written = v8str.write_one_byte( + scope, + &mut buffer, + 0, + v8::WriteOptions::NO_NULL_TERMINATION, + ); + assert!(written == len); + } + Ok(ByteString(buffer)) } } diff --git a/serde_v8/src/magic/field.rs b/serde_v8/src/magic/field.rs deleted file mode 100644 index a188ed0e57..0000000000 --- a/serde_v8/src/magic/field.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2018-2022 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) -pub type TransmutedField = u64; -pub type FieldResult = Result; - -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; - type SerializeTuple = Impossible; - type SerializeTupleStruct = Impossible; - type SerializeTupleVariant = Impossible; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Impossible; - - 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(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( - self, - _name: &'static str, - _value: &T, - ) -> FieldResult { - unreachable!(); - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> FieldResult { - unreachable!(); - } - fn serialize_seq(self, _len: Option) -> Result { - unreachable!(); - } - - fn serialize_tuple(self, _len: usize) -> Result { - unreachable!(); - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - unreachable!(); - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - unreachable!(); - } - - fn serialize_map(self, _len: Option) -> Result { - unreachable!(); - } - - fn serialize_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - unreachable!(); - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - unreachable!(); - } -} diff --git a/serde_v8/src/magic/mod.rs b/serde_v8/src/magic/mod.rs index e90b5ab608..bc86c6a7c9 100644 --- a/serde_v8/src/magic/mod.rs +++ b/serde_v8/src/magic/mod.rs @@ -1,11 +1,9 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. pub mod buffer; pub mod bytestring; -mod field; pub mod string_or_buffer; +pub mod transl8; pub mod u16string; mod value; pub mod zero_copy_buf; - -pub use field::FieldSerializer; -pub use value::{Value, FIELD, NAME}; +pub use value::Value; diff --git a/serde_v8/src/magic/transl8.rs b/serde_v8/src/magic/transl8.rs new file mode 100644 index 0000000000..458b821299 --- /dev/null +++ b/serde_v8/src/magic/transl8.rs @@ -0,0 +1,143 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +//! Transerialization extends the set of serde-compatible types (for given de/serializers). +//! By "hackishly" transmuting references across serde boundaries as u64s. +//! Type-safety is enforced using special struct names for each "magic type". +//! Memory-safety relies on transerialized values being "pinned" during de/serialization. + +pub(crate) const MAGIC_FIELD: &str = "$__v8_magic_field"; + +pub(crate) trait MagicType { + const NAME: &'static str; + const MAGIC_NAME: &'static str; +} + +pub(crate) trait ToV8 { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error>; +} + +pub(crate) trait FromV8: Sized { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result; +} + +pub(crate) fn magic_serialize( + serializer: S, + x: &T, +) -> Result +where + S: serde::Serializer, + T: MagicType, +{ + use serde::ser::SerializeStruct; + + let mut s = serializer.serialize_struct(T::MAGIC_NAME, 1)?; + let ptr = opaque_send(x); + s.serialize_field(MAGIC_FIELD, &ptr)?; + s.end() +} + +pub(crate) fn magic_deserialize<'de, T, D>( + deserializer: D, +) -> Result +where + D: serde::Deserializer<'de>, + T: MagicType, +{ + struct ValueVisitor { + p1: std::marker::PhantomData, + } + + impl<'de, T: MagicType> serde::de::Visitor<'de> for ValueVisitor { + type Value = T; + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + formatter.write_str("a ")?; + formatter.write_str(T::NAME) + } + + fn visit_u64(self, ptr: u64) -> Result + where + E: serde::de::Error, + { + // SAFETY: opaque ptr originates from visit_magic, which forgets ownership so we can take it + Ok(unsafe { opaque_take(ptr) }) + } + } + + deserializer.deserialize_struct( + T::MAGIC_NAME, + &[MAGIC_FIELD], + ValueVisitor:: { + p1: std::marker::PhantomData, + }, + ) +} + +pub(crate) fn visit_magic<'de, T, V, E>(visitor: V, x: T) -> Result +where + V: serde::de::Visitor<'de>, + E: serde::de::Error, +{ + let y = visitor.visit_u64::(opaque_send(&x)); + std::mem::forget(x); + y +} + +/// Constructs an "opaque" ptr from a reference to transerialize +pub(crate) fn opaque_send(x: &T) -> u64 { + (x as *const T) as u64 +} + +/// Copies an "opaque" ptr from a reference to an opaque ptr (transerialized) +/// NOTE: ptr-to-ptr, extra indirection +pub(crate) unsafe fn opaque_recv(ptr: &T) -> u64 { + *(ptr as *const T as *const u64) +} + +/// Transmutes an "opaque" ptr back into a reference +pub(crate) unsafe fn opaque_deref<'a, T>(ptr: u64) -> &'a T { + std::mem::transmute(ptr) +} + +/// Transmutes & copies the value from the "opaque" ptr +/// NOTE: takes ownership & requires other end to forget its ownership +pub(crate) unsafe fn opaque_take(ptr: u64) -> T { + std::mem::transmute_copy::(std::mem::transmute(ptr)) +} + +macro_rules! impl_magic { + ($t:ty) => { + impl crate::magic::transl8::MagicType for $t { + const NAME: &'static str = stringify!($t); + const MAGIC_NAME: &'static str = concat!("$__v8_magic_", stringify!($t)); + } + + impl serde::Serialize for $t { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + crate::magic::transl8::magic_serialize(serializer, self) + } + } + + impl<'de> serde::Deserialize<'de> for $t { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + crate::magic::transl8::magic_deserialize(deserializer) + } + } + }; +} +pub(crate) use impl_magic; diff --git a/serde_v8/src/magic/u16string.rs b/serde_v8/src/magic/u16string.rs index a75af016be..39ebf88d9e 100644 --- a/serde_v8/src/magic/u16string.rs +++ b/serde_v8/src/magic/u16string.rs @@ -1,13 +1,12 @@ +use crate::magic::transl8::impl_magic; +use crate::Error; use std::ops::{Deref, DerefMut}; -use serde::Serialize; +use super::transl8::{FromV8, ToV8}; -pub const NAME: &str = "$__v8_magic_u16string"; -pub const FIELD_PTR: &str = "$__v8_magic_u16string_ptr"; -pub const FIELD_LEN: &str = "$__v8_magic_u16string_len"; - -#[derive(Default, PartialEq, Eq)] +#[derive(Default, PartialEq, Eq, Debug)] pub struct U16String(pub Vec); +impl_magic!(U16String); impl U16String { pub fn with_zeroes(length: usize) -> U16String { @@ -45,18 +44,40 @@ impl AsMut<[u16]> for U16String { } } -impl Serialize for U16String { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - - let mut s = serializer.serialize_struct(NAME, 3)?; - s.serialize_field(FIELD_PTR, &(self.0.as_ptr() as usize))?; - s.serialize_field(FIELD_LEN, &self.0.len())?; - s.end() +impl ToV8 for U16String { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let v = + v8::String::new_from_two_byte(scope, self, v8::NewStringType::Normal) + .unwrap(); + Ok(v.into()) } } -// TODO: Deserialize +impl FromV8 for U16String { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + let v8str = v8::Local::::try_from(value) + .map_err(|_| Error::ExpectedString)?; + let len = v8str.length(); + let mut buffer = Vec::with_capacity(len); + // SAFETY: we set length == capacity (see previous line), + // before immediately writing into that buffer and sanity check with an assert + #[allow(clippy::uninit_vec)] + unsafe { + buffer.set_len(len); + let written = v8str.write( + scope, + &mut buffer, + 0, + v8::WriteOptions::NO_NULL_TERMINATION, + ); + assert!(written == len); + } + Ok(U16String(buffer)) + } +} diff --git a/serde_v8/src/magic/value.rs b/serde_v8/src/magic/value.rs index 7bd9a4059e..5d844fbdab 100644 --- a/serde_v8/src/magic/value.rs +++ b/serde_v8/src/magic/value.rs @@ -1,21 +1,19 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use std::fmt; -use std::marker::PhantomData; - -pub const FIELD: &str = "$__v8_magic_value"; -pub const NAME: &str = "$__v8_magic_Value"; +use crate::magic::transl8::impl_magic; +use crate::magic::transl8::FromV8; +use crate::magic::transl8::ToV8; +use std::mem::transmute; /// 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" +/// when de/serializing & allows mixing rust & v8 values in structs, tuples... +// +// SAFETY: caveat emptor, the rust-compiler can no longer link lifetimes to their +// original scope, you must take special care in ensuring your handles don't outlive their scope pub struct Value<'s> { pub v8_value: v8::Local<'s, v8::Value>, } +impl_magic!(Value<'_>); impl<'s> From> for Value<'s> { fn from(v8_value: v8::Local<'s, v8::Value>) -> Self { @@ -29,50 +27,22 @@ impl<'s> From> for v8::Local<'s, v8::Value> { } } -impl serde::Serialize for Value<'_> { - fn serialize(&self, serializer: S) -> Result - 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 ToV8 for Value<'_> { + fn to_v8<'a>( + &self, + _scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + // SAFETY: not fully safe, since lifetimes are detached from original scope + Ok(unsafe { transmute(self.v8_value) }) } } -impl<'de, 's> serde::Deserialize<'de> for Value<'s> { - fn deserialize(deserializer: D) -> Result, 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(self, v: u64) -> Result - 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) +impl FromV8 for Value<'_> { + fn from_v8( + _scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + // SAFETY: not fully safe, since lifetimes are detached from original scope + Ok(unsafe { transmute::(value.into()) }) } } diff --git a/serde_v8/src/magic/zero_copy_buf.rs b/serde_v8/src/magic/zero_copy_buf.rs index 30acd8137c..c63d4ba668 100644 --- a/serde_v8/src/magic/zero_copy_buf.rs +++ b/serde_v8/src/magic/zero_copy_buf.rs @@ -4,6 +4,8 @@ use std::cell::Cell; use std::ops::Deref; use std::ops::DerefMut; +use super::transl8::FromV8; + /// 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 @@ -43,28 +45,33 @@ impl ZeroCopyBuf { }), } } -} -impl<'s> TryFrom> for ZeroCopyBuf { - type Error = v8::DataError; - fn try_from(buffer: v8::Local) -> Result { - Self::from_buffer(buffer, 0, buffer.byte_length()) + pub fn from_view( + scope: &mut v8::HandleScope, + view: v8::Local, + ) -> Result { + let buffer = view.buffer(scope).ok_or(v8::DataError::NoData { + expected: "view to have a buffer", + })?; + Self::from_buffer(buffer, view.byte_offset(), view.byte_length()) } } -// TODO(@AaronO): consider streamlining this as "ScopedValue" ? -type ScopedView<'a, 'b, 's> = ( - &'s mut v8::HandleScope<'a>, - v8::Local<'b, v8::ArrayBufferView>, -); -impl<'a, 'b, 's> TryFrom> for ZeroCopyBuf { - type Error = v8::DataError; - fn try_from( - scoped_view: ScopedView<'a, 'b, 's>, - ) -> Result { - let (scope, view) = scoped_view; - let buffer = view.buffer(scope).unwrap(); - Self::from_buffer(buffer, view.byte_offset(), view.byte_length()) +impl FromV8 for ZeroCopyBuf { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + if value.is_array_buffer() { + value + .try_into() + .and_then(|b| Self::from_buffer(b, 0, b.byte_length())) + } else { + value + .try_into() + .and_then(|view| Self::from_view(scope, view)) + } + .map_err(|_| crate::Error::ExpectedBuffer) } } diff --git a/serde_v8/src/ser.rs b/serde_v8/src/ser.rs index ce7e8c707a..5241aeaeff 100644 --- a/serde_v8/src/ser.rs +++ b/serde_v8/src/ser.rs @@ -6,7 +6,9 @@ use std::cell::RefCell; use crate::error::{Error, Result}; use crate::keys::v8_struct_key; -use crate::magic; +use crate::magic::transl8::MAGIC_FIELD; +use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8}; +use crate::{magic, Buffer, ByteString, U16String}; type JsValue<'s> = v8::Local<'s, v8::Value>; type JsResult<'s> = Result>; @@ -212,191 +214,55 @@ impl<'a, 'b, 'c> ser::SerializeStruct for ObjectSerializer<'a, 'b, 'c> { } } -pub struct MagicSerializer<'a> { - v8_value: Option>, -} - -impl<'a> ser::SerializeStruct for MagicSerializer<'a> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &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> { +pub struct MagicalSerializer<'a, 'b, 'c, T> { scope: ScopePtr<'a, 'b, 'c>, - f1: u64, - f2: u64, + opaque: u64, + p1: std::marker::PhantomData, } -impl<'a, 'b, 'c> MagicBufferSerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { +impl<'a, 'b, 'c, T> MagicalSerializer<'a, 'b, 'c, T> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> MagicalSerializer<'a, 'b, 'c, T> { Self { scope, - f1: 0, - f2: 0, + opaque: 0, + p1: std::marker::PhantomData:: {}, } } } -impl<'a, 'b, 'c> ser::SerializeStruct for MagicBufferSerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &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>, - len: Option, -} - -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> +impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct + for MagicalSerializer<'a, 'b, 'c, T> { type Ok = JsValue<'a>; type Error = Error; - fn serialize_field( + fn serialize_field( &mut self, key: &'static str, - value: &T, + value: &U, ) -> 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!(), - } + assert_eq!(key, MAGIC_FIELD); + let ptr: &U = value; + // SAFETY: MagicalSerializer only ever receives single field u64s, + // type-safety is ensured by MAGIC_NAME checks in `serialize_struct()` + self.opaque = unsafe { opaque_recv(ptr) }; Ok(()) } fn end(self) -> JsResult<'a> { - // SAFETY: 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()) - }; + // SAFETY: transerialization assumptions imply `T` is still alive. + let x: &T = unsafe { opaque_deref(self.opaque) }; 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()) - } -} - -pub struct MagicU16StringSerializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, - ptr: Option>, - len: Option, -} - -impl<'a, 'b, 'c> MagicU16StringSerializer<'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 MagicU16StringSerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - // Get u64 chunk - let transmuted = value.serialize(magic::FieldSerializer {})?; - match key { - magic::u16string::FIELD_PTR => { - self.ptr = std::ptr::NonNull::new(transmuted as *mut u16) - } - magic::u16string::FIELD_LEN => self.len = Some(transmuted as usize), - _ => unreachable!(), - } - Ok(()) - } - - fn end(self) -> JsResult<'a> { - // SAFETY: This function is only called from U16String::serialize(), which - // guarantees the Vec is still alive. - let slice = 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_two_byte(scope, slice, v8::NewStringType::Normal) - .unwrap(); - Ok(v8_value.into()) + x.to_v8(scope) } } // 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>), - MagicU16String(MagicU16StringSerializer<'a, 'b, 'c>), + Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>), + MagicBuffer(MagicalSerializer<'a, 'b, 'c, Buffer>), + MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), + MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), Regular(ObjectSerializer<'a, 'b, 'c>), } @@ -650,23 +516,24 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { len: usize, ) -> Result { 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); + ByteString::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); Ok(StructSerializers::MagicByteString(m)) } - magic::u16string::NAME => { - let m = MagicU16StringSerializer::new(self.scope); + U16String::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); Ok(StructSerializers::MagicU16String(m)) } + Buffer::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); + Ok(StructSerializers::MagicBuffer(m)) + } + magic::Value::MAGIC_NAME => { + let m = MagicalSerializer::>::new(self.scope); + Ok(StructSerializers::Magic(m)) + } _ => { + // Regular structs let o = ObjectSerializer::new(self.scope, len); Ok(StructSerializers::Regular(o)) } @@ -685,21 +552,3 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { 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/tests/de.rs b/serde_v8/tests/de.rs index 525089849f..0c784acedd 100644 --- a/serde_v8/tests/de.rs +++ b/serde_v8/tests/de.rs @@ -2,9 +2,9 @@ use serde::Deserialize; use serde_v8::utils::{js_exec, v8_do}; -use serde_v8::Buffer; use serde_v8::ByteString; use serde_v8::Error; +use serde_v8::{Buffer, U16String}; #[derive(Debug, Deserialize, PartialEq)] struct MathOp { @@ -316,3 +316,16 @@ detest!( detest!(de_bstr, ByteString, "'hello'", ByteString("hello".into())); defail!(defail_bstr, ByteString, "'👋bye'", |e| e == Err(Error::ExpectedLatin1)); + +detest!( + de_u16str, + U16String, + "'hello'", + U16String("hello".encode_utf16().collect()) +); +detest!( + de_u16str_non_latin1, + U16String, + "'👋bye'", + U16String("👋bye".encode_utf16().collect()) +);