diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs index 69e618cb3a..2deb5da396 100644 --- a/serde_v8/src/de.rs +++ b/serde_v8/src/de.rs @@ -134,9 +134,20 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> ValueType::ArrayBufferView => { v8::Local::::try_from(self.input) .and_then(|view| { - magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view) + magic::zero_copy_buf::ZeroCopyBuf::try_from(( + &mut *self.scope, + view, + )) }) - .map_err(|_| Error::ExpectedInteger) + .map_err(|_| Error::ExpectedBuffer) + .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) + } + ValueType::ArrayBuffer => { + v8::Local::::try_from(self.input) + .and_then(|buffer| { + magic::zero_copy_buf::ZeroCopyBuf::try_from(buffer) + }) + .map_err(|_| Error::ExpectedBuffer) .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) } } @@ -339,12 +350,20 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> // Magic Buffer if name == magic::buffer::BUF_NAME { - let zero_copy_buf = - v8::Local::::try_from(self.input) + let zero_copy_buf = match self.input.is_array_buffer() { + // ArrayBuffer + true => v8::Local::::try_from(self.input) + .and_then(magic::zero_copy_buf::ZeroCopyBuf::try_from), + // maybe ArrayBufferView + false => v8::Local::::try_from(self.input) .and_then(|view| { - magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view) - }) - .map_err(|_| Error::ExpectedArray)?; + magic::zero_copy_buf::ZeroCopyBuf::try_from(( + &mut *self.scope, + view, + )) + }), + } + .map_err(|_| Error::ExpectedBuffer)?; let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) }; return visitor.visit_bytes(&data); } diff --git a/serde_v8/src/error.rs b/serde_v8/src/error.rs index 523dd62b01..6f4305536e 100644 --- a/serde_v8/src/error.rs +++ b/serde_v8/src/error.rs @@ -16,6 +16,7 @@ pub enum Error { ExpectedMap, ExpectedEnum, ExpectedObject, + ExpectedBuffer, ExpectedUtf8, diff --git a/serde_v8/src/magic/buffer.rs b/serde_v8/src/magic/buffer.rs index 80f2f8bc77..e6f85324e3 100644 --- a/serde_v8/src/magic/buffer.rs +++ b/serde_v8/src/magic/buffer.rs @@ -1,5 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use std::convert::TryFrom; use std::fmt; use std::ops::Deref; use std::ops::DerefMut; @@ -15,25 +16,32 @@ pub enum MagicBuffer { } impl MagicBuffer { - pub fn new<'s>( - scope: &mut v8::HandleScope<'s>, - view: v8::Local, - ) -> Self { - Self::try_new(scope, view).unwrap() - } - - pub fn try_new<'s>( - scope: &mut v8::HandleScope<'s>, - view: v8::Local, - ) -> Result { - Ok(Self::FromV8(ZeroCopyBuf::try_new(scope, view)?)) - } - pub fn empty() -> Self { MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice()))) } } +impl<'s> TryFrom> for MagicBuffer { + type Error = v8::DataError; + fn try_from(buffer: v8::Local) -> Result { + Ok(Self::FromV8(ZeroCopyBuf::try_from(buffer)?)) + } +} + +// TODO(@AaronO): consider streamlining this as "ScopedValue" ? +type ScopedView<'a, 'b, 's> = ( + &'s mut v8::HandleScope<'a>, + v8::Local<'b, v8::ArrayBufferView>, +); +impl<'a, 'b, 's> TryFrom> for MagicBuffer { + type Error = v8::DataError; + fn try_from( + scoped_view: ScopedView<'a, 'b, 's>, + ) -> Result { + Ok(Self::FromV8(ZeroCopyBuf::try_from(scoped_view)?)) + } +} + impl Clone for MagicBuffer { fn clone(&self) -> Self { match self { diff --git a/serde_v8/src/magic/zero_copy_buf.rs b/serde_v8/src/magic/zero_copy_buf.rs index b536f58598..30acd8137c 100644 --- a/serde_v8/src/magic/zero_copy_buf.rs +++ b/serde_v8/src/magic/zero_copy_buf.rs @@ -25,31 +25,46 @@ pub struct ZeroCopyBuf { unsafe impl Send for ZeroCopyBuf {} impl ZeroCopyBuf { - pub fn new<'s>( - scope: &mut v8::HandleScope<'s>, - view: v8::Local, - ) -> Self { - Self::try_new(scope, view).unwrap() - } - - pub fn try_new<'s>( - scope: &mut v8::HandleScope<'s>, - view: v8::Local, + pub fn from_buffer( + buffer: v8::Local, + byte_offset: usize, + byte_length: usize, ) -> Result { - let backing_store = view.buffer(scope).unwrap().get_backing_store(); - if backing_store.is_shared() { - return Err(v8::DataError::BadType { + 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, + }), } - let byte_offset = view.byte_offset(); - let byte_length = view.byte_length(); - Ok(Self { - backing_store, - byte_offset, - byte_length, - }) + } +} + +impl<'s> TryFrom> for ZeroCopyBuf { + type Error = v8::DataError; + fn try_from(buffer: v8::Local) -> Result { + Self::from_buffer(buffer, 0, buffer.byte_length()) + } +} + +// TODO(@AaronO): consider streamlining this as "ScopedValue" ? +type ScopedView<'a, 'b, 's> = ( + &'s mut v8::HandleScope<'a>, + v8::Local<'b, v8::ArrayBufferView>, +); +impl<'a, 'b, 's> TryFrom> for ZeroCopyBuf { + type Error = v8::DataError; + fn try_from( + scoped_view: ScopedView<'a, 'b, 's>, + ) -> Result { + let (scope, view) = scoped_view; + let buffer = view.buffer(scope).unwrap(); + Self::from_buffer(buffer, view.byte_offset(), view.byte_length()) } } diff --git a/serde_v8/src/payload.rs b/serde_v8/src/payload.rs index 1450664036..c9f3e85aa7 100644 --- a/serde_v8/src/payload.rs +++ b/serde_v8/src/payload.rs @@ -11,6 +11,7 @@ pub enum ValueType { Number, String, Array, + ArrayBuffer, ArrayBufferView, Object, } @@ -25,6 +26,8 @@ impl ValueType { 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() { diff --git a/serde_v8/tests/de.rs b/serde_v8/tests/de.rs index 2b85d88397..1253ea92b5 100644 --- a/serde_v8/tests/de.rs +++ b/serde_v8/tests/de.rs @@ -2,6 +2,7 @@ use serde::Deserialize; use serde_v8::utils::{js_exec, v8_do}; +use serde_v8::Buffer; use serde_v8::Error; #[derive(Debug, Deserialize, PartialEq)] @@ -192,6 +193,29 @@ fn de_string_or_buffer() { ); } +#[test] +fn de_buffers() { + // ArrayBufferView + dedo("new Uint8Array([97])", |scope, v| { + let buf: Buffer = serde_v8::from_v8(scope, v).unwrap(); + assert_eq!(&*buf, &[97]); + }); + + // ArrayBuffer + dedo("(new Uint8Array([97])).buffer", |scope, v| { + let buf: Buffer = serde_v8::from_v8(scope, v).unwrap(); + assert_eq!(&*buf, &[97]); + }); + + dedo( + "(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))", + |scope, v| { + let buf: Buffer = serde_v8::from_v8(scope, v).unwrap(); + assert_eq!(&*buf, &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); + }, + ); +} + //// // JSON tests: serde_json::Value compatibility ////