feat(serde_v8): support BigInt serialization (#18225)

This commit enables serializing `v8::BigInt` to `num_bigint::BigInt`
in Rust.

Pre-requisite for sub upcoming feature work.
This commit is contained in:
Luca Casonato 2023-03-16 17:59:47 +01:00 committed by GitHub
parent 1a3c2e2f1d
commit b99c431ac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 0 deletions

1
Cargo.lock generated
View File

@ -4104,6 +4104,7 @@ dependencies = [
"bencher",
"bytes",
"derive_more",
"num-bigint",
"serde",
"serde_bytes",
"serde_json",

View File

@ -101,6 +101,7 @@ log = "=0.4.17"
lsp-types = "=0.93.2" # used by tower-lsp and "proposed" feature is unstable in patch releases
lzzzz = "1.0"
notify = "=5.0.0"
num-bigint = "0.4"
once_cell = "1.17.1"
os_pipe = "=1.0.1"
parking_lot = "0.12.0"

View File

@ -16,6 +16,7 @@ path = "lib.rs"
[dependencies]
bytes.workspace = true
derive_more = "0.99.17"
num-bigint.workspace = true
serde.workspace = true
serde_bytes.workspace = true
smallvec = { workspace = true, features = ["union"] }

View File

@ -13,6 +13,7 @@ use crate::magic::transl8::visit_magic;
use crate::magic::transl8::FromV8;
use crate::magic::transl8::MagicType;
use crate::payload::ValueType;
use crate::BigInt;
use crate::ByteString;
use crate::DetachedBuffer;
use crate::StringOrBuffer;
@ -348,6 +349,9 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
StringOrBuffer::MAGIC_NAME => {
visit_magic(visitor, StringOrBuffer::from_v8(self.scope, self.input)?)
}
BigInt::MAGIC_NAME => {
visit_magic(visitor, BigInt::from_v8(self.scope, self.input)?)
}
magic::Value::MAGIC_NAME => {
visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?)
}

View File

@ -23,6 +23,7 @@ pub enum Error {
ExpectedBuffer,
ExpectedDetachable,
ExpectedExternal,
ExpectedBigInt,
ExpectedUtf8,
ExpectedLatin1,

View File

@ -15,6 +15,7 @@ pub use de::Deserializer;
pub use error::Error;
pub use error::Result;
pub use keys::KeyCache;
pub use magic::bigint::BigInt;
pub use magic::buffer::ZeroCopyBuf;
pub use magic::bytestring::ByteString;
pub use magic::detached_buffer::DetachedBuffer;

76
serde_v8/magic/bigint.rs Normal file
View File

@ -0,0 +1,76 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use smallvec::smallvec;
use smallvec::SmallVec;
use super::transl8::FromV8;
use super::transl8::ToV8;
use crate::magic::transl8::impl_magic;
use crate::Error;
#[derive(
PartialEq,
Eq,
Clone,
Debug,
Default,
derive_more::Deref,
derive_more::DerefMut,
derive_more::AsRef,
derive_more::AsMut,
)]
#[as_mut(forward)]
#[as_ref(forward)]
pub struct BigInt(num_bigint::BigInt);
impl_magic!(BigInt);
impl ToV8 for BigInt {
fn to_v8<'a>(
&mut self,
scope: &mut v8::HandleScope<'a>,
) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
let (sign, words) = self.0.to_u64_digits();
let sign_bit = sign == num_bigint::Sign::Minus;
let v = v8::BigInt::new_from_words(scope, sign_bit, &words).unwrap();
Ok(v.into())
}
}
impl FromV8 for BigInt {
fn from_v8(
_scope: &mut v8::HandleScope,
value: v8::Local<v8::Value>,
) -> Result<Self, crate::Error> {
let v8bigint = v8::Local::<v8::BigInt>::try_from(value)
.map_err(|_| Error::ExpectedBigInt)?;
let word_count = v8bigint.word_count();
let mut words: SmallVec<[u64; 1]> = smallvec![0u64; word_count];
let (sign_bit, _words) = v8bigint.to_words_array(&mut words);
let sign = match sign_bit {
true => num_bigint::Sign::Minus,
false => num_bigint::Sign::Plus,
};
// SAFETY: Because the alignment of u64 is 8, the alignment of u32 is 4, and
// the size of u64 is 8, the size of u32 is 4, the alignment of u32 is a
// factor of the alignment of u64, and the size of u32 is a factor of the
// size of u64, we can safely transmute the slice of u64 to a slice of u32.
let (prefix, slice, suffix) = unsafe { words.align_to::<u32>() };
assert!(prefix.is_empty());
assert!(suffix.is_empty());
assert_eq!(slice.len(), words.len() * 2);
let big_int = num_bigint::BigInt::from_slice(sign, slice);
Ok(Self(big_int))
}
}
impl From<num_bigint::BigInt> for BigInt {
fn from(big_int: num_bigint::BigInt) -> Self {
Self(big_int)
}
}
impl From<BigInt> for num_bigint::BigInt {
fn from(big_int: BigInt) -> Self {
big_int.0
}
}

