use alloc::string::{String, ToString as _};
use alloc::vec::Vec;
use rquickjs::{
Exception, Filter, Function, Null, Object, String as JSString, Value,
atom::PredefinedAtom,
function::This,
object::ObjectIter,
qjs::{
JS_GetClassID, JS_GetProperty, JS_GetPropertyUint32, JS_TAG_EXCEPTION,
JS_VALUE_GET_NORM_TAG,
},
};
use serde::{
de::{self, IntoDeserializer, Unexpected},
forward_to_deserialize_any,
};
use crate::err::{Error, Result};
use crate::utils::{as_key, to_string_lossy};
use crate::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
enum ClassId {
Number = 4,
String = 5,
Bool = 6,
BigInt = 35,
}
pub struct Deserializer<'js> {
value: Value<'js>,
strict: bool,
map_key: bool,
current_kv: Option<(Value<'js>, Value<'js>)>,
stack: Vec<Value<'js>>,
}
impl<'js> Deserializer<'js> {
pub fn new(value: Value<'js>) -> Self {
Self::from(value)
}
pub fn with_strict(self) -> Self {
Self {
strict: true,
..self
}
}
}
impl<'de> From<Value<'de>> for Deserializer<'de> {
fn from(value: Value<'de>) -> Self {
Self {
value,
strict: false,
map_key: false,
current_kv: None,
stack: Vec::with_capacity(100),
}
}
}
impl<'js> Deserializer<'js> {
fn deserialize_number<'de, V>(&mut self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if let Some(i) = self.value.as_int() {
return visitor.visit_i32(i);
}
if let Some(f64_representation) = self.value.as_float() {
let is_positive = f64_representation.is_sign_positive();
let safe_integer_range = (MIN_SAFE_INTEGER as f64)..=(MAX_SAFE_INTEGER as f64);
let whole = (f64_representation % 1.0) == 0.0;
if whole && is_positive && f64_representation <= u32::MAX as f64 {
return visitor.visit_u32(f64_representation as u32);
}
if whole && safe_integer_range.contains(&f64_representation) {
let x = f64_representation as i64;
return visitor.visit_i64(x);
}
return visitor.visit_f64(f64_representation);
}
Err(Error::new(Exception::throw_type(
self.value.ctx(),
"Unsupported number type",
)))
}
fn pop_visited(&mut self) -> Result<Value<'js>> {
let v = self
.stack
.pop()
.ok_or_else(|| Error::new("No entries found in the deserializer stack"))?;
Ok(v)
}
fn check_cycles(&self) -> Result<()> {
for val in self.stack.iter().rev() {
if self.value.eq(val) {
return Err(Error::new(Exception::throw_type(
val.ctx(),
"circular dependency",
)));
}
}
Ok(())
}
}
impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if self.value.is_number() {
return self.deserialize_number(visitor);
}
if get_class_id(&self.value) == ClassId::Number as u32 {
let value_of = get_valueof(&self.value);
if let Some(f) = value_of {
let v = f.call((This(self.value.clone()),)).map_err(Error::new)?;
self.value = v;
return self.deserialize_number(visitor);
}
}
if let Some(b) = self.value.as_bool() {
return visitor.visit_bool(b);
}
if get_class_id(&self.value) == ClassId::Bool as u32 {
let value_of = get_valueof(&self.value);
if let Some(f) = value_of {
let v = f.call((This(self.value.clone()),)).map_err(Error::new)?;
return visitor.visit_bool(v);
}
}
if self.value.is_null() || self.value.is_undefined() {
return visitor.visit_unit();
}
if get_class_id(&self.value) == ClassId::String as u32 {
let value_of = get_to_string(&self.value);
if let Some(f) = value_of {
let v = f.call(((This(self.value.clone())),)).map_err(Error::new)?;
self.value = v;
}
}
if self.value.is_string() {
if self.map_key {
self.map_key = false;
let key = as_key(&self.value)?;
return visitor.visit_str(&key);
} else {
let val = self
.value
.as_string()
.map(|s| {
s.to_string()
.unwrap_or_else(|e| to_string_lossy(self.value.ctx(), s, e))
})
.unwrap();
return visitor.visit_str(&val);
}
}
if is_array_or_proxy_of_array(&self.value)
&& let Some(seq) = self.value.as_object()
{
let seq_access = SeqAccess::new(self, seq.clone())?;
return visitor.visit_seq(seq_access);
}
if self.value.is_object() {
ensure_supported(&self.value)?;
if let Some(f) = get_to_json(&self.value) {
let v: Value = f.call((This(self.value.clone()),)).map_err(Error::new)?;
if v.is_undefined() {
self.value = Value::new_undefined(v.ctx().clone());
} else {
self.value = v;
}
return self.deserialize_any(visitor);
}
let map_access = MapAccess::new(self, self.value.clone().into_object().unwrap())?;
let result = visitor.visit_map(map_access);
return result;
}
if get_class_id(&self.value) == ClassId::BigInt as u32 || self.value.is_big_int() {
if let Some(f) = get_to_json(&self.value) {
let v: Value = f.call((This(self.value.clone()),)).map_err(Error::new)?;
self.value = v;
return self.deserialize_any(visitor);
}
if let Some(f) = get_to_string(&self.value)
&& !self.strict
{
let v: Value = f.call((This(self.value.clone()),)).map_err(Error::new)?;
self.value = v;
return self.deserialize_any(visitor);
}
}
Err(Error::new(Exception::throw_type(
self.value.ctx(),
"Unsupported type",
)))
}
fn is_human_readable(&self) -> bool {
false
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if self.value.is_null() || self.value.is_undefined() {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if get_class_id(&self.value) == ClassId::String as u32
&& let Some(f) = get_to_string(&self.value)
{
let v = f.call((This(self.value.clone()),)).map_err(Error::new)?;
self.value = v;
}
let s = if let Some(s) = self.value.as_string() {
s.to_string()
.unwrap_or_else(|e| to_string_lossy(self.value.ctx(), s, e))
} else {
return Err(Error::new("expected a string for enum unit variant"));
};
visitor.visit_enum(UnitEnumAccess { variant: s })
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string
bytes byte_buf unit unit_struct seq tuple
tuple_struct map struct identifier ignored_any
}
}
struct MapAccess<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
properties: ObjectIter<'de, Value<'de>, Value<'de>>,
obj: Object<'de>,
}
impl<'a, 'de> MapAccess<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>, obj: Object<'de>) -> Result<Self> {
let filter = Filter::new().enum_only().string();
let properties: ObjectIter<'_, _, Value<'_>> =
obj.own_props::<Value<'_>, Value<'_>>(filter);
let val = obj.clone().into_value();
de.stack.push(val.clone());
Ok(Self {
de,
properties,
obj,
})
}
fn pop(&mut self) -> Result<()> {
let v = self.de.pop_visited()?;
if v != self.obj.clone().into_value() {
return Err(Error::new(
"Popped a mismatched value. Expected the top level sequence value",
));
}
Ok(())
}
}
impl<'de> de::MapAccess<'de> for MapAccess<'_, 'de> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: de::DeserializeSeed<'de>,
{
loop {
if let Some(kv) = self.properties.next() {
let (k, v) = kv.map_err(Error::new)?;
let to_json = get_to_json(&v);
let v = if let Some(f) = to_json {
f.call((This(v.clone()), k.clone())).map_err(Error::new)?
} else {
v
};
if !ensure_supported(&v)? || k.is_symbol() {
continue;
}
let class_id = get_class_id(&v);
if class_id == ClassId::Bool as u32 || class_id == ClassId::Number as u32 {
let value_of = get_valueof(&v);
if let Some(f) = value_of {
let v = f.call((This(v.clone()),)).map_err(Error::new)?;
self.de.current_kv = Some((k.clone(), v));
}
} else if class_id == ClassId::String as u32 {
let to_string = get_to_string(&v);
if let Some(f) = to_string {
let v = f.call((This(v.clone()),)).map_err(Error::new)?;
self.de.current_kv = Some((k.clone(), v));
}
} else {
self.de.current_kv = Some((k.clone(), v));
}
self.de.value = k;
self.de.map_key = true;
return seed.deserialize(&mut *self.de).map(Some);
} else {
self.pop()?;
return Ok(None);
}
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: de::DeserializeSeed<'de>,
{
self.de.value = self.de.current_kv.clone().unwrap().1;
self.de.check_cycles()?;
seed.deserialize(&mut *self.de)
}
}
struct SeqAccess<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
seq: Object<'de>,
length: usize,
index: usize,
}
impl<'a, 'de: 'a> SeqAccess<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>, seq: Object<'de>) -> Result<Self> {
de.stack.push(seq.clone().into_value());
let value: Value = seq.get(PredefinedAtom::Length).map_err(Error::new)?;
let length: usize = if let Some(n) = value.as_number() {
n as usize
} else {
let value_of: Function = value
.as_object()
.expect("length to be an object")
.get(PredefinedAtom::ValueOf)
.map_err(Error::new)?;
value_of.call(()).map_err(Error::new)?
};
Ok(Self {
de,
seq,
length,
index: 0,
})
}
fn pop(&mut self) -> Result<()> {
let v = self.de.pop_visited()?;
if v != self.seq.clone().into_value() {
return Err(Error::new(
"Popped a mismatched value. Expected the top level sequence value",
));
}
Ok(())
}
}
impl<'de> de::SeqAccess<'de> for SeqAccess<'_, 'de> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
if self.index < self.length {
let el = get_index(&self.seq, self.index).map_err(Error::new)?;
let to_json = get_to_json(&el);
if let Some(f) = to_json {
let index_value = JSString::from_str(el.ctx().clone(), &self.index.to_string());
self.de.value = f
.call((This(el.clone()), index_value))
.map_err(Error::new)?;
} else if ensure_supported(&el)? {
self.de.value = el
} else {
self.de.value = Null.into_value(self.seq.ctx().clone())
}
self.index += 1;
self.de.check_cycles()?;
seed.deserialize(&mut *self.de).map(Some)
} else {
self.pop()?;
Ok(None)
}
}
}
pub(crate) fn get_to_json<'a>(value: &Value<'a>) -> Option<Function<'a>> {
get_function(value, PredefinedAtom::ToJSON)
}
fn get_valueof<'a>(value: &Value<'a>) -> Option<Function<'a>> {
get_function(value, PredefinedAtom::ValueOf)
}
fn get_to_string<'a>(value: &Value<'a>) -> Option<Function<'a>> {
get_function(value, PredefinedAtom::ToString)
}
fn get_function<'a>(value: &Value<'a>, atom: PredefinedAtom) -> Option<Function<'a>> {
let f = unsafe { JS_GetProperty(value.ctx().as_raw().as_ptr(), value.as_raw(), atom as u32) };
let f = unsafe { Value::from_raw(value.ctx().clone(), f) };
if f.is_function()
&& let Some(f) = f.into_function()
{
Some(f)
} else {
None
}
}
fn get_class_id(v: &Value) -> u32 {
unsafe { JS_GetClassID(v.as_raw()) }
}
fn ensure_supported(value: &Value<'_>) -> Result<bool> {
let class_id = get_class_id(value);
if class_id == (ClassId::Bool as u32) || class_id == (ClassId::Number as u32) {
return Ok(true);
}
if class_id == ClassId::BigInt as u32 {
return Err(Error::new(Exception::throw_type(
value.ctx(),
"BigInt not supported",
)));
}
Ok(!matches!(
value.type_of(),
rquickjs::Type::Undefined
| rquickjs::Type::Symbol
| rquickjs::Type::Function
| rquickjs::Type::Uninitialized
| rquickjs::Type::Constructor
))
}
fn is_array_or_proxy_of_array(val: &Value) -> bool {
if val.is_array() {
return true;
}
let mut val = val.clone();
loop {
let Some(proxy) = val.into_proxy() else {
return false;
};
let Ok(target) = proxy.target() else {
return false;
};
val = target.into_value();
if val.is_array() {
return true;
}
}
}
fn get_index<'a>(obj: &Object<'a>, idx: usize) -> rquickjs::Result<Value<'a>> {
unsafe {
let ctx = obj.ctx();
let val = JS_GetPropertyUint32(ctx.as_raw().as_ptr(), obj.as_raw(), idx as _);
if JS_VALUE_GET_NORM_TAG(val) == JS_TAG_EXCEPTION {
return Err(rquickjs::Error::Exception);
}
Ok(Value::from_raw(ctx.clone(), val))
}
}
struct UnitEnumAccess {
variant: String,
}
impl<'de> de::EnumAccess<'de> for UnitEnumAccess {
type Error = Error;
type Variant = UnitOnlyVariant;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: de::DeserializeSeed<'de>,
{
let v = seed.deserialize(self.variant.into_deserializer())?;
Ok((v, UnitOnlyVariant))
}
}
struct UnitOnlyVariant;
impl<'de> de::VariantAccess<'de> for UnitOnlyVariant {
type Error = Error;
fn unit_variant(self) -> Result<()> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
Err(de::Error::invalid_type(
Unexpected::NewtypeVariant,
&"unit variant",
))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
Err(de::Error::invalid_type(
Unexpected::TupleVariant,
&"unit variant",
))
}
fn struct_variant<V>(self, _fields: &'static [&'static str], _visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
Err(de::Error::invalid_type(
Unexpected::StructVariant,
&"unit variant",
))
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use rquickjs::Value;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use super::Deserializer as ValueDeserializer;
use crate::test::Runtime;
use crate::{MAX_SAFE_INTEGER, from_value, to_value};
fn deserialize_value<T>(v: Value<'_>) -> T
where
T: DeserializeOwned,
{
let ctx = v.ctx().clone();
let mut deserializer = ValueDeserializer::from(v);
match T::deserialize(&mut deserializer) {
Ok(val) => val,
Err(e) => panic!("{}", e.catch(&ctx)),
}
}
#[test]
fn test_null() {
let rt = Runtime::default();
rt.context().with(|cx| {
let val = Value::new_null(cx);
deserialize_value::<()>(val);
});
}
#[test]
fn test_undefined() {
let rt = Runtime::default();
rt.context().with(|cx| {
let val = Value::new_undefined(cx);
deserialize_value::<()>(val);
});
}
#[test]
fn test_nan() {
let rt = Runtime::default();
rt.context().with(|cx| {
let val = Value::new_float(cx, f64::NAN);
let actual = deserialize_value::<f64>(val);
assert!(actual.is_nan());
});
}
#[test]
fn test_infinity() {
let rt = Runtime::default();
rt.context().with(|cx| {
let val = Value::new_float(cx, f64::INFINITY);
let actual = deserialize_value::<f64>(val);
assert!(actual.is_infinite() && actual.is_sign_positive());
});
}
#[test]
fn test_negative_infinity() {
let rt = Runtime::default();
rt.context().with(|cx| {
let val = Value::new_float(cx, f64::NEG_INFINITY);
let actual = deserialize_value::<f64>(val);
assert!(actual.is_infinite() && actual.is_sign_negative());
})
}
#[test]
fn test_map_always_converts_keys_to_string() {
let rt = Runtime::default();
rt.context().with(|c| {
c.eval::<Value<'_>, _>("var a = {1337: 42};").unwrap();
let val = c.globals().get("a").unwrap();
let actual = deserialize_value::<BTreeMap<String, i32>>(val);
assert_eq!(42, *actual.get("1337").unwrap())
});
}
#[test]
fn test_u64_bounds() {
let rt = Runtime::default();
rt.context().with(|c| {
let max = u64::MAX;
let val = Value::new_number(c.clone(), max as f64);
let actual = deserialize_value::<f64>(val);
assert_eq!(max as f64, actual);
let min = u64::MIN;
let val = Value::new_number(c.clone(), min as f64);
let actual = deserialize_value::<f64>(val);
assert_eq!(min as f64, actual);
});
}
#[test]
fn test_i64_bounds() {
let rt = Runtime::default();
rt.context().with(|c| {
let max = i64::MAX;
let val = Value::new_number(c.clone(), max as _);
let actual = deserialize_value::<f64>(val);
assert_eq!(max as f64, actual);
let min = i64::MIN;
let val = Value::new_number(c.clone(), min as _);
let actual = deserialize_value::<f64>(val);
assert_eq!(min as f64, actual);
});
}
#[test]
fn test_float_to_integer_conversion() {
let rt = Runtime::default();
rt.context().with(|c| {
let expected = MAX_SAFE_INTEGER - 1;
let val = Value::new_float(c.clone(), expected as _);
let actual = deserialize_value::<i64>(val);
assert_eq!(expected, actual);
let expected = MAX_SAFE_INTEGER + 1;
let val = Value::new_float(c.clone(), expected as _);
let actual = deserialize_value::<f64>(val);
assert_eq!(expected as f64, actual);
});
}
#[test]
fn test_u32_upper_bound() {
let rt = Runtime::default();
rt.context().with(|c| {
let expected = u32::MAX;
let val = Value::new_number(c, expected as _);
let actual = deserialize_value::<u32>(val);
assert_eq!(expected, actual);
});
}
#[test]
fn test_u32_lower_bound() {
let rt = Runtime::default();
rt.context().with(|cx| {
let expected = i32::MAX as u32 + 1;
let val = Value::new_number(cx, expected as _);
let actual = deserialize_value::<u32>(val);
assert_eq!(expected, actual);
});
}
#[test]
fn test_array() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>("var a = [1, 2, 3];").unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<Vec<u8>>(v);
assert_eq!(vec![1, 2, 3], val);
});
}
#[test]
fn test_array_proxy() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>(
r#"
var arr = [1, 2, 3];
var a = new Proxy(arr, {});
"#,
)
.unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<Vec<u8>>(v);
assert_eq!(vec![1, 2, 3], val);
});
}
#[test]
fn test_non_json_object_values_are_dropped() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>(
r#"
var unitialized;
var a = {
a: undefined,
b: function() {},
c: Symbol(),
d: () => {},
e: unitialized,
};"#,
)
.unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<BTreeMap<String, ()>>(v);
assert_eq!(BTreeMap::new(), val);
});
}
#[test]
fn test_non_json_array_values_are_null() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>(
r#"
var unitialized;
var a = [
undefined,
function() {},
Symbol(),
() => {},
unitialized,
];"#,
)
.unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<Vec<Option<()>>>(v);
assert_eq!(vec![None; 5], val);
});
}
#[test]
fn test_enum() {
let rt = Runtime::default();
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
enum Test {
One,
Two,
Three,
}
rt.context().with(|cx| {
let left = Test::Two;
let value = to_value(cx, left).unwrap();
let right: Test = from_value(value).unwrap();
assert_eq!(left, right);
});
}
#[test]
fn test_short_bigint() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>("var a = BigInt(1);").unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<String>(v);
assert_eq!(val, "1");
});
}
#[test]
fn test_bigint() {
let rt = Runtime::default();
rt.context().with(|cx| {
cx.eval::<Value<'_>, _>(
r#"
const left = 12345678901234567890n;
const right = 98765432109876543210n;
var a = left * right;
"#,
)
.unwrap();
let v = cx.globals().get("a").unwrap();
let val = deserialize_value::<String>(v);
assert_eq!(val, "1219326311370217952237463801111263526900");
});
}
}