11use 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} ;
811use 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> {
387392struct 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> {
398405impl < ' 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.
549582struct UnitEnumAccess {
550583 variant : String ,
0 commit comments