Skip to content

Commit 476d9e8

Browse files
committed
Update rquickjs, fix BigInt support and support Proxy of Array
1 parent 8c017af commit 476d9e8

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

Cargo.lock

Lines changed: 46 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ readme = "README.md"
99
authors = ["Emile Fugulin <[email protected]>", "The Javy Project Developers"]
1010

1111
[dependencies]
12-
rquickjs = "0.9"
12+
rquickjs = "0.10"
1313
serde = "1"
1414

1515
[dev-dependencies]

src/de.rs

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use rquickjs::{
2-
Array, Exception, Filter, Function, Null, Object, String as JSString, Value,
2+
Exception, Filter, Function, Null, Object, String as JSString, Value,
33
atom::PredefinedAtom,
44
function::This,
55
object::ObjectIter,
6-
qjs::{JS_GetClassID, JS_GetProperty},
6+
qjs::{
7+
JS_GetClassID, JS_GetProperty, JS_GetPropertyUint32, JS_GetProxyTarget, JS_IsArray,
8+
JS_IsProxy, JS_TAG_EXCEPTION, JS_VALUE_GET_NORM_TAG,
9+
},
710
};
811
use serde::{
912
de::{self, IntoDeserializer, Unexpected},
@@ -21,7 +24,7 @@ enum ClassId {
2124
Number = 4,
2225
String = 5,
2326
Bool = 6,
24-
BigInt = 33,
27+
BigInt = 34,
2528
}
2629

2730
/// `Deserializer` is a deserializer for [Value] values, implementing the `serde::Deserializer` trait.
@@ -184,8 +187,10 @@ impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
184187
}
185188
}
186189

187-
if let Some(arr) = self.value.as_array() {
188-
let seq_access = SeqAccess::new(self, arr.clone())?;
190+
if is_array_or_proxy_of_array(&self.value)
191+
&& let Some(seq) = self.value.as_object()
192+
{
193+
let seq_access = SeqAccess::new(self, seq.clone())?;
189194
return visitor.visit_seq(seq_access);
190195
}
191196

@@ -387,8 +392,10 @@ impl<'de> de::MapAccess<'de> for MapAccess<'_, 'de> {
387392
struct SeqAccess<'a, 'de: 'a> {
388393
/// The deserializer.
389394
de: &'a mut Deserializer<'de>,
390-
/// The sequence, represented as a JavaScript array.
391-
seq: Array<'de>,
395+
/// The sequence, represented as a JavaScript object.
396+
// Using Object instead of Array because `SeqAccess` needs to support
397+
// proxies of arrays and a proxy Value cannot be converted into Array.
398+
seq: Object<'de>,
392399
/// The sequence length.
393400
length: usize,
394401
/// The current index.
@@ -398,17 +405,14 @@ struct SeqAccess<'a, 'de: 'a> {
398405
impl<'a, 'de: 'a> SeqAccess<'a, 'de> {
399406
/// Creates a new `SeqAccess` ensuring that the top-level value is added
400407
/// to the `Deserializer` visitor stack.
401-
fn new(de: &'a mut Deserializer<'de>, seq: Array<'de>) -> Result<Self> {
408+
fn new(de: &'a mut Deserializer<'de>, seq: Object<'de>) -> Result<Self> {
402409
de.stack.push(seq.clone().into_value());
403410

404411
// Retrieve the `length` property from the object itself rather than
405412
// using the bindings `Array::len` given that according to the spec
406413
// it's fine to return any value, not just a number from the
407414
// `length` property.
408-
let value: Value = seq
409-
.as_object()
410-
.get(PredefinedAtom::Length)
411-
.map_err(Error::new)?;
415+
let value: Value = seq.get(PredefinedAtom::Length).map_err(Error::new)?;
412416
let length: usize = if let Some(n) = value.as_number() {
413417
n as usize
414418
} else {
@@ -450,7 +454,7 @@ impl<'de> de::SeqAccess<'de> for SeqAccess<'_, 'de> {
450454
T: de::DeserializeSeed<'de>,
451455
{
452456
if self.index < self.length {
453-
let el = self.seq.get(self.index).map_err(Error::new)?;
457+
let el = get_index(&self.seq, self.index).map_err(Error::new)?;
454458
let to_json = get_to_json(&el);
455459

456460
if let Some(f) = to_json {
@@ -545,6 +549,35 @@ fn ensure_supported(value: &Value<'_>) -> Result<bool> {
545549
))
546550
}
547551

552+
fn is_array_or_proxy_of_array(val: &Value) -> bool {
553+
if val.is_array() {
554+
return true;
555+
}
556+
let ctx = val.ctx().as_raw().as_ptr();
557+
let mut val = val.as_raw();
558+
loop {
559+
let is_proxy = unsafe { JS_IsProxy(val) };
560+
if !is_proxy {
561+
return false;
562+
}
563+
val = unsafe { JS_GetProxyTarget(ctx, val) };
564+
if unsafe { JS_IsArray(val) } {
565+
return true;
566+
}
567+
}
568+
}
569+
570+
fn get_index<'a>(obj: &Object<'a>, idx: usize) -> rquickjs::Result<Value<'a>> {
571+
unsafe {
572+
let ctx = obj.ctx();
573+
let val = JS_GetPropertyUint32(ctx.as_raw().as_ptr(), obj.as_raw(), idx as _);
574+
if JS_VALUE_GET_NORM_TAG(val) == JS_TAG_EXCEPTION {
575+
return Err(rquickjs::Error::Exception);
576+
}
577+
Ok(Value::from_raw(ctx.clone(), val))
578+
}
579+
}
580+
548581
/// A helper struct for deserializing enums containing unit variants.
549582
struct UnitEnumAccess {
550583
variant: String,

0 commit comments

Comments
 (0)