From 6516130b0138ef382a0588f983287fb463222086 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Thu, 24 Mar 2022 11:23:40 +0100 Subject: chore: drop src/ in bench_util & serde_v8 (#14097) To align with conventions used in our other crates --- .github/workflows/ci.yml | 6 +- bench_util/Cargo.toml | 3 +- bench_util/js_runtime.rs | 120 +++++++ bench_util/lib.rs | 7 + bench_util/profiling.rs | 82 +++++ bench_util/src/js_runtime.rs | 120 ------- bench_util/src/lib.rs | 7 - bench_util/src/profiling.rs | 82 ----- serde_v8/Cargo.toml | 3 + serde_v8/de.rs | 614 +++++++++++++++++++++++++++++++++ serde_v8/error.rs | 48 +++ serde_v8/keys.rs | 31 ++ serde_v8/lib.rs | 20 ++ serde_v8/magic/buffer.rs | 119 +++++++ serde_v8/magic/bytestring.rs | 122 +++++++ serde_v8/magic/mod.rs | 9 + serde_v8/magic/string_or_buffer.rs | 45 +++ serde_v8/magic/transl8.rs | 143 ++++++++ serde_v8/magic/u16string.rs | 83 +++++ serde_v8/magic/value.rs | 48 +++ serde_v8/magic/zero_copy_buf.rs | 136 ++++++++ serde_v8/payload.rs | 40 +++ serde_v8/ser.rs | 554 +++++++++++++++++++++++++++++ serde_v8/serializable.rs | 142 ++++++++ serde_v8/src/de.rs | 614 --------------------------------- serde_v8/src/error.rs | 48 --- serde_v8/src/keys.rs | 31 -- serde_v8/src/lib.rs | 20 -- serde_v8/src/magic/buffer.rs | 119 ------- serde_v8/src/magic/bytestring.rs | 122 ------- serde_v8/src/magic/mod.rs | 9 - serde_v8/src/magic/string_or_buffer.rs | 45 --- serde_v8/src/magic/transl8.rs | 143 -------- serde_v8/src/magic/u16string.rs | 83 ----- serde_v8/src/magic/value.rs | 48 --- serde_v8/src/magic/zero_copy_buf.rs | 136 -------- serde_v8/src/payload.rs | 40 --- serde_v8/src/ser.rs | 554 ----------------------------- serde_v8/src/serializable.rs | 142 -------- serde_v8/src/utils.rs | 33 -- serde_v8/utils.rs | 33 ++ 41 files changed, 2404 insertions(+), 2400 deletions(-) create mode 100644 bench_util/js_runtime.rs create mode 100644 bench_util/lib.rs create mode 100644 bench_util/profiling.rs delete mode 100644 bench_util/src/js_runtime.rs delete mode 100644 bench_util/src/lib.rs delete mode 100644 bench_util/src/profiling.rs create mode 100644 serde_v8/de.rs create mode 100644 serde_v8/error.rs create mode 100644 serde_v8/keys.rs create mode 100644 serde_v8/lib.rs create mode 100644 serde_v8/magic/buffer.rs create mode 100644 serde_v8/magic/bytestring.rs create mode 100644 serde_v8/magic/mod.rs create mode 100644 serde_v8/magic/string_or_buffer.rs create mode 100644 serde_v8/magic/transl8.rs create mode 100644 serde_v8/magic/u16string.rs create mode 100644 serde_v8/magic/value.rs create mode 100644 serde_v8/magic/zero_copy_buf.rs create mode 100644 serde_v8/payload.rs create mode 100644 serde_v8/ser.rs create mode 100644 serde_v8/serializable.rs delete mode 100644 serde_v8/src/de.rs delete mode 100644 serde_v8/src/error.rs delete mode 100644 serde_v8/src/keys.rs delete mode 100644 serde_v8/src/lib.rs delete mode 100644 serde_v8/src/magic/buffer.rs delete mode 100644 serde_v8/src/magic/bytestring.rs delete mode 100644 serde_v8/src/magic/mod.rs delete mode 100644 serde_v8/src/magic/string_or_buffer.rs delete mode 100644 serde_v8/src/magic/transl8.rs delete mode 100644 serde_v8/src/magic/u16string.rs delete mode 100644 serde_v8/src/magic/value.rs delete mode 100644 serde_v8/src/magic/zero_copy_buf.rs delete mode 100644 serde_v8/src/payload.rs delete mode 100644 serde_v8/src/ser.rs delete mode 100644 serde_v8/src/serializable.rs delete mode 100644 serde_v8/src/utils.rs create mode 100644 serde_v8/utils.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63a9389db..d0c6a0030 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,7 +236,7 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: 3-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} + key: 4-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} # In main branch, always creates fresh cache - name: Cache build output (main) @@ -252,7 +252,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: | - 3-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} + 4-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} # Restore cache from the latest 'main' branch build. - name: Cache build output (PR) @@ -268,7 +268,7 @@ jobs: !./target/*/*.tar.gz key: never_saved restore-keys: | - 3-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- + 4-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- # Don't save cache after building PRs or branches other than 'main'. - name: Skip save cache (PR) diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index f43436c06..acc7cb22f 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -10,7 +10,8 @@ readme = "README.md" repository = "https://github.com/denoland/deno" description = "Bench and profiling utilities for deno crates" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +path = "lib.rs" [dependencies] bencher = "0.1" diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs new file mode 100644 index 000000000..06dd79fae --- /dev/null +++ b/bench_util/js_runtime.rs @@ -0,0 +1,120 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use bencher::Bencher; +use deno_core::v8; +use deno_core::Extension; +use deno_core::JsRuntime; +use deno_core::RuntimeOptions; + +use crate::profiling::is_profiling; + +pub fn create_js_runtime(setup: impl FnOnce() -> Vec) -> JsRuntime { + JsRuntime::new(RuntimeOptions { + extensions: setup(), + ..Default::default() + }) +} + +fn loop_code(iters: u64, src: &str) -> String { + format!(r#"for(let i=0; i < {}; i++) {{ {} }}"#, iters, src,) +} + +#[derive(Copy, Clone)] +pub struct BenchOptions { + pub benching_inner: u64, + pub profiling_inner: u64, + pub profiling_outer: u64, +} + +impl Default for BenchOptions { + fn default() -> Self { + Self { + benching_inner: 1_000, + profiling_inner: 1_000, + profiling_outer: 10_000, + } + } +} + +pub fn bench_js_sync( + b: &mut Bencher, + src: &str, + setup: impl FnOnce() -> Vec, +) { + bench_js_sync_with(b, src, setup, Default::default()) +} + +pub fn bench_js_sync_with( + b: &mut Bencher, + src: &str, + setup: impl FnOnce() -> Vec, + opts: BenchOptions, +) { + let mut runtime = create_js_runtime(setup); + let scope = &mut runtime.handle_scope(); + + // Increase JS iterations if profiling for nicer flamegraphs + let inner_iters = if is_profiling() { + opts.profiling_inner * opts.profiling_outer + } else { + opts.benching_inner + }; + // Looped code + let looped_src = loop_code(inner_iters, src); + + let code = v8::String::new(scope, looped_src.as_ref()).unwrap(); + let script = v8::Script::compile(scope, code, None).unwrap(); + + // Run once if profiling, otherwise regular bench loop + if is_profiling() { + script.run(scope).unwrap(); + } else { + b.iter(|| { + script.run(scope).unwrap(); + }); + } +} + +pub fn bench_js_async( + b: &mut Bencher, + src: &str, + setup: impl FnOnce() -> Vec, +) { + bench_js_async_with(b, src, setup, Default::default()) +} + +pub fn bench_js_async_with( + b: &mut Bencher, + src: &str, + setup: impl FnOnce() -> Vec, + opts: BenchOptions, +) { + let mut runtime = create_js_runtime(setup); + let tokio_runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + // Looped code + let inner_iters = if is_profiling() { + opts.profiling_inner + } else { + opts.benching_inner + }; + let looped = loop_code(inner_iters, src); + let src = looped.as_ref(); + + if is_profiling() { + for _ in 0..opts.profiling_outer { + tokio_runtime.block_on(inner_async(src, &mut runtime)); + } + } else { + b.iter(|| { + tokio_runtime.block_on(inner_async(src, &mut runtime)); + }); + } +} + +async fn inner_async(src: &str, runtime: &mut JsRuntime) { + runtime.execute_script("inner_loop", src).unwrap(); + runtime.run_event_loop(false).await.unwrap(); +} diff --git a/bench_util/lib.rs b/bench_util/lib.rs new file mode 100644 index 000000000..1dfb06ae0 --- /dev/null +++ b/bench_util/lib.rs @@ -0,0 +1,7 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +mod js_runtime; +mod profiling; + +pub use bencher; +pub use js_runtime::*; +pub use profiling::*; // Exports bench_or_profile! macro diff --git a/bench_util/profiling.rs b/bench_util/profiling.rs new file mode 100644 index 000000000..26d8d8fd6 --- /dev/null +++ b/bench_util/profiling.rs @@ -0,0 +1,82 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use bencher::{DynBenchFn, StaticBenchFn, TestDescAndFn, TestOpts}; + +pub fn is_profiling() -> bool { + std::env::var("PROFILING").is_ok() +} + +#[macro_export] +// Tweaked and copied from https://github.com/bluss/bencher/blob/master/macros.rs +macro_rules! bench_or_profile { + ($($group_name:path),+) => { + fn main() { + use $crate::bencher::TestOpts; + use $crate::bencher::run_tests_console; + let mut test_opts = TestOpts::default(); + // check to see if we should filter: + if let Some(arg) = ::std::env::args().skip(1).find(|arg| *arg != "--bench") { + test_opts.filter = Some(arg); + } + let mut benches = Vec::new(); + $( + benches.extend($group_name()); + )+ + + if $crate::is_profiling() { + // Run profling + $crate::run_profiles(&test_opts, benches); + } else { + // Run benches + run_tests_console(&test_opts, benches).unwrap(); + } + } + }; + ($($group_name:path,)+) => { + bench_or_profile!($($group_name),+); + }; +} + +pub fn run_profiles(opts: &TestOpts, tests: Vec) { + let tests = filter_tests(opts, tests); + // let decs = tests.iter().map(|t| t.desc.clone()).collect(); + + println!(); + for b in tests { + println!("Profiling {}", b.desc.name); + run_profile(b); + } + println!(); +} + +fn run_profile(test: TestDescAndFn) { + match test.testfn { + DynBenchFn(bencher) => { + bencher::bench::run_once(|harness| bencher.run(harness)); + } + StaticBenchFn(benchfn) => { + bencher::bench::run_once(benchfn); + } + }; +} + +// Copied from https://github.com/bluss/bencher/blob/master/lib.rs +fn filter_tests( + opts: &TestOpts, + tests: Vec, +) -> Vec { + let mut filtered = tests; + + // Remove tests that don't match the test filter + filtered = match opts.filter { + None => filtered, + Some(ref filter) => filtered + .into_iter() + .filter(|test| test.desc.name.contains(&filter[..])) + .collect(), + }; + + // Sort the tests alphabetically + filtered.sort_by(|t1, t2| t1.desc.name.cmp(&t2.desc.name)); + + filtered +} diff --git a/bench_util/src/js_runtime.rs b/bench_util/src/js_runtime.rs deleted file mode 100644 index 06dd79fae..000000000 --- a/bench_util/src/js_runtime.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use bencher::Bencher; -use deno_core::v8; -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; - -use crate::profiling::is_profiling; - -pub fn create_js_runtime(setup: impl FnOnce() -> Vec) -> JsRuntime { - JsRuntime::new(RuntimeOptions { - extensions: setup(), - ..Default::default() - }) -} - -fn loop_code(iters: u64, src: &str) -> String { - format!(r#"for(let i=0; i < {}; i++) {{ {} }}"#, iters, src,) -} - -#[derive(Copy, Clone)] -pub struct BenchOptions { - pub benching_inner: u64, - pub profiling_inner: u64, - pub profiling_outer: u64, -} - -impl Default for BenchOptions { - fn default() -> Self { - Self { - benching_inner: 1_000, - profiling_inner: 1_000, - profiling_outer: 10_000, - } - } -} - -pub fn bench_js_sync( - b: &mut Bencher, - src: &str, - setup: impl FnOnce() -> Vec, -) { - bench_js_sync_with(b, src, setup, Default::default()) -} - -pub fn bench_js_sync_with( - b: &mut Bencher, - src: &str, - setup: impl FnOnce() -> Vec, - opts: BenchOptions, -) { - let mut runtime = create_js_runtime(setup); - let scope = &mut runtime.handle_scope(); - - // Increase JS iterations if profiling for nicer flamegraphs - let inner_iters = if is_profiling() { - opts.profiling_inner * opts.profiling_outer - } else { - opts.benching_inner - }; - // Looped code - let looped_src = loop_code(inner_iters, src); - - let code = v8::String::new(scope, looped_src.as_ref()).unwrap(); - let script = v8::Script::compile(scope, code, None).unwrap(); - - // Run once if profiling, otherwise regular bench loop - if is_profiling() { - script.run(scope).unwrap(); - } else { - b.iter(|| { - script.run(scope).unwrap(); - }); - } -} - -pub fn bench_js_async( - b: &mut Bencher, - src: &str, - setup: impl FnOnce() -> Vec, -) { - bench_js_async_with(b, src, setup, Default::default()) -} - -pub fn bench_js_async_with( - b: &mut Bencher, - src: &str, - setup: impl FnOnce() -> Vec, - opts: BenchOptions, -) { - let mut runtime = create_js_runtime(setup); - let tokio_runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - // Looped code - let inner_iters = if is_profiling() { - opts.profiling_inner - } else { - opts.benching_inner - }; - let looped = loop_code(inner_iters, src); - let src = looped.as_ref(); - - if is_profiling() { - for _ in 0..opts.profiling_outer { - tokio_runtime.block_on(inner_async(src, &mut runtime)); - } - } else { - b.iter(|| { - tokio_runtime.block_on(inner_async(src, &mut runtime)); - }); - } -} - -async fn inner_async(src: &str, runtime: &mut JsRuntime) { - runtime.execute_script("inner_loop", src).unwrap(); - runtime.run_event_loop(false).await.unwrap(); -} diff --git a/bench_util/src/lib.rs b/bench_util/src/lib.rs deleted file mode 100644 index 1dfb06ae0..000000000 --- a/bench_util/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -mod js_runtime; -mod profiling; - -pub use bencher; -pub use js_runtime::*; -pub use profiling::*; // Exports bench_or_profile! macro diff --git a/bench_util/src/profiling.rs b/bench_util/src/profiling.rs deleted file mode 100644 index 26d8d8fd6..000000000 --- a/bench_util/src/profiling.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use bencher::{DynBenchFn, StaticBenchFn, TestDescAndFn, TestOpts}; - -pub fn is_profiling() -> bool { - std::env::var("PROFILING").is_ok() -} - -#[macro_export] -// Tweaked and copied from https://github.com/bluss/bencher/blob/master/macros.rs -macro_rules! bench_or_profile { - ($($group_name:path),+) => { - fn main() { - use $crate::bencher::TestOpts; - use $crate::bencher::run_tests_console; - let mut test_opts = TestOpts::default(); - // check to see if we should filter: - if let Some(arg) = ::std::env::args().skip(1).find(|arg| *arg != "--bench") { - test_opts.filter = Some(arg); - } - let mut benches = Vec::new(); - $( - benches.extend($group_name()); - )+ - - if $crate::is_profiling() { - // Run profling - $crate::run_profiles(&test_opts, benches); - } else { - // Run benches - run_tests_console(&test_opts, benches).unwrap(); - } - } - }; - ($($group_name:path,)+) => { - bench_or_profile!($($group_name),+); - }; -} - -pub fn run_profiles(opts: &TestOpts, tests: Vec) { - let tests = filter_tests(opts, tests); - // let decs = tests.iter().map(|t| t.desc.clone()).collect(); - - println!(); - for b in tests { - println!("Profiling {}", b.desc.name); - run_profile(b); - } - println!(); -} - -fn run_profile(test: TestDescAndFn) { - match test.testfn { - DynBenchFn(bencher) => { - bencher::bench::run_once(|harness| bencher.run(harness)); - } - StaticBenchFn(benchfn) => { - bencher::bench::run_once(benchfn); - } - }; -} - -// Copied from https://github.com/bluss/bencher/blob/master/lib.rs -fn filter_tests( - opts: &TestOpts, - tests: Vec, -) -> Vec { - let mut filtered = tests; - - // Remove tests that don't match the test filter - filtered = match opts.filter { - None => filtered, - Some(ref filter) => filtered - .into_iter() - .filter(|test| test.desc.name.contains(&filter[..])) - .collect(), - }; - - // Sort the tests alphabetically - filtered.sort_by(|t1, t2| t1.desc.name.cmp(&t2.desc.name)); - - filtered -} diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml index 8a9ee3337..4462b6d8c 100644 --- a/serde_v8/Cargo.toml +++ b/serde_v8/Cargo.toml @@ -9,6 +9,9 @@ readme = "README.md" repository = "https://github.com/denoland/deno" description = "Rust to V8 serialization and deserialization" +[lib] +path = "lib.rs" + [dependencies] serde = { version = "1.0.130", features = ["derive"] } serde_bytes = "0.11" diff --git a/serde_v8/de.rs b/serde_v8/de.rs new file mode 100644 index 000000000..002b741cf --- /dev/null +++ b/serde_v8/de.rs @@ -0,0 +1,614 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use serde::de::{self, Visitor}; +use serde::Deserialize; + +use crate::error::{Error, Result}; +use crate::keys::{v8_struct_key, KeyCache}; +use crate::magic::transl8::FromV8; +use crate::magic::transl8::{visit_magic, MagicType}; +use crate::payload::ValueType; +use crate::{magic, Buffer, ByteString, 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 +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 +where + T: Deserialize<'de>, +{ + let mut deserializer = Deserializer::new(scope, input, Some(key_cache)); + let t = T::deserialize(&mut deserializer)?; + Ok(t) +} + +macro_rules! wip { + ($method:ident) => { + fn $method(self, _v: V) -> Result + where + V: Visitor<'de>, + { + unimplemented!() + } + }; +} + +// TODO: maybe check for BigInt truncation ? +// (i.e: values larger than i64/u64 can hold) +macro_rules! deserialize_signed { + ($dmethod:ident, $vmethod:ident, $t:tt) => { + fn $dmethod(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value: $t = match self.input.is_big_int() { + true => { + let bigint = v8::Local::::try_from(self.input); + bigint.unwrap().i64_value().0 as $t + } + false => self.input.integer_value(&mut self.scope).unwrap() as $t, + }; + visitor.$vmethod(value) + } + }; +} + +macro_rules! deserialize_unsigned { + ($dmethod:ident, $vmethod:ident, $t:tt) => { + fn $dmethod(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let value: $t = match self.input.is_big_int() { + true => { + let bigint = v8::Local::::try_from(self.input); + bigint.unwrap().u64_value().0 as $t + } + false => self.input.integer_value(&mut self.scope).unwrap() as $t, + }; + visitor.$vmethod(value) + } + }; +} + +impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> + for &'x mut Deserializer<'a, 'b, 's> +{ + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match ValueType::from_v8(self.input) { + ValueType::Null => self.deserialize_unit(visitor), + ValueType::Bool => self.deserialize_bool(visitor), + // Handle floats & ints separately to work with loosely-typed serde_json + ValueType::Number => { + if self.input.is_uint32() { + self.deserialize_u32(visitor) + } else if self.input.is_int32() { + self.deserialize_i32(visitor) + } else { + self.deserialize_f64(visitor) + } + } + ValueType::String => self.deserialize_string(visitor), + ValueType::Array => self.deserialize_seq(visitor), + ValueType::Object => self.deserialize_map(visitor), + // Map to Vec when deserialized via deserialize_any + // e.g: for untagged enums or StringOrBuffer + ValueType::ArrayBufferView | ValueType::ArrayBuffer => { + magic::zero_copy_buf::ZeroCopyBuf::from_v8(&mut *self.scope, self.input) + .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) + } + } + } + + fn deserialize_bool(self, visitor: V) -> Result + 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(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f32(self.input.number_value(self.scope).unwrap() as f32) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f64(self.input.number_value(self.scope).unwrap()) + } + + wip!(deserialize_char); + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_string(visitor) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.input.is_string() { + let v8_string = v8::Local::::try_from(self.input).unwrap(); + let string = v8_string.to_rust_string_lossy(self.scope); + visitor.visit_string(string) + } else { + Err(Error::ExpectedString) + } + } + + wip!(deserialize_bytes); + wip!(deserialize_byte_buf); + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.input.is_null_or_undefined() { + visitor.visit_none() + } else { + visitor.visit_some(self) + } + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } + + fn deserialize_unit_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + 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( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let arr = v8::Local::::try_from(self.input) + .map_err(|_| Error::ExpectedArray)?; + let len = arr.length(); + let obj = v8::Local::::from(arr); + let seq = SeqAccess { + pos: 0, + len, + obj, + scope: self.scope, + }; + visitor.visit_seq(seq) + } + + // Like deserialize_seq except it prefers tuple's length over input array's length + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + // TODO: error on length mismatch + let obj = v8::Local::::try_from(self.input).unwrap(); + let seq = SeqAccess { + pos: 0, + len: len as u32, + obj, + scope: self.scope, + }; + visitor.visit_seq(seq) + } + + // Tuple structs look just like sequences in JSON. + fn deserialize_tuple_struct( + self, + _name: &'static str, + len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_tuple(len, visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + // Assume object, then get_own_property_names + let obj = v8::Local::::try_from(self.input) + .map_err(|_| Error::ExpectedObject)?; + let prop_names = obj.get_own_property_names(self.scope); + let mut keys: Vec = match prop_names { + Some(names) => from_v8(self.scope, names.into()).unwrap(), + None => vec![], + }; + let keys: Vec> = keys + .drain(..) + .map(|x| x.into()) + // Filter keys to drop keys whose value is undefined + // TODO: optimize, since this doubles our get calls + .filter(|key| !obj.get(self.scope, *key).unwrap().is_undefined()) + .collect(); + + let map = MapAccess { + obj, + keys, + pos: 0, + scope: self.scope, + }; + visitor.visit_map(map) + } + + fn deserialize_struct( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match name { + Buffer::MAGIC_NAME => { + visit_magic(visitor, Buffer::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)?) + } + magic::Value::MAGIC_NAME => { + visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) + } + _ => { + // Regular struct + let obj = self.input.try_into().or(Err(Error::ExpectedObject))?; + visitor.visit_seq(StructAccess { + fields, + obj, + pos: 0, + scope: self.scope, + _cache: None, + }) + } + } + } + + /// 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( + self, + _name: &str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + // Unit variant + if self.input.is_string() { + let payload = v8::undefined(self.scope).into(); + visitor.visit_enum(EnumAccess { + scope: self.scope, + tag: self.input, + payload, + }) + } + // Struct or tuple variant + else if self.input.is_object() { + // Assume object + let obj = v8::Local::::try_from(self.input).unwrap(); + // Unpack single-key + let tag = { + let prop_names = obj.get_own_property_names(self.scope); + let prop_names = prop_names.ok_or(Error::ExpectedEnum)?; + if prop_names.length() != 1 { + return Err(Error::LengthMismatch); + } + prop_names.get_index(self.scope, 0).unwrap() + }; + + let payload = obj.get(self.scope, tag).unwrap(); + visitor.visit_enum(EnumAccess { + scope: self.scope, + tag, + payload, + }) + } else { + // TODO: improve error + Err(Error::ExpectedEnum) + } + } + + // An identifier in Serde is the type that identifies a field of a struct or + // the variant of an enum. In JSON, struct fields and enum variants are + // represented as strings. In other formats they may be represented as + // numeric indices. + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_none() + } +} + +struct MapAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + keys: Vec>, + pos: usize, +} + +impl<'de> de::MapAccess<'de> for MapAccess<'_, '_, '_> { + type Error = Error; + + fn next_key_seed>( + &mut self, + seed: K, + ) -> Result> { + Ok(match self.keys.get(self.pos) { + Some(key) => { + let mut deserializer = Deserializer::new(self.scope, *key, None); + Some(seed.deserialize(&mut deserializer)?) + } + None => None, + }) + } + + fn next_value_seed>( + &mut self, + seed: V, + ) -> Result { + if self.pos >= self.keys.len() { + return Err(Error::LengthMismatch); + } + let key = self.keys[self.pos]; + self.pos += 1; + let v8_val = self.obj.get(self.scope, key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + seed.deserialize(&mut deserializer) + } + + fn next_entry_seed< + K: de::DeserializeSeed<'de>, + V: de::DeserializeSeed<'de>, + >( + &mut self, + kseed: K, + vseed: V, + ) -> Result> { + if self.pos >= self.keys.len() { + return Ok(None); + } + let v8_key = self.keys[self.pos]; + self.pos += 1; + let mut kdeserializer = Deserializer::new(self.scope, v8_key, None); + Ok(Some((kseed.deserialize(&mut kdeserializer)?, { + let v8_val = self.obj.get(self.scope, v8_key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, v8_val, None); + vseed.deserialize(&mut deserializer)? + }))) + } +} + +struct StructAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + fields: &'static [&'static str], + pos: usize, + _cache: Option<&'b mut KeyCache>, +} + +impl<'de> de::SeqAccess<'de> for StructAccess<'_, '_, '_> { + type Error = Error; + + fn next_element_seed>( + &mut self, + seed: T, + ) -> Result> { + if self.pos >= self.fields.len() { + return Ok(None); + } + + let pos = self.pos; + self.pos += 1; + + let field = self.fields[pos]; + let key = v8_struct_key(self.scope, field).into(); + let val = self.obj.get(self.scope, key).unwrap(); + let mut deserializer = Deserializer::new(self.scope, val, None); + match seed.deserialize(&mut deserializer) { + Ok(val) => Ok(Some(val)), + // Fallback to Ok(None) for #[serde(Default)] at cost of error quality ... + // TODO(@AaronO): double check that there's no better solution + Err(_) if val.is_undefined() => Ok(None), + Err(e) => Err(e), + } + } +} + +struct SeqAccess<'a, 'b, 's> { + obj: v8::Local<'a, v8::Object>, + scope: &'b mut v8::HandleScope<'s>, + len: u32, + pos: u32, +} + +impl<'de> de::SeqAccess<'de> for SeqAccess<'_, '_, '_> { + type Error = Error; + + fn next_element_seed>( + &mut self, + seed: T, + ) -> Result> { + let pos = self.pos; + self.pos += 1; + + if pos < self.len { + let val = self.obj.get_index(self.scope, pos).unwrap(); + let mut deserializer = Deserializer::new(self.scope, val, None); + Ok(Some(seed.deserialize(&mut deserializer)?)) + } else { + Ok(None) + } + } + + fn size_hint(&self) -> Option { + Some((self.len - self.pos) as usize) + } +} + +struct EnumAccess<'a, 'b, 's> { + tag: v8::Local<'a, v8::Value>, + payload: v8::Local<'a, v8::Value>, + scope: &'b mut v8::HandleScope<'s>, + // p1: std::marker::PhantomData<&'x ()>, +} + +impl<'de, 'a, 'b, 's, 'x> de::EnumAccess<'de> for EnumAccess<'a, 'b, 's> { + type Error = Error; + type Variant = VariantDeserializer<'a, 'b, 's>; + + fn variant_seed>( + 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>( + self, + seed: T, + ) -> Result { + let mut d = Deserializer::new(self.scope, self.value, None); + seed.deserialize(&mut d) + } + + fn tuple_variant>( + self, + len: usize, + visitor: V, + ) -> Result { + let mut d = Deserializer::new(self.scope, self.value, None); + de::Deserializer::deserialize_tuple(&mut d, len, visitor) + } + + fn struct_variant>( + self, + fields: &'static [&'static str], + visitor: V, + ) -> Result { + let mut d = Deserializer::new(self.scope, self.value, None); + de::Deserializer::deserialize_struct(&mut d, "", fields, visitor) + } +} diff --git a/serde_v8/error.rs b/serde_v8/error.rs new file mode 100644 index 000000000..e4f7f363b --- /dev/null +++ b/serde_v8/error.rs @@ -0,0 +1,48 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use std::fmt::{self, Display}; + +use serde::{de, ser}; + +pub type Result = std::result::Result; + +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + Message(String), + + ExpectedBoolean, + ExpectedInteger, + ExpectedString, + ExpectedArray, + ExpectedMap, + ExpectedEnum, + ExpectedObject, + ExpectedBuffer, + + ExpectedUtf8, + ExpectedLatin1, + + LengthMismatch, +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Message(msg) => formatter.write_str(msg), + err => formatter.write_str(format!("serde_v8 error: {:?}", err).as_ref()), + } + } +} + +impl std::error::Error for Error {} diff --git a/serde_v8/keys.rs b/serde_v8/keys.rs new file mode 100644 index 000000000..4811a87f8 --- /dev/null +++ b/serde_v8/keys.rs @@ -0,0 +1,31 @@ +// Copyright 2018-2022 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>); + +// 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 new file mode 100644 index 000000000..203e1d004 --- /dev/null +++ b/serde_v8/lib.rs @@ -0,0 +1,20 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +mod de; +mod error; +mod keys; +mod magic; +mod payload; +mod ser; +mod serializable; +pub mod utils; + +pub use de::{from_v8, from_v8_cached, Deserializer}; +pub use error::{Error, Result}; +pub use keys::KeyCache; +pub use magic::buffer::MagicBuffer as Buffer; +pub use magic::bytestring::ByteString; +pub use magic::string_or_buffer::StringOrBuffer; +pub use magic::u16string::U16String; +pub use magic::Value; +pub use ser::{to_v8, Serializer}; +pub use serializable::{Serializable, SerializablePkg}; diff --git a/serde_v8/magic/buffer.rs b/serde_v8/magic/buffer.rs new file mode 100644 index 000000000..484984ac5 --- /dev/null +++ b/serde_v8/magic/buffer.rs @@ -0,0 +1,119 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::ops::Deref; +use std::ops::DerefMut; +use std::sync::Mutex; + +use super::transl8::FromV8; +use super::transl8::ToV8; +use super::zero_copy_buf::ZeroCopyBuf; +use crate::magic::transl8::impl_magic; + +// An asymmetric wrapper around ZeroCopyBuf, +// allowing us to use a single type for familiarity +pub enum MagicBuffer { + FromV8(ZeroCopyBuf), + ToV8(Mutex>>), +} +impl_magic!(MagicBuffer); + +impl MagicBuffer { + pub fn empty() -> Self { + MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice()))) + } +} + +impl Clone for MagicBuffer { + fn clone(&self) -> Self { + match self { + Self::FromV8(zbuf) => Self::FromV8(zbuf.clone()), + Self::ToV8(_) => panic!("Don't Clone a MagicBuffer sent to v8"), + } + } +} + +impl AsRef<[u8]> for MagicBuffer { + fn as_ref(&self) -> &[u8] { + &*self + } +} + +impl AsMut<[u8]> for MagicBuffer { + fn as_mut(&mut self) -> &mut [u8] { + &mut *self + } +} + +impl Deref for MagicBuffer { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match self { + Self::FromV8(buf) => &*buf, + Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"), + } + } +} + +impl DerefMut for MagicBuffer { + fn deref_mut(&mut self) -> &mut [u8] { + match self { + Self::FromV8(buf) => &mut *buf, + Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"), + } + } +} + +impl From> for MagicBuffer { + fn from(buf: Box<[u8]>) -> Self { + MagicBuffer::ToV8(Mutex::new(Some(buf))) + } +} + +impl From> for MagicBuffer { + fn from(vec: Vec) -> Self { + vec.into_boxed_slice().into() + } +} + +impl ToV8 for MagicBuffer { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let buf: Box<[u8]> = match self { + Self::FromV8(buf) => { + let value: &[u8] = buf; + value.into() + } + Self::ToV8(x) => x.lock().unwrap().take().expect("MagicBuffer was empty"), + }; + + 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 = buf.len(); + let backing_store = + v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf); + let backing_store_shared = backing_store.make_shared(); + let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared); + Ok( + v8::Uint8Array::new(scope, ab, 0, buf_len) + .expect("Failed to create Uint8Array") + .into(), + ) + } +} + +impl FromV8 for MagicBuffer { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + Ok(Self::FromV8(ZeroCopyBuf::from_v8(scope, value)?)) + } +} diff --git a/serde_v8/magic/bytestring.rs b/serde_v8/magic/bytestring.rs new file mode 100644 index 000000000..43ac54b97 --- /dev/null +++ b/serde_v8/magic/bytestring.rs @@ -0,0 +1,122 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::ops::{Deref, DerefMut}; + +use super::transl8::{FromV8, ToV8}; +use crate::magic::transl8::impl_magic; +use crate::Error; + +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct ByteString(pub Vec); +impl_magic!(ByteString); + +impl ByteString { + pub fn new() -> ByteString { + ByteString(Vec::new()) + } + + pub fn with_capacity(capacity: usize) -> ByteString { + ByteString(Vec::with_capacity(capacity)) + } + + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + pub fn reserve(&mut self, additional: usize) { + self.0.reserve(additional) + } + + pub fn reserve_exact(&mut self, additional: usize) { + self.0.reserve_exact(additional) + } + + pub fn shrink_to_fit(&mut self) { + self.0.shrink_to_fit() + } + + pub fn truncate(&mut self, len: usize) { + self.0.truncate(len) + } + + pub fn push(&mut self, value: u8) { + self.0.push(value) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } +} + +impl Default for ByteString { + fn default() -> Self { + ByteString::new() + } +} + +impl Deref for ByteString { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +impl DerefMut for ByteString { + fn deref_mut(&mut self) -> &mut [u8] { + self.0.deref_mut() + } +} + +impl AsRef<[u8]> for ByteString { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsMut<[u8]> for ByteString { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +impl ToV8 for ByteString { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let v = + v8::String::new_from_one_byte(scope, self, v8::NewStringType::Normal) + .unwrap(); + Ok(v.into()) + } +} + +impl FromV8 for ByteString { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + let v8str = v8::Local::::try_from(value) + .map_err(|_| Error::ExpectedString)?; + if !v8str.contains_only_onebyte() { + return Err(Error::ExpectedLatin1); + } + let len = v8str.length(); + let mut buffer = Vec::with_capacity(len); + // SAFETY: we set length == capacity (see previous line), + // before immediately writing into that buffer and sanity check with an assert + #[allow(clippy::uninit_vec)] + unsafe { + buffer.set_len(len); + let written = v8str.write_one_byte( + scope, + &mut buffer, + 0, + v8::WriteOptions::NO_NULL_TERMINATION, + ); + assert!(written == len); + } + Ok(ByteString(buffer)) + } +} diff --git a/serde_v8/magic/mod.rs b/serde_v8/magic/mod.rs new file mode 100644 index 000000000..bc86c6a7c --- /dev/null +++ b/serde_v8/magic/mod.rs @@ -0,0 +1,9 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +pub mod buffer; +pub mod bytestring; +pub mod string_or_buffer; +pub mod transl8; +pub mod u16string; +mod value; +pub mod zero_copy_buf; +pub use value::Value; diff --git a/serde_v8/magic/string_or_buffer.rs b/serde_v8/magic/string_or_buffer.rs new file mode 100644 index 000000000..edde2adcd --- /dev/null +++ b/serde_v8/magic/string_or_buffer.rs @@ -0,0 +1,45 @@ +use std::ops::Deref; + +#[derive(Debug)] +pub struct StringOrBuffer(Vec); + +impl Deref for StringOrBuffer { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.0 + } +} + +impl StringOrBuffer { + pub fn into_bytes(self) -> Vec { + self.0 + } +} + +impl<'de> serde::Deserialize<'de> for StringOrBuffer { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + StringOrBufferInner::deserialize(deserializer) + .map(|x| StringOrBuffer(x.into_bytes())) + } +} + +// TODO(@AaronO): explore if we can make this work with ZeroCopyBuf +#[derive(serde::Deserialize)] +#[serde(untagged)] +enum StringOrBufferInner { + #[serde(with = "serde_bytes")] + Buffer(Vec), + String(String), +} + +impl StringOrBufferInner { + fn into_bytes(self) -> Vec { + match self { + Self::String(s) => s.into_bytes(), + Self::Buffer(b) => b, + } + } +} diff --git a/serde_v8/magic/transl8.rs b/serde_v8/magic/transl8.rs new file mode 100644 index 000000000..458b82129 --- /dev/null +++ b/serde_v8/magic/transl8.rs @@ -0,0 +1,143 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +//! Transerialization extends the set of serde-compatible types (for given de/serializers). +//! By "hackishly" transmuting references across serde boundaries as u64s. +//! Type-safety is enforced using special struct names for each "magic type". +//! Memory-safety relies on transerialized values being "pinned" during de/serialization. + +pub(crate) const MAGIC_FIELD: &str = "$__v8_magic_field"; + +pub(crate) trait MagicType { + const NAME: &'static str; + const MAGIC_NAME: &'static str; +} + +pub(crate) trait ToV8 { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error>; +} + +pub(crate) trait FromV8: Sized { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result; +} + +pub(crate) fn magic_serialize( + serializer: S, + x: &T, +) -> Result +where + S: serde::Serializer, + T: MagicType, +{ + use serde::ser::SerializeStruct; + + let mut s = serializer.serialize_struct(T::MAGIC_NAME, 1)?; + let ptr = opaque_send(x); + s.serialize_field(MAGIC_FIELD, &ptr)?; + s.end() +} + +pub(crate) fn magic_deserialize<'de, T, D>( + deserializer: D, +) -> Result +where + D: serde::Deserializer<'de>, + T: MagicType, +{ + struct ValueVisitor { + p1: std::marker::PhantomData, + } + + impl<'de, T: MagicType> serde::de::Visitor<'de> for ValueVisitor { + type Value = T; + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + formatter.write_str("a ")?; + formatter.write_str(T::NAME) + } + + fn visit_u64(self, ptr: u64) -> Result + where + E: serde::de::Error, + { + // SAFETY: opaque ptr originates from visit_magic, which forgets ownership so we can take it + Ok(unsafe { opaque_take(ptr) }) + } + } + + deserializer.deserialize_struct( + T::MAGIC_NAME, + &[MAGIC_FIELD], + ValueVisitor:: { + p1: std::marker::PhantomData, + }, + ) +} + +pub(crate) fn visit_magic<'de, T, V, E>(visitor: V, x: T) -> Result +where + V: serde::de::Visitor<'de>, + E: serde::de::Error, +{ + let y = visitor.visit_u64::(opaque_send(&x)); + std::mem::forget(x); + y +} + +/// Constructs an "opaque" ptr from a reference to transerialize +pub(crate) fn opaque_send(x: &T) -> u64 { + (x as *const T) as u64 +} + +/// Copies an "opaque" ptr from a reference to an opaque ptr (transerialized) +/// NOTE: ptr-to-ptr, extra indirection +pub(crate) unsafe fn opaque_recv(ptr: &T) -> u64 { + *(ptr as *const T as *const u64) +} + +/// Transmutes an "opaque" ptr back into a reference +pub(crate) unsafe fn opaque_deref<'a, T>(ptr: u64) -> &'a T { + std::mem::transmute(ptr) +} + +/// Transmutes & copies the value from the "opaque" ptr +/// NOTE: takes ownership & requires other end to forget its ownership +pub(crate) unsafe fn opaque_take(ptr: u64) -> T { + std::mem::transmute_copy::(std::mem::transmute(ptr)) +} + +macro_rules! impl_magic { + ($t:ty) => { + impl crate::magic::transl8::MagicType for $t { + const NAME: &'static str = stringify!($t); + const MAGIC_NAME: &'static str = concat!("$__v8_magic_", stringify!($t)); + } + + impl serde::Serialize for $t { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + crate::magic::transl8::magic_serialize(serializer, self) + } + } + + impl<'de> serde::Deserialize<'de> for $t { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + crate::magic::transl8::magic_deserialize(deserializer) + } + } + }; +} +pub(crate) use impl_magic; diff --git a/serde_v8/magic/u16string.rs b/serde_v8/magic/u16string.rs new file mode 100644 index 000000000..39ebf88d9 --- /dev/null +++ b/serde_v8/magic/u16string.rs @@ -0,0 +1,83 @@ +use crate::magic::transl8::impl_magic; +use crate::Error; +use std::ops::{Deref, DerefMut}; + +use super::transl8::{FromV8, ToV8}; + +#[derive(Default, PartialEq, Eq, Debug)] +pub struct U16String(pub Vec); +impl_magic!(U16String); + +impl U16String { + pub fn with_zeroes(length: usize) -> U16String { + U16String(vec![0u16; length]) + } + + pub fn truncate(&mut self, new_length: usize) { + self.0.truncate(new_length); + self.0.shrink_to_fit() + } +} + +impl Deref for U16String { + type Target = [u16]; + fn deref(&self) -> &[u16] { + self.0.deref() + } +} + +impl DerefMut for U16String { + fn deref_mut(&mut self) -> &mut [u16] { + self.0.deref_mut() + } +} + +impl AsRef<[u16]> for U16String { + fn as_ref(&self) -> &[u16] { + self.0.as_ref() + } +} + +impl AsMut<[u16]> for U16String { + fn as_mut(&mut self) -> &mut [u16] { + self.0.as_mut() + } +} + +impl ToV8 for U16String { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + let v = + v8::String::new_from_two_byte(scope, self, v8::NewStringType::Normal) + .unwrap(); + Ok(v.into()) + } +} + +impl FromV8 for U16String { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + let v8str = v8::Local::::try_from(value) + .map_err(|_| Error::ExpectedString)?; + let len = v8str.length(); + let mut buffer = Vec::with_capacity(len); + // SAFETY: we set length == capacity (see previous line), + // before immediately writing into that buffer and sanity check with an assert + #[allow(clippy::uninit_vec)] + unsafe { + buffer.set_len(len); + let written = v8str.write( + scope, + &mut buffer, + 0, + v8::WriteOptions::NO_NULL_TERMINATION, + ); + assert!(written == len); + } + Ok(U16String(buffer)) + } +} diff --git a/serde_v8/magic/value.rs b/serde_v8/magic/value.rs new file mode 100644 index 000000000..5d844fbda --- /dev/null +++ b/serde_v8/magic/value.rs @@ -0,0 +1,48 @@ +// Copyright 2018-2022 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> for Value<'s> { + fn from(v8_value: v8::Local<'s, v8::Value>) -> Self { + Self { v8_value } + } +} + +impl<'s> From> for v8::Local<'s, v8::Value> { + fn from(v: Value<'s>) -> Self { + v.v8_value + } +} + +impl ToV8 for Value<'_> { + fn to_v8<'a>( + &self, + _scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + // SAFETY: not fully safe, since lifetimes are detached from original scope + Ok(unsafe { transmute(self.v8_value) }) + } +} + +impl FromV8 for Value<'_> { + fn from_v8( + _scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + // SAFETY: not fully safe, since lifetimes are detached from original scope + Ok(unsafe { transmute::(value.into()) }) + } +} diff --git a/serde_v8/magic/zero_copy_buf.rs b/serde_v8/magic/zero_copy_buf.rs new file mode 100644 index 000000000..c63d4ba66 --- /dev/null +++ b/serde_v8/magic/zero_copy_buf.rs @@ -0,0 +1,136 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::cell::Cell; +use std::ops::Deref; +use std::ops::DerefMut; + +use super::transl8::FromV8; + +/// A ZeroCopyBuf encapsulates a slice that's been borrowed from a JavaScript +/// ArrayBuffer object. JavaScript objects can normally be garbage collected, +/// but the existence of a ZeroCopyBuf inhibits this until it is dropped. It +/// behaves much like an Arc<[u8]>. +/// +/// # Cloning +/// Cloning a ZeroCopyBuf does not clone the contents of the buffer, +/// it creates a new reference to that buffer. +/// +/// To actually clone the contents of the buffer do +/// `let copy = Vec::from(&*zero_copy_buf);` +#[derive(Clone)] +pub struct ZeroCopyBuf { + backing_store: v8::SharedRef, + byte_offset: usize, + byte_length: usize, +} + +unsafe impl Send for ZeroCopyBuf {} + +impl ZeroCopyBuf { + pub fn from_buffer( + buffer: v8::Local, + byte_offset: usize, + byte_length: usize, + ) -> Result { + let backing_store = buffer.get_backing_store(); + match backing_store.is_shared() { + true => Err(v8::DataError::BadType { + actual: "shared ArrayBufferView", + expected: "non-shared ArrayBufferView", + }), + false => Ok(Self { + backing_store, + byte_offset, + byte_length, + }), + } + } + + pub fn from_view( + scope: &mut v8::HandleScope, + view: v8::Local, + ) -> Result { + let buffer = view.buffer(scope).ok_or(v8::DataError::NoData { + expected: "view to have a buffer", + })?; + Self::from_buffer(buffer, view.byte_offset(), view.byte_length()) + } +} + +impl FromV8 for ZeroCopyBuf { + fn from_v8( + scope: &mut v8::HandleScope, + value: v8::Local, + ) -> Result { + if value.is_array_buffer() { + value + .try_into() + .and_then(|b| Self::from_buffer(b, 0, b.byte_length())) + } else { + value + .try_into() + .and_then(|view| Self::from_view(scope, view)) + } + .map_err(|_| crate::Error::ExpectedBuffer) + } +} + +impl Deref for ZeroCopyBuf { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + get_backing_store_slice( + &self.backing_store, + self.byte_offset, + self.byte_length, + ) + } + } +} + +impl DerefMut for ZeroCopyBuf { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + get_backing_store_slice_mut( + &self.backing_store, + self.byte_offset, + self.byte_length, + ) + } + } +} + +impl AsRef<[u8]> for ZeroCopyBuf { + fn as_ref(&self) -> &[u8] { + &*self + } +} + +impl AsMut<[u8]> for ZeroCopyBuf { + fn as_mut(&mut self) -> &mut [u8] { + &mut *self + } +} + +unsafe fn get_backing_store_slice( + backing_store: &v8::SharedRef, + byte_offset: usize, + byte_length: usize, +) -> &[u8] { + let cells: *const [Cell] = + &backing_store[byte_offset..byte_offset + byte_length]; + let bytes = cells as *const [u8]; + &*bytes +} + +#[allow(clippy::mut_from_ref)] +unsafe fn get_backing_store_slice_mut( + backing_store: &v8::SharedRef, + byte_offset: usize, + byte_length: usize, +) -> &mut [u8] { + let cells: *const [Cell] = + &backing_store[byte_offset..byte_offset + byte_length]; + let bytes = cells as *const _ as *mut [u8]; + &mut *bytes +} diff --git a/serde_v8/payload.rs b/serde_v8/payload.rs new file mode 100644 index 000000000..c9f3e85aa --- /dev/null +++ b/serde_v8/payload.rs @@ -0,0 +1,40 @@ +// Copyright 2018-2022 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, + String, + Array, + ArrayBuffer, + ArrayBufferView, + Object, +} + +impl ValueType { + pub fn from_v8(v: v8::Local) -> 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_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 new file mode 100644 index 000000000..5241aeaef --- /dev/null +++ b/serde_v8/ser.rs @@ -0,0 +1,554 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use serde::ser; +use serde::ser::Serialize; + +use std::cell::RefCell; + +use crate::error::{Error, Result}; +use crate::keys::v8_struct_key; +use crate::magic::transl8::MAGIC_FIELD; +use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8}; +use crate::{magic, Buffer, ByteString, U16String}; + +type JsValue<'s> = v8::Local<'s, v8::Value>; +type JsResult<'s> = Result>; + +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, Error = Error>, +{ + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field( + &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, Error = Error>, +{ + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field( + &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>, + scope: ScopePtr<'a, 'b, 'c>, +} + +impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> 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( + &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( + &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( + &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>, + values: Vec>, +} + +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( + &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, +} + +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:: {}, + } + } +} + +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( + &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: &T = unsafe { opaque_deref(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> { + Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>), + MagicBuffer(MagicalSerializer<'a, 'b, 'c, Buffer>), + MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), + MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), + Regular(ObjectSerializer<'a, 'b, 'c>), +} + +impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> { + type Ok = JsValue<'a>; + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<()> { + match self { + StructSerializers::Magic(s) => s.serialize_field(key, value), + StructSerializers::MagicBuffer(s) => s.serialize_field(key, value), + StructSerializers::MagicByteString(s) => s.serialize_field(key, value), + StructSerializers::MagicU16String(s) => s.serialize_field(key, value), + StructSerializers::Regular(s) => s.serialize_field(key, value), + } + } + + fn end(self) -> JsResult<'a> { + match self { + StructSerializers::Magic(s) => s.end(), + StructSerializers::MagicBuffer(s) => s.end(), + StructSerializers::MagicByteString(s) => s.end(), + StructSerializers::MagicU16String(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>, + values: Vec>, +} + +impl<'a, 'b, 'c> MapSerializer<'a, 'b, 'c> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> 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(&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( + &mut self, + value: &T, + ) -> Result<()> { + let v8_value = value.serialize(Serializer::new(self.scope))?; + self.values.push(v8_value); + Ok(()) + } + + fn end(self) -> JsResult<'a> { + debug_assert!(self.keys.len() == self.values.len()); + let scope = &mut *self.scope.borrow_mut(); + let null = v8::null(scope).into(); + let obj = v8::Object::with_prototype_and_properties( + scope, + null, + &self.keys[..], + &self.values[..], + ); + Ok(obj.into()) + } +} + +pub struct Serializer<'a, 'b, 'c> { + scope: ScopePtr<'a, 'b, 'c>, +} + +impl<'a, 'b, 'c> Serializer<'a, 'b, 'c> { + pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { + Serializer { scope } + } +} + +macro_rules! forward_to { + ($($name:ident($ty:ty, $to:ident, $lt:lifetime);)*) => { + $(fn $name(self, v: $ty) -> JsResult<$lt> { + self.$to(v as _) + })* + }; +} + +impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { + type Ok = v8::Local<'a, v8::Value>; + type Error = Error; + + type SerializeSeq = ArraySerializer<'a, 'b, 'c>; + type SerializeTuple = ArraySerializer<'a, 'b, 'c>; + type SerializeTupleStruct = ArraySerializer<'a, 'b, 'c>; + type SerializeTupleVariant = + VariantSerializer<'a, 'b, 'c, ArraySerializer<'a, 'b, 'c>>; + type SerializeMap = MapSerializer<'a, 'b, 'c>; + type SerializeStruct = StructSerializers<'a, 'b, 'c>; + type SerializeStructVariant = + VariantSerializer<'a, 'b, 'c, StructSerializers<'a, 'b, 'c>>; + + forward_to! { + serialize_i8(i8, serialize_i32, 'a); + serialize_i16(i16, serialize_i32, 'a); + + serialize_u8(u8, serialize_u32, 'a); + serialize_u16(u16, serialize_u32, 'a); + + serialize_f32(f32, serialize_f64, 'a); + serialize_u64(u64, serialize_f64, 'a); + serialize_i64(i64, serialize_f64, 'a); + } + + fn serialize_i32(self, v: i32) -> JsResult<'a> { + Ok(v8::Integer::new(&mut self.scope.borrow_mut(), v).into()) + } + + fn serialize_u32(self, v: u32) -> JsResult<'a> { + Ok(v8::Integer::new_from_unsigned(&mut self.scope.borrow_mut(), v).into()) + } + + fn serialize_f64(self, v: f64) -> JsResult<'a> { + Ok(v8::Number::new(&mut self.scope.borrow_mut(), v).into()) + } + + fn serialize_bool(self, v: bool) -> JsResult<'a> { + Ok(v8::Boolean::new(&mut self.scope.borrow_mut(), v).into()) + } + + fn serialize_char(self, _v: char) -> JsResult<'a> { + unimplemented!(); + } + + fn serialize_str(self, v: &str) -> JsResult<'a> { + v8::String::new(&mut self.scope.borrow_mut(), v) + .map(|v| v.into()) + .ok_or(Error::ExpectedString) + } + + fn serialize_bytes(self, _v: &[u8]) -> JsResult<'a> { + // TODO: investigate using Uint8Arrays + unimplemented!() + } + + fn serialize_none(self) -> JsResult<'a> { + Ok(v8::null(&mut self.scope.borrow_mut()).into()) + } + + fn serialize_some(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( + self, + _name: &'static str, + value: &T, + ) -> JsResult<'a> { + value.serialize(self) + } + + fn serialize_newtype_variant( + 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) -> Result { + Ok(ArraySerializer::new(self.scope, len)) + } + + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_tuple(len) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(VariantSerializer::new( + self.scope, + variant, + self.serialize_tuple_struct(variant, len)?, + )) + } + + fn serialize_map(self, len: Option) -> Result { + // 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 { + match name { + ByteString::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); + Ok(StructSerializers::MagicByteString(m)) + } + U16String::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); + Ok(StructSerializers::MagicU16String(m)) + } + Buffer::MAGIC_NAME => { + let m = MagicalSerializer::::new(self.scope); + Ok(StructSerializers::MagicBuffer(m)) + } + magic::Value::MAGIC_NAME => { + let m = MagicalSerializer::>::new(self.scope); + Ok(StructSerializers::Magic(m)) + } + _ => { + // Regular structs + let o = ObjectSerializer::new(self.scope, len); + Ok(StructSerializers::Regular(o)) + } + } + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + let scope = self.scope; + let x = self.serialize_struct(variant, len)?; + Ok(VariantSerializer::new(scope, variant, x)) + } +} diff --git a/serde_v8/serializable.rs b/serde_v8/serializable.rs new file mode 100644 index 000000000..533b3f83f --- /dev/null +++ b/serde_v8/serializable.rs @@ -0,0 +1,142 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use std::any::TypeId; +use std::mem::transmute_copy; + +use crate::Buffer; +use crate::ByteString; +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>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error>; +} + +/// Allows all implementors of `serde::Serialize` to implement Serializable +impl Serializable for T { + fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, crate::Error> { + crate::to_v8(scope, self) + } +} + +/// SerializablePkg exists to provide a fast path for op returns, +/// allowing them to avoid boxing primtives (ints/floats/bool/unit/...) +pub enum SerializablePkg { + Primitive(Primitive), + Serializable(Box), +} + +impl SerializablePkg { + pub fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, 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), + Buffer(Buffer), + ByteString(ByteString), + U16String(U16String), +} + +impl serde::Serialize for Primitive { + fn serialize(&self, s: S) -> Result + 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::Buffer(x) => x.serialize(s), + Self::ByteString(x) => x.serialize(s), + Self::U16String(x) => x.serialize(s), + } + } +} + +impl From for SerializablePkg { + fn from(x: T) -> Self { + #[inline(always)] + fn tc(src: T) -> U { + let x = unsafe { transmute_copy(&src) }; + std::mem::forget(src); + x + } + + let tid = TypeId::of::(); + if tid == TypeId::of::<()>() { + Self::Primitive(Primitive::Unit) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Bool(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Int8(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Int16(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Int32(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Int64(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::UInt8(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::UInt16(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::UInt32(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::UInt64(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Float32(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Float64(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::String(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::Buffer(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::ByteString(tc(x))) + } else if tid == TypeId::of::() { + Self::Primitive(Primitive::U16String(tc(x))) + } else { + Self::Serializable(Box::new(x)) + } + } +} diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs deleted file mode 100644 index 002b741cf..000000000 --- a/serde_v8/src/de.rs +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use serde::de::{self, Visitor}; -use serde::Deserialize; - -use crate::error::{Error, Result}; -use crate::keys::{v8_struct_key, KeyCache}; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::{visit_magic, MagicType}; -use crate::payload::ValueType; -use crate::{magic, Buffer, ByteString, 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 -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 -where - T: Deserialize<'de>, -{ - let mut deserializer = Deserializer::new(scope, input, Some(key_cache)); - let t = T::deserialize(&mut deserializer)?; - Ok(t) -} - -macro_rules! wip { - ($method:ident) => { - fn $method(self, _v: V) -> Result - where - V: Visitor<'de>, - { - unimplemented!() - } - }; -} - -// TODO: maybe check for BigInt truncation ? -// (i.e: values larger than i64/u64 can hold) -macro_rules! deserialize_signed { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let value: $t = match self.input.is_big_int() { - true => { - let bigint = v8::Local::::try_from(self.input); - bigint.unwrap().i64_value().0 as $t - } - false => self.input.integer_value(&mut self.scope).unwrap() as $t, - }; - visitor.$vmethod(value) - } - }; -} - -macro_rules! deserialize_unsigned { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let value: $t = match self.input.is_big_int() { - true => { - let bigint = v8::Local::::try_from(self.input); - bigint.unwrap().u64_value().0 as $t - } - false => self.input.integer_value(&mut self.scope).unwrap() as $t, - }; - visitor.$vmethod(value) - } - }; -} - -impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> - for &'x mut Deserializer<'a, 'b, 's> -{ - type Error = Error; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match ValueType::from_v8(self.input) { - ValueType::Null => self.deserialize_unit(visitor), - ValueType::Bool => self.deserialize_bool(visitor), - // Handle floats & ints separately to work with loosely-typed serde_json - ValueType::Number => { - if self.input.is_uint32() { - self.deserialize_u32(visitor) - } else if self.input.is_int32() { - self.deserialize_i32(visitor) - } else { - self.deserialize_f64(visitor) - } - } - ValueType::String => self.deserialize_string(visitor), - ValueType::Array => self.deserialize_seq(visitor), - ValueType::Object => self.deserialize_map(visitor), - // Map to Vec when deserialized via deserialize_any - // e.g: for untagged enums or StringOrBuffer - ValueType::ArrayBufferView | ValueType::ArrayBuffer => { - magic::zero_copy_buf::ZeroCopyBuf::from_v8(&mut *self.scope, self.input) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } - } - } - - fn deserialize_bool(self, visitor: V) -> Result - 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(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_f32(self.input.number_value(self.scope).unwrap() as f32) - } - - fn deserialize_f64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_f64(self.input.number_value(self.scope).unwrap()) - } - - wip!(deserialize_char); - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - if self.input.is_string() { - let v8_string = v8::Local::::try_from(self.input).unwrap(); - let string = v8_string.to_rust_string_lossy(self.scope); - visitor.visit_string(string) - } else { - Err(Error::ExpectedString) - } - } - - wip!(deserialize_bytes); - wip!(deserialize_byte_buf); - - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - if self.input.is_null_or_undefined() { - visitor.visit_none() - } else { - visitor.visit_some(self) - } - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_unit() - } - - fn deserialize_unit_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - 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( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let arr = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedArray)?; - let len = arr.length(); - let obj = v8::Local::::from(arr); - let seq = SeqAccess { - pos: 0, - len, - obj, - scope: self.scope, - }; - visitor.visit_seq(seq) - } - - // Like deserialize_seq except it prefers tuple's length over input array's length - fn deserialize_tuple(self, len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - // TODO: error on length mismatch - let obj = v8::Local::::try_from(self.input).unwrap(); - let seq = SeqAccess { - pos: 0, - len: len as u32, - obj, - scope: self.scope, - }; - visitor.visit_seq(seq) - } - - // Tuple structs look just like sequences in JSON. - fn deserialize_tuple_struct( - self, - _name: &'static str, - len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_tuple(len, visitor) - } - - fn deserialize_map(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - // Assume object, then get_own_property_names - let obj = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedObject)?; - let prop_names = obj.get_own_property_names(self.scope); - let mut keys: Vec = match prop_names { - Some(names) => from_v8(self.scope, names.into()).unwrap(), - None => vec![], - }; - let keys: Vec> = keys - .drain(..) - .map(|x| x.into()) - // Filter keys to drop keys whose value is undefined - // TODO: optimize, since this doubles our get calls - .filter(|key| !obj.get(self.scope, *key).unwrap().is_undefined()) - .collect(); - - let map = MapAccess { - obj, - keys, - pos: 0, - scope: self.scope, - }; - visitor.visit_map(map) - } - - fn deserialize_struct( - self, - name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - match name { - Buffer::MAGIC_NAME => { - visit_magic(visitor, Buffer::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)?) - } - magic::Value::MAGIC_NAME => { - visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) - } - _ => { - // Regular struct - let obj = self.input.try_into().or(Err(Error::ExpectedObject))?; - visitor.visit_seq(StructAccess { - fields, - obj, - pos: 0, - scope: self.scope, - _cache: None, - }) - } - } - } - - /// 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( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - // Unit variant - if self.input.is_string() { - let payload = v8::undefined(self.scope).into(); - visitor.visit_enum(EnumAccess { - scope: self.scope, - tag: self.input, - payload, - }) - } - // Struct or tuple variant - else if self.input.is_object() { - // Assume object - let obj = v8::Local::::try_from(self.input).unwrap(); - // Unpack single-key - let tag = { - let prop_names = obj.get_own_property_names(self.scope); - let prop_names = prop_names.ok_or(Error::ExpectedEnum)?; - if prop_names.length() != 1 { - return Err(Error::LengthMismatch); - } - prop_names.get_index(self.scope, 0).unwrap() - }; - - let payload = obj.get(self.scope, tag).unwrap(); - visitor.visit_enum(EnumAccess { - scope: self.scope, - tag, - payload, - }) - } else { - // TODO: improve error - Err(Error::ExpectedEnum) - } - } - - // An identifier in Serde is the type that identifies a field of a struct or - // the variant of an enum. In JSON, struct fields and enum variants are - // represented as strings. In other formats they may be represented as - // numeric indices. - fn deserialize_identifier(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_ignored_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_none() - } -} - -struct MapAccess<'a, 'b, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'b mut v8::HandleScope<'s>, - keys: Vec>, - pos: usize, -} - -impl<'de> de::MapAccess<'de> for MapAccess<'_, '_, '_> { - type Error = Error; - - fn next_key_seed>( - &mut self, - seed: K, - ) -> Result> { - Ok(match self.keys.get(self.pos) { - Some(key) => { - let mut deserializer = Deserializer::new(self.scope, *key, None); - Some(seed.deserialize(&mut deserializer)?) - } - None => None, - }) - } - - fn next_value_seed>( - &mut self, - seed: V, - ) -> Result { - if self.pos >= self.keys.len() { - return Err(Error::LengthMismatch); - } - let key = self.keys[self.pos]; - self.pos += 1; - let v8_val = self.obj.get(self.scope, key).unwrap(); - let mut deserializer = Deserializer::new(self.scope, v8_val, None); - seed.deserialize(&mut deserializer) - } - - fn next_entry_seed< - K: de::DeserializeSeed<'de>, - V: de::DeserializeSeed<'de>, - >( - &mut self, - kseed: K, - vseed: V, - ) -> Result> { - if self.pos >= self.keys.len() { - return Ok(None); - } - let v8_key = self.keys[self.pos]; - self.pos += 1; - let mut kdeserializer = Deserializer::new(self.scope, v8_key, None); - Ok(Some((kseed.deserialize(&mut kdeserializer)?, { - let v8_val = self.obj.get(self.scope, v8_key).unwrap(); - let mut deserializer = Deserializer::new(self.scope, v8_val, None); - vseed.deserialize(&mut deserializer)? - }))) - } -} - -struct StructAccess<'a, 'b, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'b mut v8::HandleScope<'s>, - fields: &'static [&'static str], - pos: usize, - _cache: Option<&'b mut KeyCache>, -} - -impl<'de> de::SeqAccess<'de> for StructAccess<'_, '_, '_> { - type Error = Error; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result> { - if self.pos >= self.fields.len() { - return Ok(None); - } - - let pos = self.pos; - self.pos += 1; - - let field = self.fields[pos]; - let key = v8_struct_key(self.scope, field).into(); - let val = self.obj.get(self.scope, key).unwrap(); - let mut deserializer = Deserializer::new(self.scope, val, None); - match seed.deserialize(&mut deserializer) { - Ok(val) => Ok(Some(val)), - // Fallback to Ok(None) for #[serde(Default)] at cost of error quality ... - // TODO(@AaronO): double check that there's no better solution - Err(_) if val.is_undefined() => Ok(None), - Err(e) => Err(e), - } - } -} - -struct SeqAccess<'a, 'b, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'b mut v8::HandleScope<'s>, - len: u32, - pos: u32, -} - -impl<'de> de::SeqAccess<'de> for SeqAccess<'_, '_, '_> { - type Error = Error; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result> { - let pos = self.pos; - self.pos += 1; - - if pos < self.len { - let val = self.obj.get_index(self.scope, pos).unwrap(); - let mut deserializer = Deserializer::new(self.scope, val, None); - Ok(Some(seed.deserialize(&mut deserializer)?)) - } else { - Ok(None) - } - } - - fn size_hint(&self) -> Option { - Some((self.len - self.pos) as usize) - } -} - -struct EnumAccess<'a, 'b, 's> { - tag: v8::Local<'a, v8::Value>, - payload: v8::Local<'a, v8::Value>, - scope: &'b mut v8::HandleScope<'s>, - // p1: std::marker::PhantomData<&'x ()>, -} - -impl<'de, 'a, 'b, 's, 'x> de::EnumAccess<'de> for EnumAccess<'a, 'b, 's> { - type Error = Error; - type Variant = VariantDeserializer<'a, 'b, 's>; - - fn variant_seed>( - 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>( - self, - seed: T, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - seed.deserialize(&mut d) - } - - fn tuple_variant>( - self, - len: usize, - visitor: V, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - de::Deserializer::deserialize_tuple(&mut d, len, visitor) - } - - fn struct_variant>( - self, - fields: &'static [&'static str], - visitor: V, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - de::Deserializer::deserialize_struct(&mut d, "", fields, visitor) - } -} diff --git a/serde_v8/src/error.rs b/serde_v8/src/error.rs deleted file mode 100644 index e4f7f363b..000000000 --- a/serde_v8/src/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use std::fmt::{self, Display}; - -use serde::{de, ser}; - -pub type Result = std::result::Result; - -#[derive(Clone, Debug, PartialEq)] -pub enum Error { - Message(String), - - ExpectedBoolean, - ExpectedInteger, - ExpectedString, - ExpectedArray, - ExpectedMap, - ExpectedEnum, - ExpectedObject, - ExpectedBuffer, - - ExpectedUtf8, - ExpectedLatin1, - - LengthMismatch, -} - -impl ser::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl de::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl Display for Error { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::Message(msg) => formatter.write_str(msg), - err => formatter.write_str(format!("serde_v8 error: {:?}", err).as_ref()), - } - } -} - -impl std::error::Error for Error {} diff --git a/serde_v8/src/keys.rs b/serde_v8/src/keys.rs deleted file mode 100644 index 4811a87f8..000000000 --- a/serde_v8/src/keys.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2022 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>); - -// creates an optimized v8::String for a struct field -// TODO: experiment with external strings -// TODO: evaluate if own KeyCache is better than v8's dedupe -pub fn v8_struct_key<'s>( - scope: &mut v8::HandleScope<'s>, - field: &'static str, -) -> v8::Local<'s, v8::String> { - // Internalized v8 strings are significantly faster than "normal" v8 strings - // since v8 deduplicates re-used strings minimizing new allocations - // see: https://github.com/v8/v8/blob/14ac92e02cc3db38131a57e75e2392529f405f2f/include/v8.h#L3165-L3171 - v8::String::new_from_utf8( - scope, - field.as_ref(), - v8::NewStringType::Internalized, - ) - .unwrap() - - // TODO: consider external strings later - // right now non-deduped external strings (without KeyCache) - // are slower than the deduped internalized strings by ~2.5x - // since they're a new string in v8's eyes and needs to be hashed, etc... - // v8::String::new_external_onebyte_static(scope, field).unwrap() -} diff --git a/serde_v8/src/lib.rs b/serde_v8/src/lib.rs deleted file mode 100644 index 203e1d004..000000000 --- a/serde_v8/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -mod de; -mod error; -mod keys; -mod magic; -mod payload; -mod ser; -mod serializable; -pub mod utils; - -pub use de::{from_v8, from_v8_cached, Deserializer}; -pub use error::{Error, Result}; -pub use keys::KeyCache; -pub use magic::buffer::MagicBuffer as Buffer; -pub use magic::bytestring::ByteString; -pub use magic::string_or_buffer::StringOrBuffer; -pub use magic::u16string::U16String; -pub use magic::Value; -pub use ser::{to_v8, Serializer}; -pub use serializable::{Serializable, SerializablePkg}; diff --git a/serde_v8/src/magic/buffer.rs b/serde_v8/src/magic/buffer.rs deleted file mode 100644 index 484984ac5..000000000 --- a/serde_v8/src/magic/buffer.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. - -use std::ops::Deref; -use std::ops::DerefMut; -use std::sync::Mutex; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use super::zero_copy_buf::ZeroCopyBuf; -use crate::magic::transl8::impl_magic; - -// An asymmetric wrapper around ZeroCopyBuf, -// allowing us to use a single type for familiarity -pub enum MagicBuffer { - FromV8(ZeroCopyBuf), - ToV8(Mutex>>), -} -impl_magic!(MagicBuffer); - -impl MagicBuffer { - pub fn empty() -> Self { - MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice()))) - } -} - -impl Clone for MagicBuffer { - fn clone(&self) -> Self { - match self { - Self::FromV8(zbuf) => Self::FromV8(zbuf.clone()), - Self::ToV8(_) => panic!("Don't Clone a MagicBuffer sent to v8"), - } - } -} - -impl AsRef<[u8]> for MagicBuffer { - fn as_ref(&self) -> &[u8] { - &*self - } -} - -impl AsMut<[u8]> for MagicBuffer { - fn as_mut(&mut self) -> &mut [u8] { - &mut *self - } -} - -impl Deref for MagicBuffer { - type Target = [u8]; - fn deref(&self) -> &[u8] { - match self { - Self::FromV8(buf) => &*buf, - Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"), - } - } -} - -impl DerefMut for MagicBuffer { - fn deref_mut(&mut self) -> &mut [u8] { - match self { - Self::FromV8(buf) => &mut *buf, - Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"), - } - } -} - -impl From> for MagicBuffer { - fn from(buf: Box<[u8]>) -> Self { - MagicBuffer::ToV8(Mutex::new(Some(buf))) - } -} - -impl From> for MagicBuffer { - fn from(vec: Vec) -> Self { - vec.into_boxed_slice().into() - } -} - -impl ToV8 for MagicBuffer { - fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let buf: Box<[u8]> = match self { - Self::FromV8(buf) => { - let value: &[u8] = buf; - value.into() - } - Self::ToV8(x) => x.lock().unwrap().take().expect("MagicBuffer was empty"), - }; - - 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 = buf.len(); - let backing_store = - v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf); - let backing_store_shared = backing_store.make_shared(); - let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared); - Ok( - v8::Uint8Array::new(scope, ab, 0, buf_len) - .expect("Failed to create Uint8Array") - .into(), - ) - } -} - -impl FromV8 for MagicBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - Ok(Self::FromV8(ZeroCopyBuf::from_v8(scope, value)?)) - } -} diff --git a/serde_v8/src/magic/bytestring.rs b/serde_v8/src/magic/bytestring.rs deleted file mode 100644 index 43ac54b97..000000000 --- a/serde_v8/src/magic/bytestring.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. - -use std::ops::{Deref, DerefMut}; - -use super::transl8::{FromV8, ToV8}; -use crate::magic::transl8::impl_magic; -use crate::Error; - -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct ByteString(pub Vec); -impl_magic!(ByteString); - -impl ByteString { - pub fn new() -> ByteString { - ByteString(Vec::new()) - } - - pub fn with_capacity(capacity: usize) -> ByteString { - ByteString(Vec::with_capacity(capacity)) - } - - pub fn capacity(&self) -> usize { - self.0.capacity() - } - - pub fn reserve(&mut self, additional: usize) { - self.0.reserve(additional) - } - - pub fn reserve_exact(&mut self, additional: usize) { - self.0.reserve_exact(additional) - } - - pub fn shrink_to_fit(&mut self) { - self.0.shrink_to_fit() - } - - pub fn truncate(&mut self, len: usize) { - self.0.truncate(len) - } - - pub fn push(&mut self, value: u8) { - self.0.push(value) - } - - pub fn pop(&mut self) -> Option { - self.0.pop() - } -} - -impl Default for ByteString { - fn default() -> Self { - ByteString::new() - } -} - -impl Deref for ByteString { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.0.deref() - } -} - -impl DerefMut for ByteString { - fn deref_mut(&mut self) -> &mut [u8] { - self.0.deref_mut() - } -} - -impl AsRef<[u8]> for ByteString { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsMut<[u8]> for ByteString { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl ToV8 for ByteString { - fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let v = - v8::String::new_from_one_byte(scope, self, v8::NewStringType::Normal) - .unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for ByteString { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let v8str = v8::Local::::try_from(value) - .map_err(|_| Error::ExpectedString)?; - if !v8str.contains_only_onebyte() { - return Err(Error::ExpectedLatin1); - } - let len = v8str.length(); - let mut buffer = Vec::with_capacity(len); - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - #[allow(clippy::uninit_vec)] - unsafe { - buffer.set_len(len); - let written = v8str.write_one_byte( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(ByteString(buffer)) - } -} diff --git a/serde_v8/src/magic/mod.rs b/serde_v8/src/magic/mod.rs deleted file mode 100644 index bc86c6a7c..000000000 --- a/serde_v8/src/magic/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -pub mod buffer; -pub mod bytestring; -pub mod string_or_buffer; -pub mod transl8; -pub mod u16string; -mod value; -pub mod zero_copy_buf; -pub use value::Value; diff --git a/serde_v8/src/magic/string_or_buffer.rs b/serde_v8/src/magic/string_or_buffer.rs deleted file mode 100644 index edde2adcd..000000000 --- a/serde_v8/src/magic/string_or_buffer.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::ops::Deref; - -#[derive(Debug)] -pub struct StringOrBuffer(Vec); - -impl Deref for StringOrBuffer { - type Target = Vec; - fn deref(&self) -> &Vec { - &self.0 - } -} - -impl StringOrBuffer { - pub fn into_bytes(self) -> Vec { - self.0 - } -} - -impl<'de> serde::Deserialize<'de> for StringOrBuffer { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - StringOrBufferInner::deserialize(deserializer) - .map(|x| StringOrBuffer(x.into_bytes())) - } -} - -// TODO(@AaronO): explore if we can make this work with ZeroCopyBuf -#[derive(serde::Deserialize)] -#[serde(untagged)] -enum StringOrBufferInner { - #[serde(with = "serde_bytes")] - Buffer(Vec), - String(String), -} - -impl StringOrBufferInner { - fn into_bytes(self) -> Vec { - match self { - Self::String(s) => s.into_bytes(), - Self::Buffer(b) => b, - } - } -} diff --git a/serde_v8/src/magic/transl8.rs b/serde_v8/src/magic/transl8.rs deleted file mode 100644 index 458b82129..000000000 --- a/serde_v8/src/magic/transl8.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. - -//! Transerialization extends the set of serde-compatible types (for given de/serializers). -//! By "hackishly" transmuting references across serde boundaries as u64s. -//! Type-safety is enforced using special struct names for each "magic type". -//! Memory-safety relies on transerialized values being "pinned" during de/serialization. - -pub(crate) const MAGIC_FIELD: &str = "$__v8_magic_field"; - -pub(crate) trait MagicType { - const NAME: &'static str; - const MAGIC_NAME: &'static str; -} - -pub(crate) trait ToV8 { - fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error>; -} - -pub(crate) trait FromV8: Sized { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result; -} - -pub(crate) fn magic_serialize( - serializer: S, - x: &T, -) -> Result -where - S: serde::Serializer, - T: MagicType, -{ - use serde::ser::SerializeStruct; - - let mut s = serializer.serialize_struct(T::MAGIC_NAME, 1)?; - let ptr = opaque_send(x); - s.serialize_field(MAGIC_FIELD, &ptr)?; - s.end() -} - -pub(crate) fn magic_deserialize<'de, T, D>( - deserializer: D, -) -> Result -where - D: serde::Deserializer<'de>, - T: MagicType, -{ - struct ValueVisitor { - p1: std::marker::PhantomData, - } - - impl<'de, T: MagicType> serde::de::Visitor<'de> for ValueVisitor { - type Value = T; - - fn expecting( - &self, - formatter: &mut std::fmt::Formatter, - ) -> std::fmt::Result { - formatter.write_str("a ")?; - formatter.write_str(T::NAME) - } - - fn visit_u64(self, ptr: u64) -> Result - where - E: serde::de::Error, - { - // SAFETY: opaque ptr originates from visit_magic, which forgets ownership so we can take it - Ok(unsafe { opaque_take(ptr) }) - } - } - - deserializer.deserialize_struct( - T::MAGIC_NAME, - &[MAGIC_FIELD], - ValueVisitor:: { - p1: std::marker::PhantomData, - }, - ) -} - -pub(crate) fn visit_magic<'de, T, V, E>(visitor: V, x: T) -> Result -where - V: serde::de::Visitor<'de>, - E: serde::de::Error, -{ - let y = visitor.visit_u64::(opaque_send(&x)); - std::mem::forget(x); - y -} - -/// Constructs an "opaque" ptr from a reference to transerialize -pub(crate) fn opaque_send(x: &T) -> u64 { - (x as *const T) as u64 -} - -/// Copies an "opaque" ptr from a reference to an opaque ptr (transerialized) -/// NOTE: ptr-to-ptr, extra indirection -pub(crate) unsafe fn opaque_recv(ptr: &T) -> u64 { - *(ptr as *const T as *const u64) -} - -/// Transmutes an "opaque" ptr back into a reference -pub(crate) unsafe fn opaque_deref<'a, T>(ptr: u64) -> &'a T { - std::mem::transmute(ptr) -} - -/// Transmutes & copies the value from the "opaque" ptr -/// NOTE: takes ownership & requires other end to forget its ownership -pub(crate) unsafe fn opaque_take(ptr: u64) -> T { - std::mem::transmute_copy::(std::mem::transmute(ptr)) -} - -macro_rules! impl_magic { - ($t:ty) => { - impl crate::magic::transl8::MagicType for $t { - const NAME: &'static str = stringify!($t); - const MAGIC_NAME: &'static str = concat!("$__v8_magic_", stringify!($t)); - } - - impl serde::Serialize for $t { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - crate::magic::transl8::magic_serialize(serializer, self) - } - } - - impl<'de> serde::Deserialize<'de> for $t { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - crate::magic::transl8::magic_deserialize(deserializer) - } - } - }; -} -pub(crate) use impl_magic; diff --git a/serde_v8/src/magic/u16string.rs b/serde_v8/src/magic/u16string.rs deleted file mode 100644 index 39ebf88d9..000000000 --- a/serde_v8/src/magic/u16string.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::magic::transl8::impl_magic; -use crate::Error; -use std::ops::{Deref, DerefMut}; - -use super::transl8::{FromV8, ToV8}; - -#[derive(Default, PartialEq, Eq, Debug)] -pub struct U16String(pub Vec); -impl_magic!(U16String); - -impl U16String { - pub fn with_zeroes(length: usize) -> U16String { - U16String(vec![0u16; length]) - } - - pub fn truncate(&mut self, new_length: usize) { - self.0.truncate(new_length); - self.0.shrink_to_fit() - } -} - -impl Deref for U16String { - type Target = [u16]; - fn deref(&self) -> &[u16] { - self.0.deref() - } -} - -impl DerefMut for U16String { - fn deref_mut(&mut self) -> &mut [u16] { - self.0.deref_mut() - } -} - -impl AsRef<[u16]> for U16String { - fn as_ref(&self) -> &[u16] { - self.0.as_ref() - } -} - -impl AsMut<[u16]> for U16String { - fn as_mut(&mut self) -> &mut [u16] { - self.0.as_mut() - } -} - -impl ToV8 for U16String { - fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let v = - v8::String::new_from_two_byte(scope, self, v8::NewStringType::Normal) - .unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for U16String { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let v8str = v8::Local::::try_from(value) - .map_err(|_| Error::ExpectedString)?; - let len = v8str.length(); - let mut buffer = Vec::with_capacity(len); - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - #[allow(clippy::uninit_vec)] - unsafe { - buffer.set_len(len); - let written = v8str.write( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(U16String(buffer)) - } -} diff --git a/serde_v8/src/magic/value.rs b/serde_v8/src/magic/value.rs deleted file mode 100644 index 5d844fbda..000000000 --- a/serde_v8/src/magic/value.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2022 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> for Value<'s> { - fn from(v8_value: v8::Local<'s, v8::Value>) -> Self { - Self { v8_value } - } -} - -impl<'s> From> for v8::Local<'s, v8::Value> { - fn from(v: Value<'s>) -> Self { - v.v8_value - } -} - -impl ToV8 for Value<'_> { - fn to_v8<'a>( - &self, - _scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute(self.v8_value) }) - } -} - -impl FromV8 for Value<'_> { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute::(value.into()) }) - } -} diff --git a/serde_v8/src/magic/zero_copy_buf.rs b/serde_v8/src/magic/zero_copy_buf.rs deleted file mode 100644 index c63d4ba66..000000000 --- a/serde_v8/src/magic/zero_copy_buf.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. - -use std::cell::Cell; -use std::ops::Deref; -use std::ops::DerefMut; - -use super::transl8::FromV8; - -/// A ZeroCopyBuf encapsulates a slice that's been borrowed from a JavaScript -/// ArrayBuffer object. JavaScript objects can normally be garbage collected, -/// but the existence of a ZeroCopyBuf inhibits this until it is dropped. It -/// behaves much like an Arc<[u8]>. -/// -/// # Cloning -/// Cloning a ZeroCopyBuf does not clone the contents of the buffer, -/// it creates a new reference to that buffer. -/// -/// To actually clone the contents of the buffer do -/// `let copy = Vec::from(&*zero_copy_buf);` -#[derive(Clone)] -pub struct ZeroCopyBuf { - backing_store: v8::SharedRef, - byte_offset: usize, - byte_length: usize, -} - -unsafe impl Send for ZeroCopyBuf {} - -impl ZeroCopyBuf { - pub fn from_buffer( - buffer: v8::Local, - byte_offset: usize, - byte_length: usize, - ) -> Result { - let backing_store = buffer.get_backing_store(); - match backing_store.is_shared() { - true => Err(v8::DataError::BadType { - actual: "shared ArrayBufferView", - expected: "non-shared ArrayBufferView", - }), - false => Ok(Self { - backing_store, - byte_offset, - byte_length, - }), - } - } - - pub fn from_view( - scope: &mut v8::HandleScope, - view: v8::Local, - ) -> Result { - let buffer = view.buffer(scope).ok_or(v8::DataError::NoData { - expected: "view to have a buffer", - })?; - Self::from_buffer(buffer, view.byte_offset(), view.byte_length()) - } -} - -impl FromV8 for ZeroCopyBuf { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - if value.is_array_buffer() { - value - .try_into() - .and_then(|b| Self::from_buffer(b, 0, b.byte_length())) - } else { - value - .try_into() - .and_then(|view| Self::from_view(scope, view)) - } - .map_err(|_| crate::Error::ExpectedBuffer) - } -} - -impl Deref for ZeroCopyBuf { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - get_backing_store_slice( - &self.backing_store, - self.byte_offset, - self.byte_length, - ) - } - } -} - -impl DerefMut for ZeroCopyBuf { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - get_backing_store_slice_mut( - &self.backing_store, - self.byte_offset, - self.byte_length, - ) - } - } -} - -impl AsRef<[u8]> for ZeroCopyBuf { - fn as_ref(&self) -> &[u8] { - &*self - } -} - -impl AsMut<[u8]> for ZeroCopyBuf { - fn as_mut(&mut self) -> &mut [u8] { - &mut *self - } -} - -unsafe fn get_backing_store_slice( - backing_store: &v8::SharedRef, - byte_offset: usize, - byte_length: usize, -) -> &[u8] { - let cells: *const [Cell] = - &backing_store[byte_offset..byte_offset + byte_length]; - let bytes = cells as *const [u8]; - &*bytes -} - -#[allow(clippy::mut_from_ref)] -unsafe fn get_backing_store_slice_mut( - backing_store: &v8::SharedRef, - byte_offset: usize, - byte_length: usize, -) -> &mut [u8] { - let cells: *const [Cell] = - &backing_store[byte_offset..byte_offset + byte_length]; - let bytes = cells as *const _ as *mut [u8]; - &mut *bytes -} diff --git a/serde_v8/src/payload.rs b/serde_v8/src/payload.rs deleted file mode 100644 index c9f3e85aa..000000000 --- a/serde_v8/src/payload.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2022 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, - String, - Array, - ArrayBuffer, - ArrayBufferView, - Object, -} - -impl ValueType { - pub fn from_v8(v: v8::Local) -> 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_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/src/ser.rs b/serde_v8/src/ser.rs deleted file mode 100644 index 5241aeaef..000000000 --- a/serde_v8/src/ser.rs +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use serde::ser; -use serde::ser::Serialize; - -use std::cell::RefCell; - -use crate::error::{Error, Result}; -use crate::keys::v8_struct_key; -use crate::magic::transl8::MAGIC_FIELD; -use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8}; -use crate::{magic, Buffer, ByteString, U16String}; - -type JsValue<'s> = v8::Local<'s, v8::Value>; -type JsResult<'s> = Result>; - -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, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &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, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &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>, - scope: ScopePtr<'a, 'b, 'c>, -} - -impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> 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( - &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( - &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( - &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>, - values: Vec>, -} - -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( - &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, -} - -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:: {}, - } - } -} - -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( - &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: &T = unsafe { opaque_deref(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> { - Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>), - MagicBuffer(MagicalSerializer<'a, 'b, 'c, Buffer>), - MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), - MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), - Regular(ObjectSerializer<'a, 'b, 'c>), -} - -impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - match self { - StructSerializers::Magic(s) => s.serialize_field(key, value), - StructSerializers::MagicBuffer(s) => s.serialize_field(key, value), - StructSerializers::MagicByteString(s) => s.serialize_field(key, value), - StructSerializers::MagicU16String(s) => s.serialize_field(key, value), - StructSerializers::Regular(s) => s.serialize_field(key, value), - } - } - - fn end(self) -> JsResult<'a> { - match self { - StructSerializers::Magic(s) => s.end(), - StructSerializers::MagicBuffer(s) => s.end(), - StructSerializers::MagicByteString(s) => s.end(), - StructSerializers::MagicU16String(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>, - values: Vec>, -} - -impl<'a, 'b, 'c> MapSerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> 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(&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( - &mut self, - value: &T, - ) -> Result<()> { - let v8_value = value.serialize(Serializer::new(self.scope))?; - self.values.push(v8_value); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - debug_assert!(self.keys.len() == self.values.len()); - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope).into(); - let obj = v8::Object::with_prototype_and_properties( - scope, - null, - &self.keys[..], - &self.values[..], - ); - Ok(obj.into()) - } -} - -pub struct Serializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, -} - -impl<'a, 'b, 'c> Serializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { - Serializer { scope } - } -} - -macro_rules! forward_to { - ($($name:ident($ty:ty, $to:ident, $lt:lifetime);)*) => { - $(fn $name(self, v: $ty) -> JsResult<$lt> { - self.$to(v as _) - })* - }; -} - -impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { - type Ok = v8::Local<'a, v8::Value>; - type Error = Error; - - type SerializeSeq = ArraySerializer<'a, 'b, 'c>; - type SerializeTuple = ArraySerializer<'a, 'b, 'c>; - type SerializeTupleStruct = ArraySerializer<'a, 'b, 'c>; - type SerializeTupleVariant = - VariantSerializer<'a, 'b, 'c, ArraySerializer<'a, 'b, 'c>>; - type SerializeMap = MapSerializer<'a, 'b, 'c>; - type SerializeStruct = StructSerializers<'a, 'b, 'c>; - type SerializeStructVariant = - VariantSerializer<'a, 'b, 'c, StructSerializers<'a, 'b, 'c>>; - - forward_to! { - serialize_i8(i8, serialize_i32, 'a); - serialize_i16(i16, serialize_i32, 'a); - - serialize_u8(u8, serialize_u32, 'a); - serialize_u16(u16, serialize_u32, 'a); - - serialize_f32(f32, serialize_f64, 'a); - serialize_u64(u64, serialize_f64, 'a); - serialize_i64(i64, serialize_f64, 'a); - } - - fn serialize_i32(self, v: i32) -> JsResult<'a> { - Ok(v8::Integer::new(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_u32(self, v: u32) -> JsResult<'a> { - Ok(v8::Integer::new_from_unsigned(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_f64(self, v: f64) -> JsResult<'a> { - Ok(v8::Number::new(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_bool(self, v: bool) -> JsResult<'a> { - Ok(v8::Boolean::new(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_char(self, _v: char) -> JsResult<'a> { - unimplemented!(); - } - - fn serialize_str(self, v: &str) -> JsResult<'a> { - v8::String::new(&mut self.scope.borrow_mut(), v) - .map(|v| v.into()) - .ok_or(Error::ExpectedString) - } - - fn serialize_bytes(self, _v: &[u8]) -> JsResult<'a> { - // TODO: investigate using Uint8Arrays - unimplemented!() - } - - fn serialize_none(self) -> JsResult<'a> { - Ok(v8::null(&mut self.scope.borrow_mut()).into()) - } - - fn serialize_some(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( - self, - _name: &'static str, - value: &T, - ) -> JsResult<'a> { - value.serialize(self) - } - - fn serialize_newtype_variant( - 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) -> Result { - Ok(ArraySerializer::new(self.scope, len)) - } - - fn serialize_tuple(self, len: usize) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - len: usize, - ) -> Result { - self.serialize_tuple(len) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - Ok(VariantSerializer::new( - self.scope, - variant, - self.serialize_tuple_struct(variant, len)?, - )) - } - - fn serialize_map(self, len: Option) -> Result { - // 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 { - match name { - ByteString::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicByteString(m)) - } - U16String::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicU16String(m)) - } - Buffer::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicBuffer(m)) - } - magic::Value::MAGIC_NAME => { - let m = MagicalSerializer::>::new(self.scope); - Ok(StructSerializers::Magic(m)) - } - _ => { - // Regular structs - let o = ObjectSerializer::new(self.scope, len); - Ok(StructSerializers::Regular(o)) - } - } - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - let scope = self.scope; - let x = self.serialize_struct(variant, len)?; - Ok(VariantSerializer::new(scope, variant, x)) - } -} diff --git a/serde_v8/src/serializable.rs b/serde_v8/src/serializable.rs deleted file mode 100644 index 533b3f83f..000000000 --- a/serde_v8/src/serializable.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use std::any::TypeId; -use std::mem::transmute_copy; - -use crate::Buffer; -use crate::ByteString; -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>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error>; -} - -/// Allows all implementors of `serde::Serialize` to implement Serializable -impl Serializable for T { - fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - crate::to_v8(scope, self) - } -} - -/// SerializablePkg exists to provide a fast path for op returns, -/// allowing them to avoid boxing primtives (ints/floats/bool/unit/...) -pub enum SerializablePkg { - Primitive(Primitive), - Serializable(Box), -} - -impl SerializablePkg { - pub fn to_v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, 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), - Buffer(Buffer), - ByteString(ByteString), - U16String(U16String), -} - -impl serde::Serialize for Primitive { - fn serialize(&self, s: S) -> Result - 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::Buffer(x) => x.serialize(s), - Self::ByteString(x) => x.serialize(s), - Self::U16String(x) => x.serialize(s), - } - } -} - -impl From for SerializablePkg { - fn from(x: T) -> Self { - #[inline(always)] - fn tc(src: T) -> U { - let x = unsafe { transmute_copy(&src) }; - std::mem::forget(src); - x - } - - let tid = TypeId::of::(); - if tid == TypeId::of::<()>() { - Self::Primitive(Primitive::Unit) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Bool(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int8(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int16(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt8(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt16(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Float32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Float64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::String(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Buffer(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::ByteString(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::U16String(tc(x))) - } else { - Self::Serializable(Box::new(x)) - } - } -} diff --git a/serde_v8/src/utils.rs b/serde_v8/src/utils.rs deleted file mode 100644 index 427ac5f2f..000000000 --- a/serde_v8/src/utils.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2022 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() { - 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(); -} diff --git a/serde_v8/utils.rs b/serde_v8/utils.rs new file mode 100644 index 000000000..427ac5f2f --- /dev/null +++ b/serde_v8/utils.rs @@ -0,0 +1,33 @@ +// Copyright 2018-2022 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() { + 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(); +} -- cgit v1.2.3