use bytes::BytesMut;
use libc::c_void;
use std::borrow::Cow;
use std::marker::PhantomData;
use std::rc::Rc;
pub trait RustToV8<'a> {
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value>;
}
pub trait RustToV8NoScope<'a> {
fn to_v8(self) -> v8::Local<'a, v8::Value>;
}
pub trait RustToV8RetVal<'a>: RustToV8<'a> {
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>);
}
pub trait RustToV8Fallible<'a> {
fn to_v8_fallible(
self,
scope: &mut v8::HandleScope<'a>,
) -> serde_v8::Result<v8::Local<'a, v8::Value>>;
}
pub trait RustToV8RetValFallible<'a>: RustToV8Fallible<'a> {
fn to_v8_rv_fallible(
self,
scope: &mut v8::HandleScope<'a>,
rv: &mut v8::ReturnValue<'a>,
) -> serde_v8::Result<()>;
}
impl<'a, T> RustToV8<'a> for Option<T>
where
T: RustToV8<'a>,
{
#[inline(always)]
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
if let Some(value) = self {
value.to_v8(scope)
} else {
v8::null(scope).into()
}
}
}
impl<'a, T> RustToV8RetVal<'a> for Option<T>
where
T: RustToV8RetVal<'a>,
{
#[inline(always)]
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>) {
if let Some(value) = self {
value.to_v8_rv(rv)
} else {
rv.set_null()
}
}
}
impl<'a, T> RustToV8Fallible<'a> for Option<T>
where
T: RustToV8Fallible<'a>,
{
#[inline(always)]
fn to_v8_fallible(
self,
scope: &mut v8::HandleScope<'a>,
) -> serde_v8::Result<v8::Local<'a, v8::Value>> {
if let Some(value) = self {
value.to_v8_fallible(scope)
} else {
Ok(v8::null(scope).into())
}
}
}
impl<'a, T> RustToV8RetValFallible<'a> for Option<T>
where
T: RustToV8RetValFallible<'a>,
{
#[inline(always)]
fn to_v8_rv_fallible(
self,
scope: &mut v8::HandleScope<'a>,
rv: &mut v8::ReturnValue<'a>,
) -> serde_v8::Result<()> {
if let Some(value) = self {
value.to_v8_rv_fallible(scope, rv)
} else {
rv.set_null();
Ok(())
}
}
}
#[repr(transparent)]
pub struct RustToV8Marker<M, T>(T, PhantomData<M>);
impl<M: Marker, T> From<T> for RustToV8Marker<M, T> {
#[inline(always)]
fn from(value: T) -> Self {
RustToV8Marker(value, PhantomData)
}
}
pub struct SerdeMarker;
impl Marker for SerdeMarker {}
pub struct SmiMarker;
impl Marker for SmiMarker {}
pub struct NumberMarker;
impl Marker for NumberMarker {}
pub struct ArrayBufferMarker;
impl Marker for ArrayBufferMarker {}
trait Marker {}
macro_rules! to_v8 {
(( $( $ty:ty ),+ ) : |$value:ident, $scope:ident| $block:expr) => {
$(
impl <'a> RustToV8<'a> for $ty {
#[inline(always)]
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
let $value = self;
let $scope = scope;
v8::Local::<v8::Value>::from($block)
}
}
)+
};
($ty:ty : |$value:ident, $scope:ident| $block:expr) => {
to_v8!(( $ty ) : |$value, $scope| $block);
};
}
macro_rules! to_v8_retval {
(( $( $ty:ty ),+ ) : |$value:ident, $rv:ident| $block:expr) => {
$(
impl <'a> RustToV8RetVal<'a> for $ty {
#[inline(always)]
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>) {
let $value = self;
let $rv = rv;
$block
}
}
)+
};
($ty:ty : |$rv:ident, $scope:ident| $block:expr) => {
to_v8_retval!(( $ty ) : |$rv, $scope| $block);
};
}
macro_rules! to_v8_fallible {
(( $( $ty:ty ),+ ) : |$value:ident, $scope:ident| $block:expr) => {
$(
#[allow(clippy::needless_borrow)]
impl <'a> RustToV8Fallible<'a> for $ty {
#[inline(always)]
fn to_v8_fallible(self, scope: &mut v8::HandleScope<'a>) -> serde_v8::Result<v8::Local<'a, v8::Value>> {
let $value = self;
let $scope = scope;
let res = $block;
match res {
Ok(v) => Ok(v.into()),
Err(err) => Err(err),
}
}
}
)+
};
($ty:ty : |$value:ident, $scope:ident| $block:expr) => {
to_v8_fallible!(( $ty ) : |$value, $scope| $block);
};
}
macro_rules! to_v8_retval_fallible {
(( $( $ty:ty ),+ ) : |$value:ident, $scope: ident, $rv:ident| $block:expr) => {
$(
impl <'a> RustToV8RetValFallible<'a> for $ty {
#[inline(always)]
fn to_v8_rv_fallible(self, scope: &mut v8::HandleScope<'a>, rv: &mut v8::ReturnValue<'a>) -> serde_v8::Result<()>{
let $value = self;
let $scope = scope;
let $rv = rv;
$block
}
}
)+
};
($ty:ty : |$value:ident, $scope: ident, $rv:ident| $block:expr) => {
to_v8_retval_fallible!(( $ty ) : |$value, $scope, $rv| $block);
};
}
to_v8!((): |_value, scope| v8::null(scope));
to_v8_retval!((): |_value, rv| rv.set_null());
to_v8!(bool: |value, scope| v8::Boolean::new(scope, value as _));
to_v8_retval!(bool: |value, rv| rv.set_bool(value));
to_v8!((u8, u16, u32): |value, scope| v8::Integer::new_from_unsigned(scope, value as _));
to_v8_retval!((u8, u16, u32): |value, rv| rv.set_uint32(value as _));
to_v8!((i8, i16, i32): |value, scope| v8::Integer::new(scope, value as _));
to_v8_retval!((i8, i16, i32): |value, rv| rv.set_int32(value as _));
to_v8!((f32, f64): |value, scope| v8::Number::new(scope, value as _));
to_v8_retval!((f32, f64): |value, rv| rv.set_double(value as _));
to_v8!((*const c_void, *mut c_void): |value, scope| {
if value.is_null() {
v8::Local::<v8::Value>::from(v8::null(scope))
} else {
v8::Local::<v8::Value>::from(v8::External::new(scope, value as _))
}
});
to_v8!((u64, usize): |value, scope| v8::BigInt::new_from_u64(scope, value as _));
to_v8!((i64, isize): |value, scope| v8::BigInt::new_from_i64(scope, value as _));
to_v8_fallible!((String, Cow<'a, str>, &'a str): |value, scope| v8::String::new(scope, &value).ok_or_else(|| serde_v8::Error::Message("failed to allocate string; buffer exceeds maximum length".into())));
to_v8_retval_fallible!((String, Cow<'a, str>, &'a str): |value, scope, rv| {
if value.is_empty() {
rv.set_empty_string();
} else {
rv.set(value.to_v8_fallible(scope)?);
}
Ok(())
});
to_v8_fallible!(Cow<'a, [u8]>: |value, scope| v8::String::new_from_one_byte(scope, &value, v8::NewStringType::Normal).ok_or_else(|| serde_v8::Error::Message("failed to allocate string; buffer exceeds maximum length".into())));
to_v8_retval_fallible!(Cow<'a, [u8]>: |value, scope, rv| {
if value.is_empty() {
rv.set_empty_string();
} else {
rv.set(value.to_v8_fallible(scope)?);
}
Ok(())
});
to_v8_fallible!(serde_v8::V8Slice<u8>: |value, scope| {
value.into_v8_local(scope).ok_or_else(|| serde_v8::Error::Message("failed to allocate array".into()))
});
to_v8!(RustToV8Marker<ArrayBufferMarker, serde_v8::V8Slice<u8>>: |value, scope| {
value.0.into_v8_unsliced_arraybuffer_local(scope)
});
to_v8_fallible!(serde_v8::V8Slice<u32>: |value, scope| {
value.into_v8_local(scope).ok_or_else(|| serde_v8::Error::Message("failed to allocate array".into()))
});
to_v8!(RustToV8Marker<ArrayBufferMarker, serde_v8::JsBuffer>: |value, scope| {
RustToV8Marker::<ArrayBufferMarker, _>::from(value.0.into_parts()).to_v8(scope)
});
to_v8_fallible!(serde_v8::JsBuffer: |value, scope| {
value.into_parts().to_v8_fallible(scope)
});
to_v8!(RustToV8Marker<ArrayBufferMarker, Box<[u8]>>: |buf, scope| {
let buf = buf.0;
if buf.is_empty() {
v8::ArrayBuffer::new(scope, 0)
} else {
let backing_store =
v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf);
let backing_store_shared = backing_store.make_shared();
v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared)
}
});
to_v8_fallible!(Box<[u8]>: |buf, scope| {
let len = buf.len();
let ab = unsafe { v8::Local::cast(RustToV8Marker::<ArrayBufferMarker, _>::from(buf).to_v8(scope)) };
v8::Uint8Array::new(scope, ab, 0, len).ok_or_else(|| serde_v8::Error::Message("failed to allocate array".into()))
});
to_v8!(RustToV8Marker<ArrayBufferMarker, Vec<u8>>: |value, scope| {
RustToV8Marker::<ArrayBufferMarker, _>::from(value.0.into_boxed_slice()).to_v8(scope)
});
to_v8_fallible!(Vec<u8>: |value, scope| value.into_boxed_slice().to_v8_fallible(scope));
to_v8!(RustToV8Marker<ArrayBufferMarker, BytesMut>: |value, scope| {
let value = value.0;
let ptr = value.as_ptr();
let len = value.len() as _;
let rc = Rc::into_raw(Rc::new(value)) as *const c_void;
extern "C" fn drop_rc(_ptr: *mut c_void, _len: usize, data: *mut c_void) {
unsafe { drop(Rc::<BytesMut>::from_raw(data as _)) }
}
let backing_store_shared = unsafe {
v8::ArrayBuffer::new_backing_store_from_ptr(
ptr as _, len, drop_rc, rc as _,
)
}
.make_shared();
v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared)
});
to_v8_fallible!(BytesMut: |buf, scope| {
let len = buf.len();
let ab = unsafe { v8::Local::cast(RustToV8Marker::<ArrayBufferMarker, _>::from(buf).to_v8(scope)) };
v8::Uint8Array::new(scope, ab, 0, len).ok_or_else(|| serde_v8::Error::Message("failed to allocate array".into()))
});
impl<'a, T: serde::Serialize> RustToV8Fallible<'a>
for RustToV8Marker<SerdeMarker, T>
{
#[inline(always)]
fn to_v8_fallible(
self,
scope: &mut v8::HandleScope<'a>,
) -> serde_v8::Result<v8::Local<'a, v8::Value>> {
serde_v8::to_v8(scope, self.0)
}
}
macro_rules! smi_to_v8 {
($($ty:ident),*) => {
$(
impl<'a> RustToV8<'a> for RustToV8Marker<SmiMarker, $ty>
{
#[inline(always)]
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
v8::Integer::new(scope, self.0 as _).into()
}
}
impl <'a> RustToV8RetVal<'a> for RustToV8Marker<SmiMarker, $ty> {
#[inline(always)]
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>) {
rv.set_int32(self.0 as _)
}
}
)*
};
}
smi_to_v8!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
macro_rules! number_64_bit_to_v8 {
($($ty:ident),*) => {
$(
impl<'a> RustToV8<'a> for RustToV8Marker<NumberMarker, $ty>
{
#[inline(always)]
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
v8::Number::new(scope, self.0 as _).into()
}
}
impl <'a> RustToV8RetVal<'a> for RustToV8Marker<NumberMarker, $ty> {
#[inline(always)]
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>) {
rv.set_double(self.0 as _)
}
}
)*
};
}
number_64_bit_to_v8!(u64, usize, i64, isize);
impl<'a, T> RustToV8<'a> for v8::Global<T>
where
v8::Local<'a, v8::Value>: From<v8::Local<'a, T>>,
{
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
v8::Local::new(scope, self).into()
}
}
impl<'a, T> RustToV8NoScope<'a> for v8::Local<'a, T>
where
v8::Local<'a, v8::Value>: From<v8::Local<'a, T>>,
{
#[inline(always)]
fn to_v8(self) -> v8::Local<'a, v8::Value> {
self.into()
}
}
impl<'a, T> RustToV8<'a> for Option<v8::Local<'a, T>>
where
v8::Local<'a, v8::Value>: From<v8::Local<'a, T>>,
{
#[inline(always)]
fn to_v8(self, scope: &mut v8::HandleScope<'a>) -> v8::Local<'a, v8::Value> {
if let Some(v) = self {
v.to_v8()
} else {
v8::null(scope).into()
}
}
}
impl<'a, T> RustToV8RetVal<'a> for Option<v8::Local<'a, T>>
where
v8::Local<'a, v8::Value>: From<v8::Local<'a, T>>,
{
fn to_v8_rv(self, rv: &mut v8::ReturnValue<'a>) {
if let Some(v) = self {
rv.set(v.to_v8())
} else {
rv.set_null()
}
}
}