View File

@ -1,4 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
pub mod bigint;
pub mod buffer;
pub mod bytestring;
pub mod detached_buffer;

View File

@ -14,6 +14,7 @@ use crate::magic::transl8::opaque_recv;
use crate::magic::transl8::MagicType;
use crate::magic::transl8::ToV8;
use crate::magic::transl8::MAGIC_FIELD;
use crate::BigInt;
use crate::ByteString;
use crate::DetachedBuffer;
use crate::ExternalPointer;
@ -277,6 +278,7 @@ pub enum StructSerializers<'a, 'b, 'c> {
MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>),
MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>),
MagicStringOrBuffer(MagicalSerializer<'a, 'b, 'c, StringOrBuffer>),
MagicBigInt(MagicalSerializer<'a, 'b, 'c, BigInt>),
Regular(ObjectSerializer<'a, 'b, 'c>),
}
@ -299,6 +301,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
StructSerializers::MagicStringOrBuffer(s) => {
s.serialize_field(key, value)
}
StructSerializers::MagicBigInt(s) => s.serialize_field(key, value),
StructSerializers::Regular(s) => s.serialize_field(key, value),
}
}
@ -312,6 +315,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
StructSerializers::MagicByteString(s) => s.end(),
StructSerializers::MagicU16String(s) => s.end(),
StructSerializers::MagicStringOrBuffer(s) => s.end(),
StructSerializers::MagicBigInt(s) => s.end(),
StructSerializers::Regular(s) => s.end(),
}
}
@ -592,6 +596,10 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
let m = MagicalSerializer::<StringOrBuffer>::new(self.scope);
Ok(StructSerializers::MagicStringOrBuffer(m))
}
BigInt::MAGIC_NAME => {
let m = MagicalSerializer::<BigInt>::new(self.scope);
Ok(StructSerializers::MagicBigInt(m))
}
magic::Value::MAGIC_NAME => {
let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope);
Ok(StructSerializers::Magic(m))

View File

@ -2,6 +2,7 @@
use std::any::TypeId;
use std::mem::transmute_copy;
use crate::BigInt;
use crate::ByteString;
use crate::U16String;
use crate::ZeroCopyBuf;
@ -65,6 +66,7 @@ pub enum Primitive {
ZeroCopyBuf(ZeroCopyBuf),
ByteString(ByteString),
U16String(U16String),
BigInt(BigInt),
}
impl serde::Serialize for Primitive {
@ -89,6 +91,7 @@ impl serde::Serialize for Primitive {
Self::ZeroCopyBuf(x) => x.serialize(s),
Self::ByteString(x) => x.serialize(s),
Self::U16String(x) => x.serialize(s),
Self::BigInt(x) => x.serialize(s),
}
}
}
@ -137,6 +140,8 @@ impl<T: serde::Serialize + 'static> From<T> for SerializablePkg {
Self::Primitive(Primitive::ByteString(tc(x)))
} else if tid == TypeId::of::<U16String>() {
Self::Primitive(Primitive::U16String(tc(x)))
} else if tid == TypeId::of::<BigInt>() {
Self::Primitive(Primitive::BigInt(tc(x)))
} else {
Self::Serializable(Box::new(x))
}

