diff options
author | Matt Mastracci <matthew@mastracci.com> | 2023-07-01 18:00:14 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-02 00:00:14 +0000 |
commit | e746b6d80654ba4e4e26370fe6e4f784ce841d92 (patch) | |
tree | 153ffad92a96126b9ab8e906dcdabf7648755931 /serde_v8 | |
parent | b9c0e7cd550ab14fa7da7e33ed87cbeeeb9785a0 (diff) |
refactor(core): Extract deno_core (#19658)
`deno_core` is moving out! You'll find it at
https://github.com/denoland/deno_core/ once this PR lands.
Diffstat (limited to 'serde_v8')
30 files changed, 0 insertions, 4673 deletions
diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml deleted file mode 100644 index 527bc27a0..000000000 --- a/serde_v8/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -[package] -name = "serde_v8" -version = "0.102.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -readme = "README.md" -repository.workspace = true -description = "Rust to V8 serialization and deserialization" - -[lib] -path = "lib.rs" - -[dependencies] -bytes.workspace = true -derive_more = "0.99.17" -num-bigint.workspace = true -serde.workspace = true -serde_bytes.workspace = true -smallvec = { workspace = true, features = ["union"] } -thiserror.workspace = true -v8.workspace = true - -[dev-dependencies] -bencher.workspace = true -serde_json.workspace = true - -[[example]] -name = "basic" - -[[bench]] -name = "de" -harness = false - -[[bench]] -name = "ser" -harness = false diff --git a/serde_v8/README.md b/serde_v8/README.md deleted file mode 100644 index d36f69375..000000000 --- a/serde_v8/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# serde_v8 - -Author: Aaron O'Mullan <aaron.omullan@gmail.com> - -Serde support for encoding/decoding (rusty_)v8 values. - -Broadly `serde_v8` aims to provide an expressive but ~maximally efficient -encoding layer to biject rust & v8/js values. It's a core component of deno's -op-layer and is used to encode/decode all non-buffer values. - -**Original issue:** -[denoland/deno#9540](https://github.com/denoland/deno/issues/9540) - -## Quickstart - -`serde_v8` fits naturally into the serde ecosystem, so if you've already used -`serde` or `serde_json`, `serde_v8`'s API should be very familiar. - -`serde_v8` exposes two key-functions: - -- `to_v8`: maps `rust->v8`, similar to `serde_json::to_string`, ... -- `from_v8`: maps `v8->rust`, similar to `serde_json::from_str`, ... - -## Best practices - -Whilst `serde_v8` is compatible with `serde_json::Value` it's important to keep -in mind that `serde_json::Value` is essentially a loosely-typed value (think -nested HashMaps), so when writing ops we recommend directly using rust -structs/tuples or primitives, since mapping to `serde_json::Value` will add -extra overhead and result in slower ops. - -I also recommend avoiding unnecessary "wrappers", if your op takes a -single-keyed struct, consider unwrapping that as a plain value unless you plan -to add fields in the near-future. - -Instead of returning "nothing" via `Ok(json!({}))`, change your return type to -rust's unit type `()` and returning `Ok(())`, `serde_v8` will efficiently encode -that as a JS `null`. - -## Advanced features - -If you need to mix rust & v8 values in structs/tuples, you can use the special -`serde_v8::Value` type, which will passthrough the original v8 value untouched -when encoding/decoding. - -## TODO - -- [ ] Experiment with KeyCache to optimize struct keys -- [ ] Experiment with external v8 strings -- [ ] Explore using - [json-stringifier.cc](https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/json/json-stringifier.cc)'s - fast-paths for arrays -- [ ] Improve tests to test parity with `serde_json` (should be mostly - interchangeable) -- [ ] Consider a `Payload` type that's deserializable by itself (holds scope & - value) -- [ ] Ensure we return errors instead of panicking on `.unwrap()`s diff --git a/serde_v8/benches/de.rs b/serde_v8/benches/de.rs deleted file mode 100644 index b7cd0bf32..000000000 --- a/serde_v8/benches/de.rs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use bencher::benchmark_group; -use bencher::benchmark_main; -use bencher::Bencher; - -use serde::Deserialize; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::ByteString; - -#[derive(Debug, Deserialize, PartialEq)] -struct MathOp { - arg1: u64, - arg2: u64, - operator: Option<String>, -} - -fn dedo( - code: &str, - f: impl FnOnce(&mut v8::HandleScope, v8::Local<v8::Value>), -) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let v = js_exec(scope, code); - - f(scope, v); - }) -} - -fn dedo_json(code: &str, f: impl FnOnce(String)) { - let code = format!("JSON.stringify({code})"); - dedo(&code[..], |scope, v| { - let s: String = serde_v8::from_v8(scope, v).unwrap(); - f(s); - }) -} - -fn de_struct_v8(b: &mut Bencher) { - dedo("({arg1: 10, arg2: 123 })", |scope, obj| { - let mut total = 0; - b.iter(move || { - let op: MathOp = serde_v8::from_v8(scope, obj).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_v8_opt(b: &mut Bencher) { - dedo("({arg1: 10, arg2: 123 })", |scope, v| { - let k_arg1 = v8::String::new(scope, "arg1").unwrap().into(); - let k_arg2 = v8::String::new(scope, "arg2").unwrap().into(); - let obj = v8::Local::<v8::Object>::try_from(v).unwrap(); - let mut total = 0; - b.iter(move || { - let v_arg1 = obj.get(scope, k_arg1).unwrap(); - let v_arg2 = obj.get(scope, k_arg2).unwrap(); - let op = MathOp { - arg1: serde_v8::from_v8(scope, v_arg1).unwrap(), - arg2: serde_v8::from_v8(scope, v_arg2).unwrap(), - operator: None, - }; - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_json(b: &mut Bencher) { - dedo_json("({arg1: 10, arg2: 123 })", |s| { - let mut total = 0; - b.iter(move || { - let op: MathOp = serde_json::from_str(&s).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_json_deopt(b: &mut Bencher) { - // JSON.stringify() in loop (semi-simulating ABI loop) - dedo("({arg1: 10, arg2: 123 })", |scope, obj| { - let mut total = 0; - b.iter(move || { - let mut scope = v8::HandleScope::new(scope); - let s = v8::json::stringify(&mut scope, obj).unwrap(); - let rs = s.to_rust_string_lossy(&mut scope); - let op: MathOp = serde_json::from_str(&rs).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -macro_rules! dualbench { - ($v8_fn:ident, $json_fn:ident, $src:expr, $t:ty) => { - fn $v8_fn(b: &mut Bencher) { - dedo($src, |scope, v| { - b.iter(move || { - let _: $t = serde_v8::from_v8(scope, v).unwrap(); - }); - }); - } - - fn $json_fn(b: &mut Bencher) { - dedo_json($src, |s| { - b.iter(move || { - let _: $t = serde_json::from_str(&s).unwrap(); - }); - }); - } - }; -} - -dualbench!(de_bool_v8, de_bool_json, "true", bool); -dualbench!(de_int_v8, de_int_json, "12345", u32); -dualbench!( - de_array_v8, - de_array_json, - "[1,2,3,4,5,6,7,8,9,10]", - Vec<u32> -); -dualbench!(de_str_v8, de_str_json, "'hello world'", String); -dualbench!(de_tuple_v8, de_tuple_json, "[1,false]", (u8, bool)); - -fn de_tuple_v8_opt(b: &mut Bencher) { - dedo("[1,false]", |scope, obj| { - let arr = v8::Local::<v8::Array>::try_from(obj).unwrap(); - let obj = v8::Local::<v8::Object>::from(arr); - - b.iter(move || { - let v1 = obj.get_index(scope, 0).unwrap(); - let v2 = obj.get_index(scope, 1).unwrap(); - let _: (u8, bool) = ( - serde_v8::from_v8(scope, v1).unwrap(), - serde_v8::from_v8(scope, v2).unwrap(), - ); - }); - }); -} - -fn de_bstr_v8_12_b(b: &mut Bencher) { - dedo(r#""hello world\n""#, |scope, v| { - b.iter(move || { - let _: ByteString = serde_v8::from_v8(scope, v).unwrap(); - }); - }); -} - -fn de_bstr_v8_1024_b(b: &mut Bencher) { - dedo( - r#""hello world\n".repeat(1e2).slice(0, 1024)"#, - |scope, v| { - b.iter(move || { - let _: ByteString = serde_v8::from_v8(scope, v).unwrap(); - }); - }, - ); -} - -fn de_sob_str_6b(b: &mut Bencher) { - dedo("'byebye'", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_str_1kb(b: &mut Bencher) { - dedo("'deno'.repeat(256)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_1b(b: &mut Bencher) { - dedo("new Uint8Array([97])", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_1kb(b: &mut Bencher) { - dedo("(new Uint8Array(1*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_16kb(b: &mut Bencher) { - dedo("(new Uint8Array(16*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_512kb(b: &mut Bencher) { - dedo("(new Uint8Array(512*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -benchmark_group!( - benches, - de_struct_v8, - de_struct_v8_opt, - de_struct_json, - de_struct_json_deopt, - de_bool_v8, - de_bool_json, - de_int_v8, - de_int_json, - de_array_v8, - de_array_json, - de_str_v8, - de_str_json, - de_tuple_v8, - de_tuple_json, - de_tuple_v8_opt, - de_bstr_v8_12_b, - de_bstr_v8_1024_b, - de_sob_str_6b, - de_sob_str_1kb, - de_sob_buf_1b, - de_sob_buf_1kb, - de_sob_buf_16kb, - de_sob_buf_512kb, -); - -benchmark_main!(benches); diff --git a/serde_v8/benches/ser.rs b/serde_v8/benches/ser.rs deleted file mode 100644 index 83274fae3..000000000 --- a/serde_v8/benches/ser.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use bencher::benchmark_group; -use bencher::benchmark_main; -use bencher::Bencher; - -use serde::Serialize; - -use serde_v8::utils::v8_do; -use serde_v8::ByteString; - -#[derive(Serialize)] -struct MathOp { - arg1: u64, - arg2: u64, - operator: Option<String>, -} - -fn serdo(f: impl FnOnce(&mut v8::HandleScope)) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - f(scope); - }) -} - -macro_rules! dualbench { - ($v8_fn:ident, $json_fn:ident, $src:expr) => { - fn $v8_fn(b: &mut Bencher) { - serdo(|scope| { - let v = $src; - b.iter(move || { - let _ = serde_v8::to_v8(scope, &v).unwrap(); - }); - }); - } - - fn $json_fn(b: &mut Bencher) { - let v = $src; - b.iter(move || { - let _ = serde_json::to_string(&v).unwrap(); - }); - } - }; -} - -dualbench!( - ser_struct_v8, - ser_struct_json, - MathOp { - arg1: 10, - arg2: 123, - operator: None - } -); -dualbench!(ser_bool_v8, ser_bool_json, true); -dualbench!(ser_int_v8, ser_int_json, 12345); -dualbench!( - ser_array_v8, - ser_array_json, - vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -); -dualbench!(ser_str_v8, ser_str_json, "hello world"); -dualbench!(ser_tuple_v8, ser_tuple_json, (1, false)); - -fn ser_struct_v8_manual(b: &mut Bencher) { - serdo(|scope| { - let v = MathOp { - arg1: 10, - arg2: 123, - operator: None, - }; - b.iter(|| { - let obj = v8::Object::new(scope); - let k1 = v8::String::new(scope, "arg1").unwrap(); - let k2 = v8::String::new(scope, "arg2").unwrap(); - let k3 = v8::String::new(scope, "operator").unwrap(); - // let k1 = v8::String::new_from_utf8(scope, "arg1".as_ref(), v8::NewStringType::Internalized).unwrap(); - // let k2 = v8::String::new_from_utf8(scope, "arg2".as_ref(), v8::NewStringType::Internalized).unwrap(); - // let k3 = v8::String::new_from_utf8(scope, "operator".as_ref(), v8::NewStringType::Internalized).unwrap(); - let v1 = v8::Number::new(scope, v.arg1 as f64); - let v2 = v8::Number::new(scope, v.arg2 as f64); - let v3 = v8::null(scope); - obj.set(scope, k1.into(), v1.into()).unwrap(); - obj.set(scope, k2.into(), v2.into()).unwrap(); - obj.set(scope, k3.into(), v3.into()).unwrap(); - }); - }); -} - -fn ser_bstr_12_b(b: &mut Bencher) { - serdo(|scope| { - let bstr = ByteString::from("hello world\n"); - b.iter(|| { - let _ = serde_v8::to_v8(scope, &bstr).unwrap(); - }); - }); -} - -fn ser_bstr_1024_b(b: &mut Bencher) { - serdo(|scope| { - let mut s = "hello world\n".repeat(100); - s.truncate(1024); - let bstr = ByteString::from(s); - b.iter(|| { - let _ = serde_v8::to_v8(scope, &bstr).unwrap(); - }); - }); -} - -benchmark_group!( - benches, - ser_struct_v8, - ser_struct_json, - ser_bool_v8, - ser_bool_json, - ser_int_v8, - ser_int_json, - ser_array_v8, - ser_array_json, - ser_str_v8, - ser_str_json, - ser_tuple_v8, - ser_tuple_json, - ser_struct_v8_manual, - ser_bstr_12_b, - ser_bstr_1024_b, -); -benchmark_main!(benches); diff --git a/serde_v8/de.rs b/serde_v8/de.rs deleted file mode 100644 index eb07271b4..000000000 --- a/serde_v8/de.rs +++ /dev/null @@ -1,787 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::de::SeqAccess as _; -use serde::de::Visitor; -use serde::de::{self}; -use serde::Deserialize; - -use crate::error::value_to_type_str; -use crate::error::Error; -use crate::error::Result; -use crate::keys::v8_struct_key; -use crate::keys::KeyCache; -use crate::magic; -use crate::magic::transl8::visit_magic; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::MagicType; -use crate::payload::ValueType; -use crate::AnyValue; -use crate::BigInt; -use crate::ByteString; -use crate::DetachedBuffer; -use crate::JsBuffer; -use crate::StringOrBuffer; -use crate::U16String; - -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! deserialize_signed { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - visitor.$vmethod( - if let Ok(x) = v8::Local::<v8::Number>::try_from(self.input) { - x.value() as $t - } else if let Ok(x) = v8::Local::<v8::BigInt>::try_from(self.input) { - x.i64_value().0 as $t - } else { - return Err(Error::ExpectedInteger(value_to_type_str(self.input))); - }, - ) - } - }; -} - -macro_rules! deserialize_unsigned { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - visitor.$vmethod( - if let Ok(x) = v8::Local::<v8::Number>::try_from(self.input) { - x.value() as $t - } else if let Ok(x) = v8::Local::<v8::BigInt>::try_from(self.input) { - x.u64_value().0 as $t - } else { - return Err(Error::ExpectedInteger(value_to_type_str(self.input))); - }, - ) - } - }; -} - -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::BigInt => Err(Error::UnsupportedType), - ValueType::String => self.deserialize_string(visitor), - ValueType::Array => self.deserialize_seq(visitor), - ValueType::Object => self.deserialize_map(visitor), - // Map to Vec<u8> when deserialized via deserialize_any - // e.g: for untagged enums or StringOrBuffer - ValueType::ArrayBufferView | ValueType::ArrayBuffer => { - magic::v8slice::V8Slice::from_v8(&mut *self.scope, self.input) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } - } - } - - 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>, - { - self.deserialize_f64(visitor) - } - fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - visitor.visit_f64( - if let Ok(x) = v8::Local::<v8::Number>::try_from(self.input) { - x.value() - } else if let Ok(x) = v8::Local::<v8::BigInt>::try_from(self.input) { - bigint_to_f64(x) - } else { - return Err(Error::ExpectedNumber(value_to_type_str(self.input))); - }, - ) - } - - fn deserialize_char<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - 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() || self.input.is_string_object() { - let v8_string = self.input.to_string(self.scope).unwrap(); - let string = to_utf8(v8_string, self.scope); - visitor.visit_string(string) - } else { - Err(Error::ExpectedString(value_to_type_str(self.input))) - } - } - - 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>, - { - visitor.visit_unit() - } - - 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(value_to_type_str(self.input)))?; - visitor.visit_seq(SeqAccess::new(arr.into(), self.scope, 0..arr.length())) - } - - // 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>, - { - let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap(); - if obj.is_array() { - // If the obj is an array fail if it's length differs from the tuple length - let array = v8::Local::<v8::Array>::try_from(self.input).unwrap(); - let array_len = array.length() as usize; - if array_len != len { - return Err(Error::LengthMismatch(array_len, len)); - } - } - visitor.visit_seq(SeqAccess::new(obj, self.scope, 0..len as u32)) - } - - // 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(value_to_type_str(self.input)))?; - - if v8::Local::<v8::Map>::try_from(self.input).is_ok() { - let pairs_array = v8::Local::<v8::Map>::try_from(self.input) - .unwrap() - .as_array(self.scope); - let map = MapPairsAccess { - pos: 0, - len: pairs_array.length(), - obj: pairs_array, - scope: self.scope, - }; - visitor.visit_map(map) - } else { - visitor.visit_map(MapObjectAccess::new(obj, self.scope)) - } - } - - fn deserialize_struct<V>( - self, - name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> Result<V::Value> - where - V: Visitor<'de>, - { - match name { - JsBuffer::MAGIC_NAME => { - visit_magic(visitor, JsBuffer::from_v8(self.scope, self.input)?) - } - DetachedBuffer::MAGIC_NAME => { - visit_magic(visitor, DetachedBuffer::from_v8(self.scope, self.input)?) - } - ByteString::MAGIC_NAME => { - visit_magic(visitor, ByteString::from_v8(self.scope, self.input)?) - } - U16String::MAGIC_NAME => { - visit_magic(visitor, U16String::from_v8(self.scope, self.input)?) - } - StringOrBuffer::MAGIC_NAME => { - visit_magic(visitor, StringOrBuffer::from_v8(self.scope, self.input)?) - } - BigInt::MAGIC_NAME => { - visit_magic(visitor, BigInt::from_v8(self.scope, self.input)?) - } - magic::Value::MAGIC_NAME => { - visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) - } - AnyValue::MAGIC_NAME => { - visit_magic(visitor, AnyValue::from_v8(self.scope, self.input)?) - } - _ => { - // Regular struct - let obj = v8::Local::<v8::Object>::try_from(self.input) - .map_err(|_| Error::ExpectedObject(value_to_type_str(self.input)))?; - - // Fields names are a hint and must be inferred when not provided - if fields.is_empty() { - visitor.visit_map(MapObjectAccess::new(obj, self.scope)) - } else { - visitor.visit_map(StructAccess { - obj, - scope: self.scope, - keys: fields.iter(), - next_value: None, - }) - } - } - } - } - - /// 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() || self.input.is_string_object() { - 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, Default::default()); - let prop_names = prop_names - .ok_or_else(|| Error::ExpectedEnum(value_to_type_str(self.input)))?; - let prop_names_len = prop_names.length(); - if prop_names_len != 1 { - return Err(Error::LengthMismatch(prop_names_len as usize, 1)); - } - 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 { - Err(Error::ExpectedEnum(value_to_type_str(self.input))) - } - } - - // 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() - } - - fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - magic::buffer::JsBuffer::from_v8(self.scope, self.input) - .and_then(|zb| visitor.visit_bytes(&zb)) - } - - fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value> - where - V: Visitor<'de>, - { - magic::buffer::JsBuffer::from_v8(self.scope, self.input) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } -} - -struct MapObjectAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - keys: SeqAccess<'a, 's>, - next_value: Option<v8::Local<'s, v8::Value>>, -} - -impl<'a, 's> MapObjectAccess<'a, 's> { - pub fn new( - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - ) -> Self { - let keys = match obj.get_own_property_names( - scope, - v8::GetPropertyNamesArgsBuilder::new() - .key_conversion(v8::KeyConversionMode::ConvertToString) - .build(), - ) { - Some(keys) => SeqAccess::new(keys.into(), scope, 0..keys.length()), - None => SeqAccess::new(obj, scope, 0..0), - }; - - Self { - obj, - keys, - next_value: None, - } - } -} - -impl<'de> de::MapAccess<'de> for MapObjectAccess<'_, '_> { - type Error = Error; - - fn next_key_seed<K: de::DeserializeSeed<'de>>( - &mut self, - seed: K, - ) -> Result<Option<K::Value>> { - while let Some(key) = self.keys.next_element::<magic::Value>()? { - let v8_val = self.obj.get(self.keys.scope, key.v8_value).unwrap(); - if v8_val.is_undefined() { - // Historically keys/value pairs with undefined values are not added to the output - continue; - } - self.next_value = Some(v8_val); - let mut deserializer = - Deserializer::new(self.keys.scope, key.v8_value, None); - return seed.deserialize(&mut deserializer).map(Some); - } - Ok(None) - } - - fn next_value_seed<V: de::DeserializeSeed<'de>>( - &mut self, - seed: V, - ) -> Result<V::Value> { - let v8_val = self - .next_value - .take() - .expect("Call next_key_seed before next_value_seed"); - let mut deserializer = Deserializer::new(self.keys.scope, v8_val, None); - seed.deserialize(&mut deserializer) - } - - fn size_hint(&self) -> Option<usize> { - self.keys.size_hint() - } -} - -struct MapPairsAccess<'a, 's> { - obj: v8::Local<'a, v8::Array>, - pos: u32, - len: u32, - scope: &'a mut v8::HandleScope<'s>, -} - -impl<'de> de::MapAccess<'de> for MapPairsAccess<'_, '_> { - type Error = Error; - - fn next_key_seed<K: de::DeserializeSeed<'de>>( - &mut self, - seed: K, - ) -> Result<Option<K::Value>> { - if self.pos < self.len { - let v8_key = self.obj.get_index(self.scope, self.pos).unwrap(); - self.pos += 1; - let mut deserializer = Deserializer::new(self.scope, v8_key, None); - let k = seed.deserialize(&mut deserializer)?; - Ok(Some(k)) - } else { - Ok(None) - } - } - - fn next_value_seed<V: de::DeserializeSeed<'de>>( - &mut self, - seed: V, - ) -> Result<V::Value> { - debug_assert!(self.pos < self.len); - let v8_val = self.obj.get_index(self.scope, self.pos).unwrap(); - self.pos += 1; - let mut deserializer = Deserializer::new(self.scope, v8_val, None); - seed.deserialize(&mut deserializer) - } - - fn size_hint(&self) -> Option<usize> { - Some((self.len - self.pos) as usize / 2) - } -} - -struct StructAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - keys: std::slice::Iter<'static, &'static str>, - next_value: Option<v8::Local<'s, v8::Value>>, -} - -impl<'de> de::MapAccess<'de> for StructAccess<'_, '_> { - type Error = Error; - - fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>> - where - K: de::DeserializeSeed<'de>, - { - for field in self.keys.by_ref() { - let key = v8_struct_key(self.scope, field).into(); - let val = self.obj.get(self.scope, key).unwrap(); - if val.is_undefined() { - // Historically keys/value pairs with undefined values are not added to the output - continue; - } - self.next_value = Some(val); - let mut deserializer = Deserializer::new(self.scope, key, None); - return seed.deserialize(&mut deserializer).map(Some); - } - Ok(None) - } - - fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value> - where - V: de::DeserializeSeed<'de>, - { - let val = self - .next_value - .take() - .expect("Call next_key_seed before next_value_seed"); - let mut deserializer = Deserializer::new(self.scope, val, None); - seed.deserialize(&mut deserializer) - } -} - -struct SeqAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - range: std::ops::Range<u32>, -} - -impl<'a, 's> SeqAccess<'a, 's> { - pub fn new( - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - range: std::ops::Range<u32>, - ) -> Self { - Self { obj, scope, range } - } -} - -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>> { - if let Some(pos) = self.range.next() { - let val = self.obj.get_index(self.scope, pos).unwrap(); - let mut deserializer = Deserializer::new(self.scope, val, None); - seed.deserialize(&mut deserializer).map(Some) - } else { - Ok(None) - } - } - - fn size_hint(&self) -> Option<usize> { - self.range.size_hint().1 - } -} - -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> 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) - } -} - -fn bigint_to_f64(b: v8::Local<v8::BigInt>) -> f64 { - // log2(f64::MAX) == log2(1.7976931348623157e+308) == 1024 - let mut words: [u64; 16] = [0; 16]; // 1024/64 => 16 64bit words - let (neg, words) = b.to_words_array(&mut words); - if b.word_count() > 16 { - return match neg { - true => f64::NEG_INFINITY, - false => f64::INFINITY, - }; - } - let sign = if neg { -1.0 } else { 1.0 }; - let x: f64 = words - .iter() - .enumerate() - .map(|(i, w)| (*w as f64) * 2.0f64.powi(64 * i as i32)) - .sum(); - sign * x -} - -pub fn to_utf8( - s: v8::Local<v8::String>, - scope: &mut v8::HandleScope, -) -> String { - to_utf8_fast(s, scope).unwrap_or_else(|| to_utf8_slow(s, scope)) -} - -fn to_utf8_fast( - s: v8::Local<v8::String>, - scope: &mut v8::HandleScope, -) -> Option<String> { - // Over-allocate by 20% to avoid checking string twice - let str_chars = s.length(); - let capacity = (str_chars as f64 * 1.2) as usize; - let mut buf = Vec::with_capacity(capacity); - - let mut nchars = 0; - let bytes_len = s.write_utf8_uninit( - scope, - buf.spare_capacity_mut(), - Some(&mut nchars), - v8::WriteOptions::NO_NULL_TERMINATION - | v8::WriteOptions::REPLACE_INVALID_UTF8, - ); - - if nchars < str_chars { - return None; - } - - // SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8 - unsafe { - buf.set_len(bytes_len); - Some(String::from_utf8_unchecked(buf)) - } -} - -fn to_utf8_slow( - s: v8::Local<v8::String>, - scope: &mut v8::HandleScope, -) -> String { - let capacity = s.utf8_length(scope); - let mut buf = Vec::with_capacity(capacity); - - let bytes_len = s.write_utf8_uninit( - scope, - buf.spare_capacity_mut(), - None, - v8::WriteOptions::NO_NULL_TERMINATION - | v8::WriteOptions::REPLACE_INVALID_UTF8, - ); - - // SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8 - unsafe { - buf.set_len(bytes_len); - String::from_utf8_unchecked(buf) - } -} diff --git a/serde_v8/error.rs b/serde_v8/error.rs deleted file mode 100644 index 16d7882b7..000000000 --- a/serde_v8/error.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::fmt::Display; - -pub type Result<T> = std::result::Result<T, Error>; - -#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] -#[non_exhaustive] -pub enum Error { - #[error("{0}")] - Message(String), - - #[error("serde_v8 error: invalid type; expected: boolean, got: {0}")] - ExpectedBoolean(&'static str), - - #[error("serde_v8 error: invalid type; expected: integer, got: {0}")] - ExpectedInteger(&'static str), - - #[error("serde_v8 error: invalid type; expected: number, got: {0}")] - ExpectedNumber(&'static str), - - #[error("serde_v8 error: invalid type; expected: string, got: {0}")] - ExpectedString(&'static str), - - #[error("serde_v8 error: invalid type; expected: array, got: {0}")] - ExpectedArray(&'static str), - - #[error("serde_v8 error: invalid type; expected: map, got: {0}")] - ExpectedMap(&'static str), - - #[error("serde_v8 error: invalid type; expected: enum, got: {0}")] - ExpectedEnum(&'static str), - - #[error("serde_v8 error: invalid type; expected: object, got: {0}")] - ExpectedObject(&'static str), - - #[error("serde_v8 error: invalid type; expected: buffer, got: {0}")] - ExpectedBuffer(&'static str), - - #[error("serde_v8 error: invalid type; expected: detachable, got: {0}")] - ExpectedDetachable(&'static str), - - #[error("serde_v8 error: invalid type; expected: external, got: {0}")] - ExpectedExternal(&'static str), - - #[error("serde_v8 error: invalid type; expected: bigint, got: {0}")] - ExpectedBigInt(&'static str), - - #[error("serde_v8 error: invalid type, expected: utf8")] - ExpectedUtf8, - #[error("serde_v8 error: invalid type, expected: latin1")] - ExpectedLatin1, - - #[error("serde_v8 error: unsupported type")] - UnsupportedType, - - #[error("serde_v8 error: length mismatch, got: {0}, expected: {1}")] - LengthMismatch(usize, usize), - - #[error("serde_v8 error: can't create slice from resizable ArrayBuffer")] - ResizableBackingStoreNotSupported, -} - -impl serde::ser::Error for Error { - fn custom<T: Display>(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl serde::de::Error for Error { - fn custom<T: Display>(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -pub(crate) fn value_to_type_str(value: v8::Local<v8::Value>) -> &'static str { - if value.is_module_namespace_object() { - "Module" - } else if value.is_wasm_module_object() { - "WASM module" - } else if value.is_wasm_memory_object() { - "WASM memory object" - } else if value.is_proxy() { - "Proxy" - } else if value.is_shared_array_buffer() { - "SharedArrayBuffer" - } else if value.is_data_view() { - "DataView" - } else if value.is_big_uint64_array() { - "BigUint64Array" - } else if value.is_big_int64_array() { - "BigInt64Array" - } else if value.is_float64_array() { - "Float64Array" - } else if value.is_float32_array() { - "Float32Array" - } else if value.is_int32_array() { - "Int32Array" - } else if value.is_uint32_array() { - "Uint32Array" - } else if value.is_int16_array() { - "Int16Array" - } else if value.is_uint16_array() { - "Uint16Array" - } else if value.is_int8_array() { - "Int8Array" - } else if value.is_uint8_clamped_array() { - "Uint8ClampedArray" - } else if value.is_uint8_array() { - "Uint8Array" - } else if value.is_typed_array() { - "TypedArray" - } else if value.is_array_buffer_view() { - "ArrayBufferView" - } else if value.is_array_buffer() { - "ArrayBuffer" - } else if value.is_weak_set() { - "WeakSet" - } else if value.is_weak_map() { - "WeakMap" - } else if value.is_set_iterator() { - "Set Iterator" - } else if value.is_map_iterator() { - "Map Iterator" - } else if value.is_set() { - "Set" - } else if value.is_map() { - "Map" - } else if value.is_promise() { - "Promise" - } else if value.is_generator_function() { - "Generator function" - } else if value.is_async_function() { - "Async function" - } else if value.is_reg_exp() { - "RegExp" - } else if value.is_date() { - "Date" - } else if value.is_number() { - "Number" - } else if value.is_boolean() { - "Boolean" - } else if value.is_big_int() { - "bigint" - } else if value.is_array() { - "array" - } else if value.is_function() { - "function" - } else if value.is_symbol() { - "symbol" - } else if value.is_string() { - "string" - } else if value.is_null() { - "null" - } else if value.is_undefined() { - "undefined" - } else { - "unknown" - } -} diff --git a/serde_v8/examples/basic.rs b/serde_v8/examples/basic.rs deleted file mode 100644 index 29fb98085..000000000 --- a/serde_v8/examples/basic.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option<String>, -} - -fn main() { - let platform = v8::new_default_platform(0, false).make_shared(); - v8::V8::initialize_platform(platform); - v8::V8::initialize(); - - { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - fn 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() - } - - let v = exec(scope, "32"); - let x32: u64 = serde_v8::from_v8(scope, v).unwrap(); - println!("x32 = {x32}"); - - let v = exec(scope, "({a: 1, b: 3, c: 'ignored'})"); - let mop: MathOp = serde_v8::from_v8(scope, v).unwrap(); - println!( - "mop = {{ a: {}, b: {}, operator: {:?} }}", - mop.a, mop.b, mop.operator - ); - - let v = exec(scope, "[1,2,3,4,5]"); - let arr: Vec<u64> = serde_v8::from_v8(scope, v).unwrap(); - println!("arr = {arr:?}"); - - let v = exec(scope, "['hello', 'world']"); - let hi: Vec<String> = serde_v8::from_v8(scope, v).unwrap(); - println!("hi = {hi:?}"); - - let v: v8::Local<v8::Value> = v8::Number::new(scope, 12345.0).into(); - let x: f64 = serde_v8::from_v8(scope, v).unwrap(); - println!("x = {x}"); - } - - // SAFETY: all isolates have been destroyed, so we can now safely let V8 clean - // up its resources. - unsafe { - v8::V8::dispose(); - } - v8::V8::dispose_platform(); -} diff --git a/serde_v8/keys.rs b/serde_v8/keys.rs deleted file mode 100644 index d44306fd6..000000000 --- a/serde_v8/keys.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -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/lib.rs b/serde_v8/lib.rs deleted file mode 100644 index 59c8fd41f..000000000 --- a/serde_v8/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2023 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; -pub use de::from_v8_cached; -pub use de::to_utf8; -pub use de::Deserializer; -pub use error::Error; -pub use error::Result; -pub use keys::KeyCache; -pub use magic::any_value::AnyValue; -pub use magic::bigint::BigInt; -pub use magic::buffer::JsBuffer; -pub use magic::buffer::ToJsBuffer; -pub use magic::bytestring::ByteString; -pub use magic::detached_buffer::DetachedBuffer; -pub use magic::string_or_buffer::StringOrBuffer; -pub use magic::u16string::U16String; -pub use magic::ExternalPointer; -pub use magic::Global; -pub use magic::Value; -pub use ser::to_v8; -pub use ser::Serializer; -pub use serializable::Serializable; -pub use serializable::SerializablePkg; diff --git a/serde_v8/magic/any_value.rs b/serde_v8/magic/any_value.rs deleted file mode 100644 index df85f90d8..000000000 --- a/serde_v8/magic/any_value.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::buffer::JsBuffer; -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::magic::transl8::impl_magic; -use crate::Error; -use crate::ToJsBuffer; -use num_bigint::BigInt; - -/// An untagged enum type that can be any of number, string, bool, bigint, or -/// buffer. -#[derive(Debug)] -pub enum AnyValue { - RustBuffer(ToJsBuffer), - V8Buffer(JsBuffer), - String(String), - Number(f64), - BigInt(BigInt), - Bool(bool), -} - -impl_magic!(AnyValue); - -impl ToV8 for AnyValue { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - match self { - Self::RustBuffer(buf) => crate::to_v8(scope, buf), - Self::V8Buffer(_) => unreachable!(), - Self::String(s) => crate::to_v8(scope, s), - Self::Number(num) => crate::to_v8(scope, num), - Self::BigInt(bigint) => { - crate::to_v8(scope, crate::BigInt::from(bigint.clone())) - } - Self::Bool(b) => crate::to_v8(scope, b), - } - } -} - -impl FromV8 for AnyValue { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - if value.is_string() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::String(string)) - } else if value.is_number() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::Number(string)) - } else if value.is_big_int() { - let bigint = crate::BigInt::from_v8(scope, value)?; - Ok(AnyValue::BigInt(bigint.into())) - } else if value.is_array_buffer_view() { - let buf = JsBuffer::from_v8(scope, value)?; - Ok(AnyValue::V8Buffer(buf)) - } else if value.is_boolean() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::Bool(string)) - } else { - Err(Error::Message( - "expected string, number, bigint, ArrayBufferView, boolean".into(), - )) - } - } -} diff --git a/serde_v8/magic/bigint.rs b/serde_v8/magic/bigint.rs deleted file mode 100644 index 330803daf..000000000 --- a/serde_v8/magic/bigint.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use smallvec::smallvec; -use smallvec::SmallVec; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; - -#[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, -)] -#[as_mut(forward)] -#[as_ref(forward)] -pub struct BigInt(num_bigint::BigInt); -impl_magic!(BigInt); - -impl ToV8 for BigInt { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - let (sign, words) = self.0.to_u64_digits(); - let sign_bit = sign == num_bigint::Sign::Minus; - let v = v8::BigInt::new_from_words(scope, sign_bit, &words).unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for BigInt { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - let v8bigint = v8::Local::<v8::BigInt>::try_from(value) - .map_err(|_| Error::ExpectedBigInt(value_to_type_str(value)))?; - let word_count = v8bigint.word_count(); - let mut words: SmallVec<[u64; 1]> = smallvec![0u64; word_count]; - let (sign_bit, _words) = v8bigint.to_words_array(&mut words); - let sign = match sign_bit { - true => num_bigint::Sign::Minus, - false => num_bigint::Sign::Plus, - }; - // SAFETY: Because the alignment of u64 is 8, the alignment of u32 is 4, and - // the size of u64 is 8, the size of u32 is 4, the alignment of u32 is a - // factor of the alignment of u64, and the size of u32 is a factor of the - // size of u64, we can safely transmute the slice of u64 to a slice of u32. - let (prefix, slice, suffix) = unsafe { words.align_to::<u32>() }; - assert!(prefix.is_empty()); - assert!(suffix.is_empty()); - assert_eq!(slice.len(), words.len() * 2); - let big_int = num_bigint::BigInt::from_slice(sign, slice); - Ok(Self(big_int)) - } -} - -impl From<num_bigint::BigInt> for BigInt { - fn from(big_int: num_bigint::BigInt) -> Self { - Self(big_int) - } -} - -impl From<BigInt> for num_bigint::BigInt { - fn from(big_int: BigInt) -> Self { - big_int.0 - } -} diff --git a/serde_v8/magic/buffer.rs b/serde_v8/magic/buffer.rs deleted file mode 100644 index 032a3be33..000000000 --- a/serde_v8/magic/buffer.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::fmt::Debug; -use std::ops::Deref; -use std::ops::DerefMut; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use super::v8slice::V8Slice; -use crate::magic::transl8::impl_magic; - -pub struct JsBuffer(V8Slice); - -impl_magic!(JsBuffer); - -impl Debug for JsBuffer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_list().entries(self.0.as_ref().iter()).finish() - } -} - -impl Clone for JsBuffer { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl AsRef<[u8]> for JsBuffer { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for JsBuffer { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Deref for JsBuffer { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.0 - } -} - -impl DerefMut for JsBuffer { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl FromV8 for JsBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - Ok(Self(V8Slice::from_v8(scope, value)?)) - } -} - -impl From<JsBuffer> for bytes::Bytes { - fn from(zbuf: JsBuffer) -> bytes::Bytes { - zbuf.0.into() - } -} - -// NOTE(bartlomieju): we use Option here, because `to_v8()` uses `&mut self` -// instead of `self` which is dictated by the `serde` API. -#[derive(Debug)] -pub struct ToJsBuffer(Option<Box<[u8]>>); - -impl_magic!(ToJsBuffer); - -impl ToJsBuffer { - pub fn empty() -> Self { - ToJsBuffer(Some(vec![0_u8; 0].into_boxed_slice())) - } -} - -impl From<Box<[u8]>> for ToJsBuffer { - fn from(buf: Box<[u8]>) -> Self { - ToJsBuffer(Some(buf)) - } -} - -impl From<Vec<u8>> for ToJsBuffer { - fn from(vec: Vec<u8>) -> Self { - vec.into_boxed_slice().into() - } -} - -impl ToV8 for ToJsBuffer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - let buf: Box<[u8]> = self.0.take().expect("RustToV8Buf was empty"); - - 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(), - ); - } - let buf_len: usize = 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(), - ) - } -} diff --git a/serde_v8/magic/bytestring.rs b/serde_v8/magic/bytestring.rs deleted file mode 100644 index 3baa704e5..000000000 --- a/serde_v8/magic/bytestring.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; -use smallvec::SmallVec; -use std::mem::size_of; - -const USIZE2X: usize = size_of::<usize>() * 2; - -#[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, -)] -#[as_mut(forward)] -#[as_ref(forward)] -pub struct ByteString(SmallVec<[u8; USIZE2X]>); -impl_magic!(ByteString); - -// const-assert that Vec<u8> and SmallVec<[u8; size_of::<usize>() * 2]> have a same size. -// Note from https://docs.rs/smallvec/latest/smallvec/#union - -// smallvec can still be larger than Vec if the inline buffer is -// larger than two machine words. -const _: () = - assert!(size_of::<Vec<u8>>() == size_of::<SmallVec<[u8; USIZE2X]>>()); - -impl ToV8 for ByteString { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - let v = - v8::String::new_from_one_byte(scope, self, v8::NewStringType::Normal) - .unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for ByteString { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - let v8str = v8::Local::<v8::String>::try_from(value) - .map_err(|_| Error::ExpectedString(value_to_type_str(value)))?; - if !v8str.contains_only_onebyte() { - return Err(Error::ExpectedLatin1); - } - let len = v8str.length(); - let mut buffer = SmallVec::with_capacity(len); - #[allow(clippy::uninit_vec)] - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - unsafe { - buffer.set_len(len); - let written = v8str.write_one_byte( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(Self(buffer)) - } -} - -// smallvec does not impl From/Into traits -// like Vec<u8> does. So here we are. - -impl From<Vec<u8>> for ByteString { - fn from(vec: Vec<u8>) -> Self { - ByteString(SmallVec::from_vec(vec)) - } -} - -#[allow(clippy::from_over_into)] -impl Into<Vec<u8>> for ByteString { - fn into(self) -> Vec<u8> { - self.0.into_vec() - } -} - -impl From<&[u8]> for ByteString { - fn from(s: &[u8]) -> Self { - ByteString(SmallVec::from_slice(s)) - } -} - -impl From<&str> for ByteString { - fn from(s: &str) -> Self { - let v: Vec<u8> = s.into(); - ByteString::from(v) - } -} - -impl From<String> for ByteString { - fn from(s: String) -> Self { - ByteString::from(s.into_bytes()) - } -} diff --git a/serde_v8/magic/detached_buffer.rs b/serde_v8/magic/detached_buffer.rs deleted file mode 100644 index bc4b3de67..000000000 --- a/serde_v8/magic/detached_buffer.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use core::ops::Range; -use std::ops::Deref; -use std::ops::DerefMut; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use super::v8slice::to_ranged_buffer; -use super::v8slice::V8Slice; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; - -// A buffer that detaches when deserialized from JS -pub struct DetachedBuffer(V8Slice); -impl_magic!(DetachedBuffer); - -impl AsRef<[u8]> for DetachedBuffer { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsMut<[u8]> for DetachedBuffer { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl Deref for DetachedBuffer { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0.deref() - } -} - -impl DerefMut for DetachedBuffer { - fn deref_mut(&mut self) -> &mut [u8] { - self.0.deref_mut() - } -} - -impl ToV8 for DetachedBuffer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - let buffer = v8::ArrayBuffer::with_backing_store(scope, &self.0.store); - let Range { start, end } = self.0.range; - let (off, len) = (start, end - start); - let v = v8::Uint8Array::new(scope, buffer, off, len).unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for DetachedBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - let (b, range) = to_ranged_buffer(scope, value) - .map_err(|_| crate::Error::ExpectedBuffer(value_to_type_str(value)))?; - if !b.is_detachable() { - return Err(crate::Error::ExpectedDetachable(value_to_type_str(value))); - } - let store = b.get_backing_store(); - b.detach(None); // Detach - Ok(Self(V8Slice { store, range })) - } -} diff --git a/serde_v8/magic/external_pointer.rs b/serde_v8/magic/external_pointer.rs deleted file mode 100644 index e22e41a01..000000000 --- a/serde_v8/magic/external_pointer.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::ffi::c_void; - -use crate::error::value_to_type_str; - -use super::transl8::impl_magic; -use super::transl8::FromV8; -use super::transl8::ToV8; - -pub struct ExternalPointer(*mut c_void); - -// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right. -unsafe impl Send for ExternalPointer {} -// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right. -unsafe impl Sync for ExternalPointer {} - -impl_magic!(ExternalPointer); - -impl ToV8 for ExternalPointer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - if self.0.is_null() { - Ok(v8::null(scope).into()) - } else { - Ok(v8::External::new(scope, self.0).into()) - } - } -} - -impl FromV8 for ExternalPointer { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - if value.is_null() { - Ok(ExternalPointer(std::ptr::null_mut())) - } else if let Ok(external) = v8::Local::<v8::External>::try_from(value) { - Ok(ExternalPointer(external.value())) - } else { - Err(crate::Error::ExpectedExternal(value_to_type_str(value))) - } - } -} - -impl From<*mut c_void> for ExternalPointer { - fn from(value: *mut c_void) -> Self { - ExternalPointer(value) - } -} - -impl From<*const c_void> for ExternalPointer { - fn from(value: *const c_void) -> Self { - ExternalPointer(value as *mut c_void) - } -} diff --git a/serde_v8/magic/global.rs b/serde_v8/magic/global.rs deleted file mode 100644 index 52b316fa5..000000000 --- a/serde_v8/magic/global.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::magic::transl8::impl_magic; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::ToV8; - -pub struct Global { - pub v8_value: v8::Global<v8::Value>, -} -impl_magic!(Global); - -impl From<v8::Global<v8::Value>> for Global { - fn from(v8_value: v8::Global<v8::Value>) -> Self { - Self { v8_value } - } -} - -impl From<Global> for v8::Global<v8::Value> { - fn from(v: Global) -> Self { - v.v8_value - } -} - -impl ToV8 for Global { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - Ok(v8::Local::new(scope, self.v8_value.clone())) - } -} - -impl FromV8 for Global { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - let global = v8::Global::new(scope, value); - Ok(global.into()) - } -} diff --git a/serde_v8/magic/mod.rs b/serde_v8/magic/mod.rs deleted file mode 100644 index 3e984527d..000000000 --- a/serde_v8/magic/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -pub mod any_value; -pub mod bigint; -pub mod buffer; -pub mod bytestring; -pub mod detached_buffer; -mod external_pointer; -mod global; -pub(super) mod rawbytes; -pub mod string_or_buffer; -pub mod transl8; -pub mod u16string; -pub mod v8slice; -mod value; -pub use external_pointer::ExternalPointer; -pub use global::Global; -pub use value::Value; diff --git a/serde_v8/magic/rawbytes.rs b/serde_v8/magic/rawbytes.rs deleted file mode 100644 index 2703c7756..000000000 --- a/serde_v8/magic/rawbytes.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -pub(crate) type AtomicPtr<T> = *mut T; -#[allow(unused)] -pub(crate) struct RawBytes { - ptr: *const u8, - len: usize, - // inlined "trait object" - data: AtomicPtr<()>, - vtable: &'static Vtable, -} - -impl RawBytes { - pub fn new_raw( - ptr: *const u8, - len: usize, - data: AtomicPtr<()>, - vtable: &'static Vtable, - ) -> bytes::Bytes { - RawBytes { - ptr, - len, - data, - vtable, - } - .into() - } -} - -// Validate some bytes::Bytes layout assumptions at compile time. -const _: () = { - assert!( - core::mem::size_of::<RawBytes>() == core::mem::size_of::<bytes::Bytes>(), - ); - assert!( - core::mem::align_of::<RawBytes>() == core::mem::align_of::<bytes::Bytes>(), - ); -}; - -#[allow(unused)] -pub(crate) struct Vtable { - /// fn(data, ptr, len) - pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> bytes::Bytes, - /// fn(data, ptr, len) - /// - /// takes `Bytes` to value - pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec<u8>, - /// fn(data, ptr, len) - pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize), -} - -impl From<RawBytes> for bytes::Bytes { - fn from(b: RawBytes) -> Self { - // SAFETY: RawBytes has the same layout as bytes::Bytes - // this is tested below, both are composed of usize-d ptrs/values - // thus aren't currently subject to rust's field re-ordering to minimize padding - unsafe { std::mem::transmute(b) } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::mem; - - const HELLO: &str = "hello"; - - // ===== impl StaticVtable ===== - - const STATIC_VTABLE: Vtable = Vtable { - clone: static_clone, - drop: static_drop, - to_vec: static_to_vec, - }; - - unsafe fn static_clone( - _: &AtomicPtr<()>, - ptr: *const u8, - len: usize, - ) -> bytes::Bytes { - from_static(std::slice::from_raw_parts(ptr, len)).into() - } - - unsafe fn static_to_vec( - _: &AtomicPtr<()>, - ptr: *const u8, - len: usize, - ) -> Vec<u8> { - let slice = std::slice::from_raw_parts(ptr, len); - slice.to_vec() - } - - unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { - // nothing to drop for &'static [u8] - } - - fn from_static(bytes: &'static [u8]) -> RawBytes { - RawBytes { - ptr: bytes.as_ptr(), - len: bytes.len(), - data: std::ptr::null_mut(), - vtable: &STATIC_VTABLE, - } - } - - #[test] - fn bytes_identity() { - let b1: bytes::Bytes = from_static(HELLO.as_bytes()).into(); - let b2 = bytes::Bytes::from_static(HELLO.as_bytes()); - assert_eq!(b1, b2); // Values are equal - } - - #[test] - fn bytes_layout() { - let u1: [usize; 4] = - // SAFETY: ensuring layout is the same - unsafe { mem::transmute(from_static(HELLO.as_bytes())) }; - let u2: [usize; 4] = - // SAFETY: ensuring layout is the same - unsafe { mem::transmute(bytes::Bytes::from_static(HELLO.as_bytes())) }; - assert_eq!(u1[..3], u2[..3]); // Struct bytes are equal besides Vtables - } -} diff --git a/serde_v8/magic/string_or_buffer.rs b/serde_v8/magic/string_or_buffer.rs deleted file mode 100644 index 986f7d32a..000000000 --- a/serde_v8/magic/string_or_buffer.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::buffer::JsBuffer; -use super::transl8::FromV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; -use std::ops::Deref; - -#[derive(Debug)] -pub enum StringOrBuffer { - Buffer(JsBuffer), - String(String), -} - -impl_magic!(StringOrBuffer); - -impl Deref for StringOrBuffer { - type Target = [u8]; - fn deref(&self) -> &Self::Target { - match self { - Self::Buffer(b) => b.as_ref(), - Self::String(s) => s.as_bytes(), - } - } -} - -impl<'a> TryFrom<&'a StringOrBuffer> for &'a str { - type Error = std::str::Utf8Error; - fn try_from(value: &'a StringOrBuffer) -> Result<Self, Self::Error> { - match value { - StringOrBuffer::String(s) => Ok(s.as_str()), - StringOrBuffer::Buffer(b) => std::str::from_utf8(b.as_ref()), - } - } -} - -impl FromV8 for StringOrBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - if let Ok(buf) = JsBuffer::from_v8(scope, value) { - return Ok(Self::Buffer(buf)); - } else if let Ok(s) = crate::from_v8(scope, value) { - return Ok(Self::String(s)); - } - Err(Error::ExpectedBuffer(value_to_type_str(value))) - } -} - -impl From<StringOrBuffer> for bytes::Bytes { - fn from(sob: StringOrBuffer) -> Self { - match sob { - StringOrBuffer::Buffer(b) => b.into(), - StringOrBuffer::String(s) => s.into_bytes().into(), - } - } -} diff --git a/serde_v8/magic/transl8.rs b/serde_v8/magic/transl8.rs deleted file mode 100644 index 3a8d0c358..000000000 --- a/serde_v8/magic/transl8.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2018-2023 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>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error>; -} - -pub(crate) trait FromV8: Sized { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error>; -} - -pub(crate) fn magic_serialize<T, S>( - serializer: S, - x: &T, -) -> Result<S::Ok, S::Error> -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<T, D::Error> -where - D: serde::Deserializer<'de>, - T: MagicType, -{ - struct ValueVisitor<T> { - p1: std::marker::PhantomData<T>, - } - - impl<'de, T: MagicType> serde::de::Visitor<'de> for ValueVisitor<T> { - 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<E>(self, ptr: u64) -> Result<Self::Value, E> - 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::<T> { - p1: std::marker::PhantomData, - }, - ) -} - -pub(crate) fn visit_magic<'de, T, V, E>(visitor: V, x: T) -> Result<V::Value, E> -where - V: serde::de::Visitor<'de>, - E: serde::de::Error, -{ - let y = visitor.visit_u64::<E>(opaque_send(&x)); - std::mem::forget(x); - y -} - -/// Constructs an "opaque" ptr from a reference to transerialize -pub(crate) fn opaque_send<T: Sized>(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<T: ?Sized>(ptr: &T) -> u64 { - *(ptr as *const T as *const u64) -} - -/// Transmutes an "opaque" ptr back into a reference -pub(crate) unsafe fn opaque_deref_mut<'a, T>(ptr: u64) -> &'a mut T { - std::mem::transmute(ptr as usize) -} - -/// Transmutes & copies the value from the "opaque" ptr -/// NOTE: takes ownership & requires other end to forget its ownership -pub(crate) unsafe fn opaque_take<T>(ptr: u64) -> T { - std::mem::transmute_copy::<T, T>(std::mem::transmute(ptr as usize)) -} - -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: serde::Serializer, - { - crate::magic::transl8::magic_serialize(serializer, self) - } - } - - impl<'de> serde::Deserialize<'de> for $t { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: serde::Deserializer<'de>, - { - crate::magic::transl8::magic_deserialize(deserializer) - } - } - }; -} -pub(crate) use impl_magic; - -macro_rules! impl_wrapper { - ($i:item) => { - #[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, - derive_more::From, - )] - #[as_mut(forward)] - #[as_ref(forward)] - #[from(forward)] - $i - }; -} -pub(crate) use impl_wrapper; diff --git a/serde_v8/magic/u16string.rs b/serde_v8/magic/u16string.rs deleted file mode 100644 index 04d742da9..000000000 --- a/serde_v8/magic/u16string.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::error::value_to_type_str; -use crate::Error; - -use super::transl8::impl_magic; -use super::transl8::impl_wrapper; -use super::transl8::FromV8; -use super::transl8::ToV8; - -impl_wrapper!( - pub struct U16String(Vec<u16>); -); -impl_magic!(U16String); - -impl ToV8 for U16String { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - let maybe_v = - v8::String::new_from_two_byte(scope, self, v8::NewStringType::Normal); - - // 'new_from_two_byte' can return 'None' if buffer length > kMaxLength. - if let Some(v) = maybe_v { - Ok(v.into()) - } else { - Err(Error::Message(String::from( - "Cannot allocate String from UTF-16: buffer exceeds maximum length.", - ))) - } - } -} - -impl FromV8 for U16String { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - let v8str = v8::Local::<v8::String>::try_from(value) - .map_err(|_| Error::ExpectedString(value_to_type_str(value)))?; - let len = v8str.length(); - let mut buffer = Vec::with_capacity(len); - #[allow(clippy::uninit_vec)] - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - unsafe { - buffer.set_len(len); - let written = v8str.write( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(buffer.into()) - } -} diff --git a/serde_v8/magic/v8slice.rs b/serde_v8/magic/v8slice.rs deleted file mode 100644 index 2b103f1c9..000000000 --- a/serde_v8/magic/v8slice.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::ops::Deref; -use std::ops::DerefMut; -use std::ops::Range; -use std::rc::Rc; - -use crate::error::value_to_type_str; - -use super::rawbytes; -use super::transl8::FromV8; - -/// A V8Slice encapsulates a slice that's been borrowed from a JavaScript -/// ArrayBuffer object. JavaScript objects can normally be garbage collected, -/// but the existence of a V8Slice inhibits this until it is dropped. It -/// behaves much like an Arc<[u8]>. -/// -/// # Cloning -/// Cloning a V8Slice 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 V8Slice { - pub(crate) store: v8::SharedRef<v8::BackingStore>, - pub(crate) range: Range<usize>, -} - -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for V8Slice {} - -impl V8Slice { - fn as_slice(&self) -> &[u8] { - // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, - // it points to a fixed continuous slice of bytes on the heap. - // We assume it's initialized and thus safe to read (though may not contain meaningful data) - unsafe { &*(&self.store[self.range.clone()] as *const _ as *const [u8]) } - } - - fn as_slice_mut(&mut self) -> &mut [u8] { - #[allow(clippy::cast_ref_to_mut)] - // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, - // it points to a fixed continuous slice of bytes on the heap. - // It's safe-ish to mutate concurrently because it can not be - // shrunk/grown/moved/reallocated, thus avoiding dangling refs (unlike a Vec). - // Concurrent writes can't lead to meaningful structural invalidation - // since we treat them as opaque buffers / "bags of bytes", - // concurrent mutation is simply an accepted fact of life. - // And in practice V8Slices also do not have overallping read/write phases. - // TLDR: permissive interior mutability on slices of bytes is "fine" - unsafe { - &mut *(&self.store[self.range.clone()] as *const _ as *mut [u8]) - } - } -} - -pub(crate) fn to_ranged_buffer<'s>( - scope: &mut v8::HandleScope<'s>, - value: v8::Local<v8::Value>, -) -> Result<(v8::Local<'s, v8::ArrayBuffer>, Range<usize>), v8::DataError> { - if let Ok(view) = v8::Local::<v8::ArrayBufferView>::try_from(value) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = view.buffer(scope).ok_or(v8::DataError::NoData { - expected: "view to have a buffer", - })?; - let buffer = v8::Local::new(scope, buffer); // recreate handle to avoid lifetime issues - return Ok((buffer, offset..offset + len)); - } - let b: v8::Local<v8::ArrayBuffer> = value.try_into()?; - let b = v8::Local::new(scope, b); // recreate handle to avoid lifetime issues - Ok((b, 0..b.byte_length())) -} - -impl FromV8 for V8Slice { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - match to_ranged_buffer(scope, value) { - Ok((b, range)) => { - let store = b.get_backing_store(); - if store.is_resizable_by_user_javascript() { - Err(crate::Error::ResizableBackingStoreNotSupported) - } else if store.is_shared() { - Err(crate::Error::ExpectedBuffer(value_to_type_str(value))) - } else { - Ok(V8Slice { store, range }) - } - } - Err(_) => Err(crate::Error::ExpectedBuffer(value_to_type_str(value))), - } - } -} - -impl Deref for V8Slice { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.as_slice() - } -} - -impl DerefMut for V8Slice { - fn deref_mut(&mut self) -> &mut [u8] { - self.as_slice_mut() - } -} - -impl AsRef<[u8]> for V8Slice { - fn as_ref(&self) -> &[u8] { - self.as_slice() - } -} - -impl AsMut<[u8]> for V8Slice { - fn as_mut(&mut self) -> &mut [u8] { - self.as_slice_mut() - } -} - -// Implement V8Slice -> bytes::Bytes -impl V8Slice { - fn rc_into_byte_parts(self: Rc<Self>) -> (*const u8, usize, *mut V8Slice) { - let (ptr, len) = { - let slice = self.as_ref(); - (slice.as_ptr(), slice.len()) - }; - let rc_raw = Rc::into_raw(self); - let data = rc_raw as *mut V8Slice; - (ptr, len, data) - } -} - -impl From<V8Slice> for bytes::Bytes { - fn from(v8slice: V8Slice) -> Self { - let (ptr, len, data) = Rc::new(v8slice).rc_into_byte_parts(); - rawbytes::RawBytes::new_raw(ptr, len, data.cast(), &V8SLICE_VTABLE) - } -} - -// NOTE: in the limit we could avoid extra-indirection and use the C++ shared_ptr -// but we can't store both the underlying data ptr & ctrl ptr ... so instead we -// use a shared rust ptr (Rc/Arc) that itself controls the C++ shared_ptr -const V8SLICE_VTABLE: rawbytes::Vtable = rawbytes::Vtable { - clone: v8slice_clone, - drop: v8slice_drop, - to_vec: v8slice_to_vec, -}; - -unsafe fn v8slice_clone( - data: &rawbytes::AtomicPtr<()>, - ptr: *const u8, - len: usize, -) -> bytes::Bytes { - let rc = Rc::from_raw(*data as *const V8Slice); - let (_, _, data) = rc.clone().rc_into_byte_parts(); - std::mem::forget(rc); - // NOTE: `bytes::Bytes` does bounds checking so we trust its ptr, len inputs - // and must use them to allow cloning Bytes it has sliced - rawbytes::RawBytes::new_raw(ptr, len, data.cast(), &V8SLICE_VTABLE) -} - -unsafe fn v8slice_to_vec( - data: &rawbytes::AtomicPtr<()>, - ptr: *const u8, - len: usize, -) -> Vec<u8> { - let rc = Rc::from_raw(*data as *const V8Slice); - std::mem::forget(rc); - // NOTE: `bytes::Bytes` does bounds checking so we trust its ptr, len inputs - // and must use them to allow cloning Bytes it has sliced - Vec::from_raw_parts(ptr as _, len, len) -} - -unsafe fn v8slice_drop( - data: &mut rawbytes::AtomicPtr<()>, - _: *const u8, - _: usize, -) { - drop(Rc::from_raw(*data as *const V8Slice)) -} diff --git a/serde_v8/magic/value.rs b/serde_v8/magic/value.rs deleted file mode 100644 index 0333d75bc..000000000 --- a/serde_v8/magic/value.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -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 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<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 ToV8 for Value<'_> { - fn to_v8<'a>( - &mut self, - _scope: &mut v8::HandleScope<'a>, - ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute(self.v8_value) }) - } -} - -impl FromV8 for Value<'_> { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local<v8::Value>, - ) -> Result<Self, crate::Error> { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute::<Value, Value>(value.into()) }) - } -} diff --git a/serde_v8/payload.rs b/serde_v8/payload.rs deleted file mode 100644 index b396ad01d..000000000 --- a/serde_v8/payload.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// TODO: maybe add a Payload type that holds scope & v8::Value -// so it can implement Deserialize by itself - -// Classifies v8::Values into sub-types -#[derive(Debug)] -pub enum ValueType { - Null, - Bool, - Number, - BigInt, - String, - Array, - ArrayBuffer, - ArrayBufferView, - 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_big_int() { - return Self::BigInt; - } else if v.is_array_buffer() { - return Self::ArrayBuffer; - } else if v.is_array_buffer_view() { - return Self::ArrayBufferView; - } 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/ser.rs b/serde_v8/ser.rs deleted file mode 100644 index 51625f230..000000000 --- a/serde_v8/ser.rs +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::ser; -use serde::ser::Serialize; - -use std::cell::RefCell; -use std::ops::DerefMut; - -use crate::error::Error; -use crate::error::Result; -use crate::keys::v8_struct_key; -use crate::magic; -use crate::magic::transl8::opaque_deref_mut; -use crate::magic::transl8::opaque_recv; -use crate::magic::transl8::MagicType; -use crate::magic::transl8::ToV8; -use crate::magic::transl8::MAGIC_FIELD; -use crate::AnyValue; -use crate::BigInt; -use crate::ByteString; -use crate::DetachedBuffer; -use crate::ExternalPointer; -use crate::ToJsBuffer; -use crate::U16String; - -type JsValue<'s> = v8::Local<'s, v8::Value>; -type JsResult<'s> = Result<JsValue<'s>>; - -type ScopePtr<'a, 'b, 'c> = &'c RefCell<&'b mut v8::HandleScope<'a>>; - -pub fn to_v8<'a, T>(scope: &mut v8::HandleScope<'a>, input: T) -> JsResult<'a> -where - T: Serialize, -{ - let scopeptr = RefCell::new(scope); - let serializer = Serializer::new(&scopeptr); - - input.serialize(serializer) -} - -/// Wraps other serializers into an enum tagged variant form. -/// Uses {"Variant": ...payload...} for compatibility with serde-json. -pub struct VariantSerializer<'a, 'b, 'c, S> { - inner: S, - scope: ScopePtr<'a, 'b, 'c>, - variant: &'static str, -} - -impl<'a, 'b, 'c, S> VariantSerializer<'a, 'b, 'c, S> { - pub fn new( - scope: ScopePtr<'a, 'b, 'c>, - variant: &'static str, - inner: S, - ) -> Self { - Self { - inner, - scope, - variant, - } - } - - fn end(self, inner: impl FnOnce(S) -> JsResult<'a>) -> JsResult<'a> { - let value = inner(self.inner)?; - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope).into(); - let key = v8_struct_key(scope, self.variant).into(); - let obj = - v8::Object::with_prototype_and_properties(scope, null, &[key], &[value]); - Ok(obj.into()) - } -} - -impl<'a, 'b, 'c, S> ser::SerializeTupleVariant - for VariantSerializer<'a, 'b, 'c, S> -where - S: ser::SerializeTupleStruct<Ok = JsValue<'a>, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field<T: ?Sized + Serialize>( - &mut self, - value: &T, - ) -> Result<()> { - self.inner.serialize_field(value) - } - - fn end(self) -> JsResult<'a> { - self.end(S::end) - } -} - -impl<'a, 'b, 'c, S> ser::SerializeStructVariant - for VariantSerializer<'a, 'b, 'c, S> -where - S: ser::SerializeStruct<Ok = JsValue<'a>, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field<T: ?Sized + Serialize>( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - self.inner.serialize_field(key, value) - } - - fn end(self) -> JsResult<'a> { - self.end(S::end) - } -} - -pub struct ArraySerializer<'a, 'b, 'c> { - pending: Vec<JsValue<'a>>, - scope: ScopePtr<'a, 'b, 'c>, -} - -impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option<usize>) -> Self { - let pending = match len { - Some(len) => Vec::with_capacity(len), - None => vec![], - }; - Self { pending, scope } - } -} - -impl<'a, 'b, 'c> ser::SerializeSeq for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_element<T: ?Sized + Serialize>( - &mut self, - value: &T, - ) -> Result<()> { - let x = value.serialize(Serializer::new(self.scope))?; - self.pending.push(x); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - let elements = self.pending.iter().as_slice(); - let scope = &mut *self.scope.borrow_mut(); - let arr = v8::Array::new_with_elements(scope, elements); - Ok(arr.into()) - } -} - -impl<'a, 'b, 'c> ser::SerializeTuple for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_element<T: ?Sized + Serialize>( - &mut self, - value: &T, - ) -> Result<()> { - ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> JsResult<'a> { - ser::SerializeSeq::end(self) - } -} - -impl<'a, 'b, 'c> ser::SerializeTupleStruct for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field<T: ?Sized + Serialize>( - &mut self, - value: &T, - ) -> Result<()> { - ser::SerializeTuple::serialize_element(self, value) - } - - fn end(self) -> JsResult<'a> { - ser::SerializeTuple::end(self) - } -} - -pub struct ObjectSerializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, - keys: Vec<v8::Local<'a, v8::Name>>, - values: Vec<JsValue<'a>>, -} - -impl<'a, 'b, 'c> ObjectSerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: usize) -> Self { - let keys = Vec::with_capacity(len); - let values = Vec::with_capacity(len); - Self { - scope, - keys, - values, - } - } -} - -impl<'a, 'b, 'c> ser::SerializeStruct for ObjectSerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field<T: ?Sized + Serialize>( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - let value = value.serialize(Serializer::new(self.scope))?; - let scope = &mut *self.scope.borrow_mut(); - let key = v8_struct_key(scope, key).into(); - self.keys.push(key); - self.values.push(value); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope); - let obj = v8::Object::with_prototype_and_properties( - scope, - null.into(), - &self.keys[..], - &self.values[..], - ); - Ok(obj.into()) - } -} - -pub struct MagicalSerializer<'a, 'b, 'c, T> { - scope: ScopePtr<'a, 'b, 'c>, - opaque: u64, - p1: std::marker::PhantomData<T>, -} - -impl<'a, 'b, 'c, T> MagicalSerializer<'a, 'b, 'c, T> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> MagicalSerializer<'a, 'b, 'c, T> { - Self { - scope, - opaque: 0, - p1: std::marker::PhantomData::<T> {}, - } - } -} - -impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct - for MagicalSerializer<'a, 'b, 'c, T> -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field<U: ?Sized + Serialize>( - &mut self, - key: &'static str, - value: &U, - ) -> Result<()> { - assert_eq!(key, MAGIC_FIELD); - let ptr: &U = value; - // SAFETY: MagicalSerializer only ever receives single field u64s, - // type-safety is ensured by MAGIC_NAME checks in `serialize_struct()` - self.opaque = unsafe { opaque_recv(ptr) }; - Ok(()) - } - - fn end(self) -> JsResult<'a> { - // SAFETY: transerialization assumptions imply `T` is still alive. - let x: &mut T = unsafe { opaque_deref_mut(self.opaque) }; - let scope = &mut *self.scope.borrow_mut(); - x.to_v8(scope) - } -} - -// Dispatches between magic and regular struct serializers -pub enum StructSerializers<'a, 'b, 'c> { - ExternalPointer(MagicalSerializer<'a, 'b, 'c, magic::ExternalPointer>), - Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>), - RustToV8Buf(MagicalSerializer<'a, 'b, 'c, ToJsBuffer>), - MagicAnyValue(MagicalSerializer<'a, 'b, 'c, AnyValue>), - MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>), - MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), - MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), - MagicBigInt(MagicalSerializer<'a, 'b, 'c, BigInt>), - 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::ExternalPointer(s) => s.serialize_field(key, value), - StructSerializers::Magic(s) => s.serialize_field(key, value), - StructSerializers::RustToV8Buf(s) => s.serialize_field(key, value), - StructSerializers::MagicAnyValue(s) => s.serialize_field(key, value), - StructSerializers::MagicDetached(s) => s.serialize_field(key, value), - StructSerializers::MagicByteString(s) => s.serialize_field(key, value), - StructSerializers::MagicU16String(s) => s.serialize_field(key, value), - StructSerializers::MagicBigInt(s) => s.serialize_field(key, value), - StructSerializers::Regular(s) => s.serialize_field(key, value), - } - } - - fn end(self) -> JsResult<'a> { - match self { - StructSerializers::ExternalPointer(s) => s.end(), - StructSerializers::Magic(s) => s.end(), - StructSerializers::RustToV8Buf(s) => s.end(), - StructSerializers::MagicAnyValue(s) => s.end(), - StructSerializers::MagicDetached(s) => s.end(), - StructSerializers::MagicByteString(s) => s.end(), - StructSerializers::MagicU16String(s) => s.end(), - StructSerializers::MagicBigInt(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 _) - })* - }; -} - -pub(crate) const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1; -pub(crate) const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER; - -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); - } - - 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_i64(self, v: i64) -> JsResult<'a> { - let s = &mut self.scope.borrow_mut(); - // If i64 can fit in max safe integer bounds then serialize as v8::Number - // otherwise serialize as v8::BigInt - if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&v) { - Ok(v8::Number::new(s, v as _).into()) - } else { - Ok(v8::BigInt::new_from_i64(s, v).into()) - } - } - - fn serialize_u64(self, v: u64) -> JsResult<'a> { - let s = &mut self.scope.borrow_mut(); - // If u64 can fit in max safe integer bounds then serialize as v8::Number - // otherwise serialize as v8::BigInt - if v <= (MAX_SAFE_INTEGER as u64) { - Ok(v8::Number::new(s, v as _).into()) - } else { - Ok(v8::BigInt::new_from_u64(s, v).into()) - } - } - - fn serialize_f64(self, v: f64) -> JsResult<'a> { - let scope = &mut self.scope.borrow_mut(); - Ok(v8::Number::new(scope.deref_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> { - self.serialize_str(&v.to_string()) - } - - fn serialize_str(self, v: &str) -> JsResult<'a> { - let maybe_str = v8::String::new(&mut self.scope.borrow_mut(), v); - - // v8 string can return 'None' if buffer length > kMaxLength. - if let Some(str) = maybe_str { - Ok(str.into()) - } else { - Err(Error::Message(String::from( - "Cannot allocate String: buffer exceeds maximum length.", - ))) - } - } - - fn serialize_bytes(self, v: &[u8]) -> JsResult<'a> { - Ok(slice_to_uint8array(&mut self.scope.borrow_mut(), v).into()) - } - - 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::ExternalPointer::MAGIC_NAME => { - let m = MagicalSerializer::<ExternalPointer>::new(self.scope); - Ok(StructSerializers::ExternalPointer(m)) - } - ByteString::MAGIC_NAME => { - let m = MagicalSerializer::<ByteString>::new(self.scope); - Ok(StructSerializers::MagicByteString(m)) - } - U16String::MAGIC_NAME => { - let m = MagicalSerializer::<U16String>::new(self.scope); - Ok(StructSerializers::MagicU16String(m)) - } - ToJsBuffer::MAGIC_NAME => { - let m = MagicalSerializer::<ToJsBuffer>::new(self.scope); - Ok(StructSerializers::RustToV8Buf(m)) - } - AnyValue::MAGIC_NAME => { - let m = MagicalSerializer::<AnyValue>::new(self.scope); - Ok(StructSerializers::MagicAnyValue(m)) - } - DetachedBuffer::MAGIC_NAME => { - let m = MagicalSerializer::<DetachedBuffer>::new(self.scope); - Ok(StructSerializers::MagicDetached(m)) - } - BigInt::MAGIC_NAME => { - let m = MagicalSerializer::<BigInt>::new(self.scope); - Ok(StructSerializers::MagicBigInt(m)) - } - magic::Value::MAGIC_NAME => { - let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope); - Ok(StructSerializers::Magic(m)) - } - _ => { - // Regular structs - let o = ObjectSerializer::new(self.scope, len); - Ok(StructSerializers::Regular(o)) - } - } - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result<Self::SerializeStructVariant> { - let scope = self.scope; - let x = self.serialize_struct(variant, len)?; - Ok(VariantSerializer::new(scope, variant, x)) - } -} - -pub fn slice_to_uint8array<'a>( - scope: &mut v8::HandleScope<'a>, - buf: &[u8], -) -> v8::Local<'a, v8::Uint8Array> { - let buffer = if buf.is_empty() { - v8::ArrayBuffer::new(scope, 0) - } else { - let store: v8::UniqueRef<_> = - v8::ArrayBuffer::new_backing_store(scope, buf.len()); - // SAFETY: raw memory copy into the v8 ArrayBuffer allocated above - unsafe { - std::ptr::copy_nonoverlapping( - buf.as_ptr(), - store.data().unwrap().as_ptr() as *mut u8, - buf.len(), - ) - } - v8::ArrayBuffer::with_backing_store(scope, &store.make_shared()) - }; - v8::Uint8Array::new(scope, buffer, 0, buf.len()) - .expect("Failed to create UintArray8") -} diff --git a/serde_v8/serializable.rs b/serde_v8/serializable.rs deleted file mode 100644 index 7380ab5a7..000000000 --- a/serde_v8/serializable.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::any::TypeId; -use std::mem::transmute_copy; - -use crate::BigInt; -use crate::ByteString; -use crate::ToJsBuffer; -use crate::U16String; - -/// 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>( - &mut 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>( - &mut 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 primitives (ints/floats/bool/unit/...) -pub enum SerializablePkg { - Primitive(Primitive), - Serializable(Box<dyn Serializable>), -} - -impl SerializablePkg { - pub fn to_v8<'a>( - &mut 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 -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), - String(String), - RustToV8Buf(ToJsBuffer), - ByteString(ByteString), - U16String(U16String), - BigInt(BigInt), -} - -impl serde::Serialize for Primitive { - fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> - where - S: serde::Serializer, - { - match self { - Self::Unit => ().serialize(s), - Self::Bool(x) => x.serialize(s), - Self::Int8(x) => x.serialize(s), - Self::Int16(x) => x.serialize(s), - Self::Int32(x) => x.serialize(s), - Self::Int64(x) => x.serialize(s), - Self::UInt8(x) => x.serialize(s), - Self::UInt16(x) => x.serialize(s), - Self::UInt32(x) => x.serialize(s), - Self::UInt64(x) => x.serialize(s), - Self::Float32(x) => x.serialize(s), - Self::Float64(x) => x.serialize(s), - Self::String(x) => x.serialize(s), - Self::RustToV8Buf(x) => x.serialize(s), - Self::ByteString(x) => x.serialize(s), - Self::U16String(x) => x.serialize(s), - Self::BigInt(x) => x.serialize(s), - } - } -} - -impl<T: serde::Serialize + 'static> From<T> for SerializablePkg { - fn from(x: T) -> Self { - #[inline(always)] - fn tc<T, U>(src: T) -> U { - // SAFETY: the caller has ensured via the TypeId that the T and U types - // are the same. - let x = unsafe { transmute_copy(&src) }; - std::mem::forget(src); - x - } - - let tid = TypeId::of::<T>(); - if tid == TypeId::of::<()>() { - Self::Primitive(Primitive::Unit) - } else if tid == TypeId::of::<bool>() { - Self::Primitive(Primitive::Bool(tc(x))) - } else if tid == TypeId::of::<i8>() { - Self::Primitive(Primitive::Int8(tc(x))) - } else if tid == TypeId::of::<i16>() { - Self::Primitive(Primitive::Int16(tc(x))) - } else if tid == TypeId::of::<i32>() { - Self::Primitive(Primitive::Int32(tc(x))) - } else if tid == TypeId::of::<i64>() { - Self::Primitive(Primitive::Int64(tc(x))) - } else if tid == TypeId::of::<u8>() { - Self::Primitive(Primitive::UInt8(tc(x))) - } else if tid == TypeId::of::<u16>() { - Self::Primitive(Primitive::UInt16(tc(x))) - } else if tid == TypeId::of::<u32>() { - Self::Primitive(Primitive::UInt32(tc(x))) - } else if tid == TypeId::of::<u64>() { - Self::Primitive(Primitive::UInt64(tc(x))) - } else if tid == TypeId::of::<f32>() { - Self::Primitive(Primitive::Float32(tc(x))) - } else if tid == TypeId::of::<f64>() { - Self::Primitive(Primitive::Float64(tc(x))) - } else if tid == TypeId::of::<String>() { - Self::Primitive(Primitive::String(tc(x))) - } else if tid == TypeId::of::<ToJsBuffer>() { - Self::Primitive(Primitive::RustToV8Buf(tc(x))) - } else if tid == TypeId::of::<ByteString>() { - Self::Primitive(Primitive::ByteString(tc(x))) - } else if tid == TypeId::of::<U16String>() { - Self::Primitive(Primitive::U16String(tc(x))) - } else if tid == TypeId::of::<BigInt>() { - Self::Primitive(Primitive::BigInt(tc(x))) - } else { - Self::Serializable(Box::new(x)) - } - } -} diff --git a/serde_v8/tests/de.rs b/serde_v8/tests/de.rs deleted file mode 100644 index 2edfe1bc6..000000000 --- a/serde_v8/tests/de.rs +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; -use serde::Deserializer; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::BigInt; -use serde_v8::ByteString; -use serde_v8::Error; -use serde_v8::JsBuffer; -use serde_v8::U16String; - -#[derive(Debug, Deserialize, PartialEq)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option<String>, -} - -#[derive(Debug, PartialEq, Deserialize)] -enum EnumUnit { - A, - B, - C, -} - -#[derive(Debug, PartialEq, Deserialize)] -enum EnumPayloads { - UInt(u64), - Int(i64), - Float(f64), - Point { x: i64, y: i64 }, - Tuple(bool, i64, ()), -} - -fn dedo( - code: &str, - f: impl FnOnce(&mut v8::HandleScope, v8::Local<v8::Value>), -) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let v = js_exec(scope, code); - - f(scope, v); - }) -} - -macro_rules! decheck { - ($fn_name:ident, $t:ty, $src:expr, $x:ident, $check:expr) => { - #[test] - fn $fn_name() { - #[allow(clippy::bool_assert_comparison)] - dedo($src, |scope, v| { - let rt = serde_v8::from_v8(scope, v); - assert!(rt.is_ok(), "from_v8(\"{}\"): {:?}", $src, rt.err()); - let $x: $t = rt.unwrap(); - $check - }); - } - }; -} - -macro_rules! detest { - ($fn_name:ident, $t:ty, $src:expr, $rust:expr) => { - decheck!($fn_name, $t, $src, t, assert_eq!(t, $rust)); - }; -} - -macro_rules! defail { - ($fn_name:ident, $t:ty, $src:expr, $failcase:expr) => { - #[test] - fn $fn_name() { - #[allow(clippy::bool_assert_comparison)] - dedo($src, |scope, v| { - let rt: serde_v8::Result<$t> = serde_v8::from_v8(scope, v); - let rtstr = format!("{:?}", rt); - let failed_as_expected = $failcase(rt); - assert!( - failed_as_expected, - "expected failure on deserialize(\"{}\"), got: {}", - $src, rtstr - ); - }); - } - }; -} - -detest!(de_option_some, Option<bool>, "true", Some(true)); -detest!(de_option_null, Option<bool>, "null", None); -detest!(de_option_undefined, Option<bool>, "undefined", None); -detest!(de_unit_null, (), "null", ()); -detest!(de_unit_undefined, (), "undefined", ()); -detest!(de_bool, bool, "true", true); -detest!(de_char, char, "'é'", 'é'); -detest!(de_u64, u64, "32", 32); -detest!(de_string, String, "'Hello'", "Hello".to_owned()); -detest!(de_vec_empty, Vec<u64>, "[]", vec![0; 0]); -detest!(de_vec_u64, Vec<u64>, "[1,2,3,4,5]", vec![1, 2, 3, 4, 5]); -detest!( - de_vec_str, - Vec<String>, - "['hello', 'world']", - vec!["hello".to_owned(), "world".to_owned()] -); -detest!( - de_tuple, - (u64, bool, ()), - "[123, true, null]", - (123, true, ()) -); -defail!( - de_tuple_wrong_len_short, - (u64, bool, ()), - "[123, true]", - |e| e == Err(Error::LengthMismatch(2, 3)) -); -defail!( - de_tuple_wrong_len_long, - (u64, bool, ()), - "[123, true, null, 'extra']", - |e| e == Err(Error::LengthMismatch(4, 3)) -); -detest!( - de_mathop, - MathOp, - "({a: 1, b: 3, c: 'ignored'})", - MathOp { - a: 1, - b: 3, - operator: None - } -); - -// Unit enums -detest!(de_enum_unit_a, EnumUnit, "'A'", EnumUnit::A); -detest!(de_enum_unit_so_a, EnumUnit, "new String('A')", EnumUnit::A); -detest!(de_enum_unit_b, EnumUnit, "'B'", EnumUnit::B); -detest!(de_enum_unit_so_b, EnumUnit, "new String('B')", EnumUnit::B); -detest!(de_enum_unit_c, EnumUnit, "'C'", EnumUnit::C); -detest!(de_enum_unit_so_c, EnumUnit, "new String('C')", EnumUnit::C); - -// Enums with payloads (tuples & struct) -detest!( - de_enum_payload_int, - EnumPayloads, - "({ Int: -123 })", - EnumPayloads::Int(-123) -); -detest!( - de_enum_payload_uint, - EnumPayloads, - "({ UInt: 123 })", - EnumPayloads::UInt(123) -); -detest!( - de_enum_payload_float, - EnumPayloads, - "({ Float: 1.23 })", - EnumPayloads::Float(1.23) -); -detest!( - de_enum_payload_point, - EnumPayloads, - "({ Point: { x: 1, y: 2 } })", - EnumPayloads::Point { x: 1, y: 2 } -); -detest!( - de_enum_payload_tuple, - EnumPayloads, - "({ Tuple: [true, 123, null ] })", - EnumPayloads::Tuple(true, 123, ()) -); - -#[test] -fn de_f64() { - dedo("12345.0", |scope, v| { - let x: f64 = serde_v8::from_v8(scope, v).unwrap(); - assert!((x - 12345.0).abs() < f64::EPSILON); - }); -} - -#[test] -fn de_map() { - use std::collections::HashMap; - - dedo("({a: 1, b: 2, c: 3})", |scope, v| { - let map: HashMap<String, u64> = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(map.get("a").cloned(), Some(1)); - assert_eq!(map.get("b").cloned(), Some(2)); - assert_eq!(map.get("c").cloned(), Some(3)); - assert_eq!(map.get("nada"), None); - }) -} - -#[test] -fn de_obj_with_numeric_keys() { - dedo( - r#"({ - lines: { - 100: { - unit: "m" - }, - 200: { - unit: "cm" - } - } -})"#, - |scope, v| { - let json: serde_json::Value = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!( - json.to_string(), - r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - }, - ) -} - -#[test] -fn de_string_or_buffer() { - dedo("'hello'", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }); - - dedo("new Uint8Array([97])", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[97]); - }); - - dedo("new Uint8Array([128])", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[128]); - }); - - dedo( - "(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))", - |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }, - ); -} - -#[test] -fn de_buffers() { - // ArrayBufferView - dedo("new Uint8Array([97])", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[97]); - }); - - // ArrayBuffer - dedo("(new Uint8Array([97])).buffer", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[97]); - }); - - dedo( - "(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))", - |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }, - ); - - dedo("(new ArrayBuffer(4))", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[0x0, 0x0, 0x0, 0x0]); - }); - - dedo("(new ArrayBuffer(8, { maxByteLength: 16}))", |scope, v| { - let result: Result<JsBuffer, Error> = serde_v8::from_v8(scope, v); - matches!(result, Err(Error::ResizableBackingStoreNotSupported)); - }); -} - -// Structs -#[derive(Debug, PartialEq, Deserialize)] -struct StructUnit; - -#[derive(Debug, PartialEq)] -struct StructPayload { - a: u64, - b: u64, -} - -struct StructVisitor; - -impl<'de> serde::de::Visitor<'de> for StructVisitor { - type Value = StructPayload; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct StructPayload") - } - fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> - where - A: serde::de::MapAccess<'de>, - { - let mut payload = StructPayload { a: 0, b: 0 }; - while let Some(key) = map.next_key::<String>()? { - match key.as_ref() { - "a" => payload.a = map.next_value()?, - "b" => payload.b = map.next_value()?, - f => panic!("Unknown field {f}"), - } - } - Ok(payload) - } -} - -detest!(de_unit_struct, StructUnit, "'StructUnit'", StructUnit); - -#[test] -fn de_struct() { - dedo("({ a: 1, b: 2 })", |scope, v| { - let mut de = serde_v8::Deserializer::new(scope, v, None); - let payload = de - .deserialize_struct("StructPayload", &[], StructVisitor) - .unwrap(); - assert_eq!(payload, StructPayload { a: 1, b: 2 }) - }) -} - -#[test] -fn de_struct_hint() { - dedo("({ a: 1, b: 2 })", |scope, v| { - let mut de = serde_v8::Deserializer::new(scope, v, None); - let payload = de - .deserialize_struct("StructPayload", &["a", "b"], StructVisitor) - .unwrap(); - assert_eq!(payload, StructPayload { a: 1, b: 2 }) - }) -} - -//// -// JSON tests: serde_json::Value compatibility -//// - -detest!( - de_json_null, - serde_json::Value, - "null", - serde_json::Value::Null -); -detest!( - de_json_bool, - serde_json::Value, - "true", - serde_json::Value::Bool(true) -); -detest!( - de_json_int, - serde_json::Value, - "123", - serde_json::Value::Number(serde_json::Number::from(123)) -); -detest!( - de_json_float, - serde_json::Value, - "123.45", - serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap()) -); -detest!( - de_json_string, - serde_json::Value, - "'Hello'", - serde_json::Value::String("Hello".to_string()) -); -detest!( - de_json_vec_string, - serde_json::Value, - "['Hello', 'World']", - serde_json::Value::Array(vec![ - serde_json::Value::String("Hello".to_string()), - serde_json::Value::String("World".to_string()) - ]) -); -detest!( - de_json_tuple, - serde_json::Value, - "[true, 'World', 123.45, null]", - serde_json::Value::Array(vec![ - serde_json::Value::Bool(true), - serde_json::Value::String("World".to_string()), - serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap()), - serde_json::Value::Null, - ]) -); -detest!( - de_json_object, - serde_json::Value, - "({a: 1, b: 'hello', c: true})", - serde_json::json!({ - "a": 1, - "b": "hello", - "c": true, - }) -); -detest!( - de_json_object_from_map, - serde_json::Value, - "(new Map([['a', 1], ['b', 'hello'], ['c', true]]))", - serde_json::json!({ - "a": 1, - "b": "hello", - "c": true, - }) -); -// TODO: this is not optimal, ideally we'd get an array of [1,2,3] instead. -// Fixing that will require exposing Set::AsArray in the v8 bindings. -detest!( - de_json_object_from_set, - serde_json::Value, - "(new Set([1, 2, 3]))", - serde_json::json!({}) -); - -defail!(defail_struct, MathOp, "123", |e| e - == Err(Error::ExpectedObject("Number"))); - -#[derive(Eq, PartialEq, Debug, Deserialize)] -pub struct SomeThing { - pub a: String, - #[serde(default)] - pub b: String, -} -detest!( - de_struct_defaults, - SomeThing, - "({ a: 'hello' })", - SomeThing { - a: "hello".into(), - b: "".into() - } -); - -detest!(de_bstr, ByteString, "'hello'", "hello".into()); -defail!(defail_bstr, ByteString, "'👋bye'", |e| e - == Err(Error::ExpectedLatin1)); - -#[derive(Eq, PartialEq, Debug, Deserialize)] -pub struct StructWithBytes { - #[serde(with = "serde_bytes")] - a: Vec<u8>, - #[serde(with = "serde_bytes")] - b: Vec<u8>, - #[serde(with = "serde_bytes")] - c: Vec<u8>, -} -detest!( - de_struct_with_bytes, - StructWithBytes, - "({ a: new Uint8Array([1, 2]), b: (new Uint8Array([3 , 4])).buffer, c: (new Uint32Array([0])).buffer})", - StructWithBytes { - a: vec![1, 2], - b: vec![3, 4], - c: vec![0, 0, 0, 0], - } -); -detest!( - de_u16str, - U16String, - "'hello'", - "hello".encode_utf16().collect::<Vec<_>>().into() -); -detest!( - de_u16str_non_latin1, - U16String, - "'👋bye'", - "👋bye".encode_utf16().collect::<Vec<_>>().into() -); - -// NaN -detest!(de_nan_u8, u8, "NaN", 0); -detest!(de_nan_u16, u16, "NaN", 0); -detest!(de_nan_u32, u32, "NaN", 0); -detest!(de_nan_u64, u64, "NaN", 0); -detest!(de_nan_i8, i8, "NaN", 0); -detest!(de_nan_i16, i16, "NaN", 0); -detest!(de_nan_i32, i32, "NaN", 0); -detest!(de_nan_i64, i64, "NaN", 0); -decheck!(de_nan_f32, f32, "NaN", t, assert!(t.is_nan())); -decheck!(de_nan_f64, f64, "NaN", t, assert!(t.is_nan())); - -// Infinity -detest!(de_inf_u8, u8, "Infinity", u8::MAX); -detest!(de_inf_u16, u16, "Infinity", u16::MAX); -detest!(de_inf_u32, u32, "Infinity", u32::MAX); -detest!(de_inf_u64, u64, "Infinity", u64::MAX); -detest!(de_inf_i8, i8, "Infinity", i8::MAX); -detest!(de_inf_i16, i16, "Infinity", i16::MAX); -detest!(de_inf_i32, i32, "Infinity", i32::MAX); -detest!(de_inf_i64, i64, "Infinity", i64::MAX); -detest!(de_inf_f32, f32, "Infinity", f32::INFINITY); -detest!(de_inf_f64, f64, "Infinity", f64::INFINITY); - -// -Infinity -detest!(de_neg_inf_u8, u8, "-Infinity", u8::MIN); -detest!(de_neg_inf_u16, u16, "-Infinity", u16::MIN); -detest!(de_neg_inf_u32, u32, "-Infinity", u32::MIN); -detest!(de_neg_inf_u64, u64, "-Infinity", u64::MIN); -detest!(de_neg_inf_i8, i8, "-Infinity", i8::MIN); -detest!(de_neg_inf_i16, i16, "-Infinity", i16::MIN); -detest!(de_neg_inf_i32, i32, "-Infinity", i32::MIN); -detest!(de_neg_inf_i64, i64, "-Infinity", i64::MIN); -detest!(de_neg_inf_f32, f32, "-Infinity", f32::NEG_INFINITY); -detest!(de_neg_inf_f64, f64, "-Infinity", f64::NEG_INFINITY); - -// BigInt to f32/f64 max/min -detest!( - de_bigint_f64_max, - f64, - "BigInt(1.7976931348623157e+308)", - f64::MAX -); -detest!( - de_bigint_f64_min, - f64, - "BigInt(-1.7976931348623157e+308)", - f64::MIN -); -detest!(de_bigint_f32_max, f32, "BigInt(3.40282347e38)", f32::MAX); -detest!(de_bigint_f32_min, f32, "BigInt(-3.40282347e38)", f32::MIN); -// BigInt to f32/f64 saturating to inf -detest!( - de_bigint_f64_inf, - f64, - "(BigInt(1.7976931348623157e+308)*BigInt(100))", - f64::INFINITY -); -detest!( - de_bigint_f64_neg_inf, - f64, - "(BigInt(-1.7976931348623157e+308)*BigInt(100))", - f64::NEG_INFINITY -); - -detest!( - de_bigint_f32_inf, - f32, - "BigInt(1.7976931348623157e+308)", - f32::INFINITY -); -detest!( - de_bigint_f32_neg_inf, - f32, - "BigInt(-1.7976931348623157e+308)", - f32::NEG_INFINITY -); - -// BigInt to BigInt -detest!( - de_bigint_var_u8, - BigInt, - "255n", - num_bigint::BigInt::from(255u8).into() -); -detest!( - de_bigint_var_i8, - BigInt, - "-128n", - num_bigint::BigInt::from(-128i8).into() -); -detest!( - de_bigint_var_u16, - BigInt, - "65535n", - num_bigint::BigInt::from(65535u16).into() -); -detest!( - de_bigint_var_i16, - BigInt, - "-32768n", - num_bigint::BigInt::from(-32768i16).into() -); -detest!( - de_bigint_var_u32, - BigInt, - "4294967295n", - num_bigint::BigInt::from(4294967295u32).into() -); -detest!( - de_bigint_var_i32, - BigInt, - "-2147483648n", - num_bigint::BigInt::from(-2147483648i32).into() -); -detest!( - de_bigint_var_u64, - BigInt, - "18446744073709551615n", - num_bigint::BigInt::from(18446744073709551615u64).into() -); -detest!( - de_bigint_var_i64, - BigInt, - "-9223372036854775808n", - num_bigint::BigInt::from(-9223372036854775808i64).into() -); -detest!( - de_bigint_var_u128, - BigInt, - "340282366920938463463374607431768211455n", - num_bigint::BigInt::from(340282366920938463463374607431768211455u128).into() -); -detest!( - de_bigint_var_i128, - BigInt, - "-170141183460469231731687303715884105728n", - num_bigint::BigInt::from(-170141183460469231731687303715884105728i128).into() -); diff --git a/serde_v8/tests/magic.rs b/serde_v8/tests/magic.rs deleted file mode 100644 index e3ed1d330..000000000 --- a/serde_v8/tests/magic.rs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; -use serde::Serialize; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::Result; - -#[derive(Deserialize)] -struct MagicOp<'s> { - #[allow(unused)] - pub a: u64, - #[allow(unused)] - pub b: u64, - pub c: serde_v8::Value<'s>, - #[allow(unused)] - pub operator: Option<String>, -} - -#[derive(Serialize)] -struct MagicContainer<'s> { - pub magic: bool, - pub contains: serde_v8::Value<'s>, -} - -#[test] -fn magic_basic() { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - // Decode - let v = js_exec(scope, "({a: 1, b: 3, c: 'abracadabra'})"); - let mop: MagicOp = serde_v8::from_v8(scope, v).unwrap(); - // Check string - let v8_value: v8::Local<v8::Value> = mop.c.into(); - let vs = v8::Local::<v8::String>::try_from(v8_value).unwrap(); - let s = vs.to_rust_string_lossy(scope); - assert_eq!(s, "abracadabra"); - - // Encode - let container = MagicContainer { - magic: true, - contains: v.into(), - }; - let vc = serde_v8::to_v8(scope, container).unwrap(); - // JSON stringify & check - let json = v8::json::stringify(scope, vc).unwrap(); - let s2 = json.to_rust_string_lossy(scope); - assert_eq!( - s2, - r#"{"magic":true,"contains":{"a":1,"b":3,"c":"abracadabra"}}"# - ); - }) -} - -#[test] -fn magic_buffer() { - v8_do(|| { - // Init isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let global = context.global(scope); - - // Simple buffer - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: serde_v8::JsBuffer = serde_v8::from_v8(scope, v8_array).unwrap(); - assert_eq!(&*zbuf, &[1, 2, 3, 4, 5]); - - // Multi buffers - let v8_arrays = - js_exec(scope, "[new Uint8Array([1,2]), new Uint8Array([3,4,5])]"); - let (z1, z2): (serde_v8::JsBuffer, serde_v8::JsBuffer) = - serde_v8::from_v8(scope, v8_arrays).unwrap(); - assert_eq!(&*z1, &[1, 2]); - assert_eq!(&*z2, &[3, 4, 5]); - - // Wrapped in option, like our current op-ABI - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: Option<serde_v8::JsBuffer> = - serde_v8::from_v8(scope, v8_array).unwrap(); - assert_eq!(&*zbuf.unwrap(), &[1, 2, 3, 4, 5]); - - // Observe mutation in JS - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let mut zbuf: serde_v8::JsBuffer = - serde_v8::from_v8(scope, v8_array).unwrap(); - let key = serde_v8::to_v8(scope, "t1").unwrap(); - global.set(scope, key, v8_array); - (&mut *zbuf)[2] = 42; - let eq = js_exec(scope, "t1[2] === 42"); - assert!(eq.is_true()); - - // Shared buffers - let v8_array = - js_exec(scope, "new Uint8Array(new SharedArrayBuffer([1,2,3,4,5]))"); - let zbuf: Result<serde_v8::JsBuffer> = serde_v8::from_v8(scope, v8_array); - assert!(zbuf.is_err()); - - // Serialization - let buf: Vec<u8> = vec![1, 2, 3, 99, 5]; - let zbuf: serde_v8::ToJsBuffer = buf.into(); - let v8_value = serde_v8::to_v8(scope, zbuf).unwrap(); - let key = serde_v8::to_v8(scope, "t2").unwrap(); - global.set(scope, key, v8_value); - let eq = js_exec(scope, "t2[3] === 99"); - assert!(eq.is_true()); - - // Composite Serialization - #[derive(serde::Serialize)] - struct Wrapper { - a: serde_v8::ToJsBuffer, - b: serde_v8::ToJsBuffer, - } - let buf1: Vec<u8> = vec![1, 2, 33, 4, 5]; - let buf2: Vec<u8> = vec![5, 4, 3, 2, 11]; - let wrapped = Wrapper { - a: buf1.into(), - b: buf2.into(), - }; - let v8_value = serde_v8::to_v8(scope, wrapped).unwrap(); - let key = serde_v8::to_v8(scope, "t3").unwrap(); - global.set(scope, key, v8_value); - let eq = js_exec(scope, "t3.a[2] === 33"); - assert!(eq.is_true()); - let eq = js_exec(scope, "t3.b[4] === 11"); - assert!(eq.is_true()); - - // JsBuffer as bytes::Bytes - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: serde_v8::JsBuffer = serde_v8::from_v8(scope, v8_array).unwrap(); - let buf: bytes::Bytes = zbuf.into(); - assert_eq!(buf, bytes::Bytes::from_static(&[1, 2, 3, 4, 5])); - assert_eq!(buf, bytes::Bytes::from_static(&[1, 2, 3, 4, 5])); - assert_eq!(buf.slice(0..2), bytes::Bytes::from_static(&[1, 2])); - assert_eq!(buf.slice(2..), bytes::Bytes::from_static(&[3, 4, 5])); - // We're specifically testing that slices are preserved post-clone - #[allow(clippy::redundant_clone)] - let buf2 = buf.slice(2..).clone(); - assert_eq!(buf2, bytes::Bytes::from_static(&[3, 4, 5])); - }) -} - -#[test] -fn magic_byte_string() { - v8_do(|| { - // Init isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let global = context.global(scope); - - // JS string to ByteString - let v8_string = js_exec(scope, "'test \\0\\t\\n\\r\\x7F\\x80áþÆñ'"); - let rust_reflex: serde_v8::ByteString = - serde_v8::from_v8(scope, v8_string).unwrap(); - assert_eq!( - rust_reflex.as_slice(), - b"test \0\t\n\r\x7F\x80\xE1\xFE\xC6\xF1" - ); - - // Non-Latin-1 characters - let v8_string = js_exec(scope, "'日本語'"); - let rust_reflex: Result<serde_v8::ByteString> = - serde_v8::from_v8(scope, v8_string); - assert!(rust_reflex.is_err()); - - // Windows-1252 characters that aren't Latin-1 - let v8_string = js_exec(scope, "'œ'"); - let rust_reflex: Result<serde_v8::ByteString> = - serde_v8::from_v8(scope, v8_string); - assert!(rust_reflex.is_err()); - - // ByteString to JS string - let expected = "a\x00sf:~\x7Fá\u{009C}þ\u{008A}"; - let buf: Vec<u8> = b"a\x00sf:~\x7F\xE1\x9C\xFE\x8A".as_ref().into(); - let zbuf = serde_v8::ByteString::from(buf); - let v8_value = serde_v8::to_v8(scope, zbuf).unwrap(); - let key = serde_v8::to_v8(scope, "actual").unwrap(); - global.set(scope, key, v8_value); - let v8_value_expected = serde_v8::to_v8(scope, expected).unwrap(); - let key_expected = serde_v8::to_v8(scope, "expected").unwrap(); - global.set(scope, key_expected, v8_value_expected); - let eq = js_exec(scope, "actual === expected"); - assert!(eq.is_true()); - }) -} diff --git a/serde_v8/tests/ser.rs b/serde_v8/tests/ser.rs deleted file mode 100644 index b61a758f9..000000000 --- a/serde_v8/tests/ser.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Serialize; -use serde_json::json; -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::BigInt; - -#[derive(Debug, Serialize, PartialEq)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option<String>, -} - -// Utility JS code (obj equality, etc...) -const JS_UTILS: &str = r#" -// Shallow obj equality (don't use deep objs for now) -function objEqual(a, b) { - const ka = Object.keys(a); - const kb = Object.keys(b); - return ka.length === kb.length && ka.every(k => a[k] === b[k]); -} - -function arrEqual(a, b) { - return a.length === b.length && a.every((v, i) => v === b[i]); -} -"#; -const JS_POLLUTE: &str = r#" -Object.defineProperty(Array.prototype, "0", { - set: function (v) { - throw new Error("Polluted Array 0 set"); - }, -}); - -Object.defineProperty(Object.prototype, "a", { - set: (v) => { - throw new Error("Polluted Object 'a' set"); - } -}); -"#; - -fn sercheck<T: Serialize>(val: T, code: &str, pollute: bool) -> bool { - let mut equal = false; - - v8_do(|| { - // Setup isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - // Load util functions - js_exec(scope, JS_UTILS); - if pollute { - js_exec(scope, JS_POLLUTE); - } - // TryCatch scope (to catch pollution exceptions) - let scope = &mut v8::TryCatch::new(scope); - - // Set value as "x" in global scope - let global = context.global(scope); - let v8_key = serde_v8::to_v8(scope, "x").unwrap(); - let v8_val = serde_v8::to_v8(scope, val).unwrap(); - global.set(scope, v8_key, v8_val); - - // Pollution check - if let Some(message) = scope.message() { - let msg = message.get(scope).to_rust_string_lossy(scope); - panic!("JS Exception: {msg}"); - } - - // Execute equality check in JS (e.g: x == ...) - let v = js_exec(scope, code); - // Cast to bool - equal = serde_v8::from_v8(scope, v).unwrap(); - }); - - equal -} - -macro_rules! sertest { - ($fn_name:ident, $rust:expr, $src:expr) => { - #[test] - fn $fn_name() { - assert!( - sercheck($rust, $src, false), - "Expected: {} where x={:?}", - $src, - $rust, - ); - } - }; -} - -macro_rules! sertest_polluted { - ($fn_name:ident, $rust:expr, $src:expr) => { - #[test] - fn $fn_name() { - assert!( - sercheck($rust, $src, true), - "Expected: {} where x={:?}", - $src, - $rust, - ); - } - }; -} - -sertest!(ser_char, 'é', "x === 'é'"); -sertest!(ser_option_some, Some(true), "x === true"); -sertest!(ser_option_null, None as Option<bool>, "x === null"); -sertest!(ser_unit_null, (), "x === null"); -sertest!(ser_bool, true, "x === true"); -sertest!(ser_u64, 9007199254740991_u64, "x === 9007199254740991"); -sertest!(ser_big_int, 9007199254740992_i64, "x === 9007199254740992n"); -sertest!( - ser_neg_big_int, - -9007199254740992_i64, - "x === -9007199254740992n" -); -sertest!(ser_f64, 12345.0, "x === 12345.0"); -sertest!(ser_string, "Hello", "x === 'Hello'"); -sertest!(ser_bytes, b"\x01\x02\x03", "arrEqual(x, [1, 2, 3])"); -sertest!(ser_vec_u64, vec![1, 2, 3, 4, 5], "arrEqual(x, [1,2,3,4,5])"); -sertest!( - ser_vec_string, - vec!["hello", "world"], - "arrEqual(x, ['hello', 'world'])" -); -sertest!(ser_tuple, (123, true, ()), "arrEqual(x, [123, true, null])"); -sertest!( - ser_mathop, - MathOp { - a: 1, - b: 3, - operator: None - }, - "objEqual(x, {a: 1, b: 3, operator: null})" -); - -sertest!( - ser_bigint_u8, - BigInt::from(num_bigint::BigInt::from(255_u8)), - "x === 255n" -); -sertest!( - ser_bigint_i8, - BigInt::from(num_bigint::BigInt::from(-128_i8)), - "x === -128n" -); -sertest!( - ser_bigint_u16, - BigInt::from(num_bigint::BigInt::from(65535_u16)), - "x === 65535n" -); -sertest!( - ser_bigint_i16, - BigInt::from(num_bigint::BigInt::from(-32768_i16)), - "x === -32768n" -); -sertest!( - ser_bigint_u32, - BigInt::from(num_bigint::BigInt::from(4294967295_u32)), - "x === 4294967295n" -); -sertest!( - ser_bigint_i32, - BigInt::from(num_bigint::BigInt::from(-2147483648_i32)), - "x === -2147483648n" -); -sertest!( - ser_bigint_u64, - BigInt::from(num_bigint::BigInt::from(9007199254740991_u64)), - "x === 9007199254740991n" -); -sertest!( - ser_bigint_i64, - BigInt::from(num_bigint::BigInt::from(-9007199254740991_i64)), - "x === -9007199254740991n" -); -sertest!( - ser_bigint_u128, - BigInt::from(num_bigint::BigInt::from( - 340282366920938463463374607431768211455_u128 - )), - "x === 340282366920938463463374607431768211455n" -); -sertest!( - ser_bigint_i128, - BigInt::from(num_bigint::BigInt::from( - -170141183460469231731687303715884105728_i128 - )), - "x === -170141183460469231731687303715884105728n" -); - -sertest!( - ser_map, - { - let map: std::collections::BTreeMap<&str, u32> = - vec![("a", 1), ("b", 2), ("c", 3)].drain(..).collect(); - map - }, - "objEqual(x, {a: 1, b: 2, c: 3})" -); - -//// -// JSON tests: json!() compatibility -//// -sertest!(ser_json_bool, json!(true), "x === true"); -sertest!(ser_json_null, json!(null), "x === null"); -sertest!( - ser_json_int, - json!(9007199254740991_u64), - "x === 9007199254740991" -); -sertest!( - ser_json_big_int, - json!(9007199254740992_i64), - "x === 9007199254740992n" -); -sertest!( - ser_json_neg_big_int, - json!(-9007199254740992_i64), - "x === -9007199254740992n" -); -sertest!(ser_json_f64, json!(123.45), "x === 123.45"); -sertest!(ser_json_string, json!("Hello World"), "x === 'Hello World'"); -sertest!(ser_json_obj_empty, json!({}), "objEqual(x, {})"); -sertest!( - ser_json_obj, - json!({"a": 1, "b": 2, "c": true}), - "objEqual(x, {a: 1, b: 2, c: true})" -); -sertest!( - ser_json_vec_int, - json!([1, 2, 3, 4, 5]), - "arrEqual(x, [1,2,3,4,5])" -); -sertest!( - ser_json_vec_string, - json!(["Goodbye", "Dinosaurs 👋☄️"]), - "arrEqual(x, ['Goodbye', 'Dinosaurs 👋☄️'])" -); -sertest!( - ser_json_tuple, - json!([true, 42, "nabla"]), - "arrEqual(x, [true, 42, 'nabla'])" -); - -//// -// Pollution tests -//// - -sertest_polluted!( - ser_polluted_obj, - MathOp { - a: 1, - b: 2, - operator: None - }, - "objEqual(x, { a: 1, b: 2, operator: null })" -); - -sertest_polluted!( - ser_polluted_tuple, - (true, 123, false), - "arrEqual(x, [true, 123, false])" -); - -sertest_polluted!(ser_polluted_vec, vec![1, 2, 3], "arrEqual(x, [1, 2, 3])"); diff --git a/serde_v8/utils.rs b/serde_v8/utils.rs deleted file mode 100644 index 6a9732400..000000000 --- a/serde_v8/utils.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -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() { - // SAFETY: this is safe, because all isolates have been shut down already. - unsafe { - v8::V8::dispose(); - } - v8::V8::dispose_platform(); -} - -pub fn v8_do(f: impl FnOnce()) { - static V8_INIT: Once = Once::new(); - V8_INIT.call_once(|| { - v8_init(); - }); - f(); - // v8_shutdown(); -} |