View File

@ -4,6 +4,7 @@ use serde::Deserializer;
use serde_v8::utils::js_exec;
use serde_v8::utils::v8_do;
use serde_v8::BigInt;
use serde_v8::ByteString;
use serde_v8::Error;
use serde_v8::U16String;
@ -566,3 +567,65 @@ detest!(
"BigInt(-1.7976931348623157e+308)",
f32::NEG_INFINITY
);
// BigInt to BigInt
detest!(
de_bigint_var_u8,
BigInt,
"255n",
num_bigint::BigInt::from(255u8).into()
);
detest!(
de_bigint_var_i8,
BigInt,
"-128n",
num_bigint::BigInt::from(-128i8).into()
);
detest!(
de_bigint_var_u16,
BigInt,
"65535n",
num_bigint::BigInt::from(65535u16).into()
);
detest!(
de_bigint_var_i16,
BigInt,
"-32768n",
num_bigint::BigInt::from(-32768i16).into()
);
detest!(
de_bigint_var_u32,
BigInt,
"4294967295n",
num_bigint::BigInt::from(4294967295u32).into()
);
detest!(
de_bigint_var_i32,
BigInt,
"-2147483648n",
num_bigint::BigInt::from(-2147483648i32).into()
);
detest!(
de_bigint_var_u64,
BigInt,
"18446744073709551615n",
num_bigint::BigInt::from(18446744073709551615u64).into()
);
detest!(
de_bigint_var_i64,
BigInt,
"-9223372036854775808n",
num_bigint::BigInt::from(-9223372036854775808i64).into()
);
detest!(
de_bigint_var_u128,
BigInt,
"340282366920938463463374607431768211455n",
num_bigint::BigInt::from(340282366920938463463374607431768211455u128).into()
);
detest!(
de_bigint_var_i128,
BigInt,
"-170141183460469231731687303715884105728n",
num_bigint::BigInt::from(-170141183460469231731687303715884105728i128).into()
);

View File

@ -3,6 +3,7 @@ use serde::Serialize;
use serde_json::json;
use serde_v8::utils::js_exec;
use serde_v8::utils::v8_do;
use serde_v8::BigInt;
#[derive(Debug, Serialize, PartialEq)]
struct MathOp {
@ -137,6 +138,61 @@ sertest!(
"objEqual(x, {a: 1, b: 3, operator: null})"
);
sertest!(
ser_bigint_u8,
BigInt::from(num_bigint::BigInt::from(255_u8)),
"x === 255n"
);
sertest!(
ser_bigint_i8,
BigInt::from(num_bigint::BigInt::from(-128_i8)),
"x === -128n"
);
sertest!(
ser_bigint_u16,
BigInt::from(num_bigint::BigInt::from(65535_u16)),
"x === 65535n"
);
sertest!(
ser_bigint_i16,
BigInt::from(num_bigint::BigInt::from(-32768_i16)),
"x === -32768n"
);
sertest!(
ser_bigint_u32,
BigInt::from(num_bigint::BigInt::from(4294967295_u32)),
"x === 4294967295n"
);
sertest!(
ser_bigint_i32,
BigInt::from(num_bigint::BigInt::from(-2147483648_i32)),
"x === -2147483648n"
);
sertest!(
ser_bigint_u64,
BigInt::from(num_bigint::BigInt::from(9007199254740991_u64)),
"x === 9007199254740991n"
);
sertest!(
ser_bigint_i64,
BigInt::from(num_bigint::BigInt::from(-9007199254740991_i64)),
"x === -9007199254740991n"
);
sertest!(
ser_bigint_u128,
BigInt::from(num_bigint::BigInt::from(
340282366920938463463374607431768211455_u128
)),
"x === 340282366920938463463374607431768211455n"
);
sertest!(
ser_bigint_i128,
BigInt::from(num_bigint::BigInt::from(
-170141183460469231731687303715884105728_i128
)),
"x === -170141183460469231731687303715884105728n"
);
sertest!(
ser_map,
{