@@ -10,7 +10,7 @@ pub(crate) mod abstract_operations;
1010mod data;
1111
1212use core:: ops:: { Index , IndexMut , RangeInclusive } ;
13- use std:: collections:: hash_map:: Entry ;
13+ use std:: { collections:: hash_map:: Entry , ops :: ControlFlow } ;
1414
1515use crate :: {
1616 ecmascript:: {
@@ -20,12 +20,13 @@ use crate::{
2020 } ,
2121 builtins:: {
2222 array:: abstract_operations:: { array_set_length, array_try_set_length} ,
23- ordinary:: ordinary_define_own_property,
23+ ordinary:: { caches :: Caches , ordinary_define_own_property} ,
2424 } ,
2525 execution:: { Agent , JsResult , ProtoIntrinsics } ,
2626 types:: {
27- BUILTIN_STRING_MEMORY , Function , InternalMethods , InternalSlots , IntoFunction ,
28- IntoObject , Object , OrdinaryObject , PropertyDescriptor , PropertyKey , Value ,
27+ BUILTIN_STRING_MEMORY , Function , GetCachedResult , InternalMethods , InternalSlots ,
28+ IntoFunction , IntoObject , IntoValue , NoCache , Object , OrdinaryObject ,
29+ PropertyDescriptor , PropertyKey , SetCachedResult , Value ,
2930 } ,
3031 } ,
3132 engine:: {
@@ -47,9 +48,13 @@ use crate::{
4748use ahash:: AHashMap ;
4849pub use data:: ArrayHeapData ;
4950
50- use super :: ordinary:: {
51- ordinary_delete, ordinary_get, ordinary_get_own_property, ordinary_has_property,
52- ordinary_try_get, ordinary_try_has_property,
51+ use super :: {
52+ array_set_length_handling,
53+ ordinary:: {
54+ caches:: PropertyLookupCache , ordinary_delete, ordinary_get, ordinary_get_own_property,
55+ ordinary_has_property, ordinary_try_get, ordinary_try_has_property,
56+ shape:: ShapeSetCachedProps ,
57+ } ,
5358} ;
5459
5560#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -407,8 +412,14 @@ impl<'a> InternalMethods<'a> for Array<'a> {
407412 } ) )
408413 } else if let Some ( backing_object) = array_data. object_index {
409414 TryResult :: Continue (
410- ordinary_get_own_property ( agent, self . into_object ( ) , backing_object, property_key)
411- . bind ( gc) ,
415+ ordinary_get_own_property (
416+ agent,
417+ self . into_object ( ) ,
418+ backing_object,
419+ property_key,
420+ gc,
421+ )
422+ . bind ( gc) ,
412423 )
413424 } else {
414425 TryResult :: Continue ( None )
@@ -478,13 +489,28 @@ impl<'a> InternalMethods<'a> for Array<'a> {
478489 // j. If index ≥ length, then
479490 // i. Set lengthDesc.[[Value]] to index + 1𝔽.
480491 // This should've already been handled by the push.
481- debug_assert_eq ! ( agent[ self ] . elements. len( ) , index + 1 ) ;
492+ debug_assert_eq ! ( array_heap_data. elements. len( ) , index + 1 ) ;
493+ if let Some ( shape) = array_heap_data. object_index . map ( |o| o. object_shape ( agent) )
494+ && shape. is_intrinsic ( agent)
495+ {
496+ // We set a value on an intrinsic object, we have to
497+ // invalidate caches.
498+ Caches :: invalidate_caches_on_intrinsic_shape_property_addition (
499+ agent,
500+ self . into_object ( ) ,
501+ shape,
502+ index. into ( ) ,
503+ u32:: MAX ,
504+ gc,
505+ ) ;
506+ }
482507 // iii. Assert: succeeded is true.
483508 TryResult :: Continue ( true )
484509 } else {
485510 // h. Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc).
486511 TryResult :: Continue ( ordinary_define_own_property_for_array (
487512 agent,
513+ self ,
488514 elements,
489515 index,
490516 property_descriptor,
@@ -847,6 +873,145 @@ impl<'a> InternalMethods<'a> for Array<'a> {
847873
848874 TryResult :: Continue ( keys)
849875 }
876+
877+ fn get_cached < ' gc > (
878+ self ,
879+ agent : & mut Agent ,
880+ p : PropertyKey ,
881+ cache : PropertyLookupCache ,
882+ gc : NoGcScope < ' gc , ' _ > ,
883+ ) -> ControlFlow < GetCachedResult < ' gc > , NoCache > {
884+ // Cached lookup of an Array should return directly from the Array's
885+ // internal memory if it can.
886+ if p == BUILTIN_STRING_MEMORY . length . to_property_key ( ) {
887+ // Length lookup: we find it always.
888+ return self . len ( agent) . into_value ( ) . into ( ) ;
889+ } else if let Some ( index) = p. into_u32 ( ) {
890+ // Indexed lookup: check our slice.
891+ if let Some ( value) = self . as_slice ( agent) . get ( index as usize ) {
892+ // Found a slot in the slice, check if it contains a Value.
893+ if let Some ( value) = value {
894+ // Slot contained value, return it.
895+ return value. bind ( gc) . into ( ) ;
896+ }
897+ // Slot did not contain a value; this is either a hole or an
898+ // accessor property.
899+ let ElementStorageRef { descriptors, .. } = self . get_storage ( agent) ;
900+ if let Some ( desc) = descriptors. and_then ( |d| d. get ( & index) ) {
901+ // This was an accessor property; if it has a getter,
902+ // return that. Otherwise, return undefined.
903+ debug_assert ! ( desc. is_accessor_descriptor( ) ) ;
904+ if let Some ( getter) = desc. getter_function ( gc) {
905+ return GetCachedResult :: Get ( getter. bind ( gc) ) . into ( ) ;
906+ } else {
907+ return Value :: Undefined . into ( ) ;
908+ }
909+ }
910+ // This was a hole, continue into the prototype chain.
911+ }
912+ }
913+ // If this was an over-indexing, a hole, or a named property on the
914+ // Array then we want to perform a normal cached lookup with the
915+ // Array's shape.
916+ let shape = self . object_shape ( agent) ;
917+ shape. get_cached (
918+ agent,
919+ p. bind ( gc) ,
920+ self . into_value ( ) . bind ( gc) ,
921+ cache. bind ( gc) ,
922+ gc,
923+ )
924+ }
925+
926+ fn set_cached < ' gc > (
927+ self ,
928+ agent : & mut Agent ,
929+ p : PropertyKey ,
930+ value : Value ,
931+ receiver : Value ,
932+ cache : PropertyLookupCache ,
933+ gc : NoGcScope < ' gc , ' _ > ,
934+ ) -> ControlFlow < SetCachedResult < ' gc > , NoCache > {
935+ // Cached set of an Array should return directly mutate the Array's
936+ // internal memory if it can.
937+ if p == BUILTIN_STRING_MEMORY . length . to_property_key ( ) {
938+ // Length lookup: we find it always.
939+ if !self . length_writable ( agent) {
940+ return SetCachedResult :: Unwritable . into ( ) ;
941+ }
942+ if let Value :: Integer ( value) = value
943+ && let Ok ( value) = u32:: try_from ( value. into_i64 ( ) )
944+ {
945+ let Ok ( result) = array_set_length_handling ( agent, self , value, None , None , None )
946+ else {
947+ // Let caller handle retry and error on TryReserveError.
948+ return NoCache . into ( ) ;
949+ } ;
950+ return if result {
951+ SetCachedResult :: Done . into ( )
952+ } else {
953+ SetCachedResult :: Unwritable . into ( )
954+ } ;
955+ } else {
956+ return NoCache . into ( ) ;
957+ }
958+ } else if let Some ( index) = p. into_u32 ( ) {
959+ // Indexed lookup: check our slice. First bounds-check.
960+ if !( 0 ..self . len ( agent) ) . contains ( & index) {
961+ // We're out of bounds; this need prototype lookups.
962+ return NoCache . into ( ) ;
963+ }
964+ // Index within slice; let's look into that memory.
965+ let storage = self . get_storage_mut ( agent) ;
966+ // First check if we have a descriptor at our index.
967+ let desc = match storage. descriptors {
968+ Entry :: Occupied ( e) => e. into_mut ( ) . get ( & index) ,
969+ Entry :: Vacant ( _) => None ,
970+ } ;
971+ if let Some ( desc) = desc {
972+ // Found a descriptor; see if it's an accessor.
973+ if desc. is_accessor_descriptor ( ) {
974+ // Found an accessor indeed; see if it has a setter,
975+ // and return that if so.
976+ if let Some ( setter) = desc. setter_function ( gc) {
977+ return SetCachedResult :: Set ( setter) . into ( ) ;
978+ }
979+ // No setter on this accessor; trying to set the value
980+ // fails.
981+ return SetCachedResult :: Accessor . into ( ) ;
982+ }
983+ // Data descriptor; see if it's not writable.
984+ if !desc. is_writable ( ) . unwrap ( ) {
985+ // Not writable; return failure.
986+ return SetCachedResult :: Unwritable . into ( ) ;
987+ }
988+ }
989+ // Writable data property or hole; check which one we're
990+ // dealing with.
991+ if let Some ( slot) = & mut storage. values [ index as usize ] {
992+ // Writable data property it is! Set its value.
993+ * slot = value. unbind ( ) ;
994+ return SetCachedResult :: Done . into ( ) ;
995+ }
996+ // Hole! We'll just return NoCache to signify that we can't be
997+ // arsed to implement the entire prototype lookup logic here.
998+ return NoCache . into ( ) ;
999+ }
1000+ // If this was a non-Array index or a named property on the Array then
1001+ // we want to perform a normal cached set with the Array's shape.
1002+ let shape = self . object_shape ( agent) ;
1003+ shape. set_cached (
1004+ agent,
1005+ ShapeSetCachedProps {
1006+ o : self . into_object ( ) ,
1007+ p,
1008+ receiver,
1009+ } ,
1010+ value,
1011+ cache,
1012+ gc,
1013+ )
1014+ }
8501015}
8511016
8521017impl Index < Array < ' _ > > for Agent {
@@ -930,8 +1095,30 @@ impl HeapSweepWeakReference for Array<'static> {
9301095 }
9311096}
9321097
1098+ /// Helper to invalidate property lookup caches associated with an index when
1099+ /// an intrinsic Array is mutated.
1100+ fn invalidate_array_index_caches ( agent : & mut Agent , array : Array , index : u32 , gc : NoGcScope ) {
1101+ if let Some ( shape) = array
1102+ . get_backing_object ( agent)
1103+ . map ( |o| o. object_shape ( agent) )
1104+ && shape. is_intrinsic ( agent)
1105+ {
1106+ // We set a value on an intrinsic object, we have to
1107+ // invalidate caches.
1108+ Caches :: invalidate_caches_on_intrinsic_shape_property_addition (
1109+ agent,
1110+ array. into_object ( ) ,
1111+ shape,
1112+ index. into ( ) ,
1113+ u32:: MAX ,
1114+ gc,
1115+ ) ;
1116+ }
1117+ }
1118+
9331119fn ordinary_define_own_property_for_array (
9341120 agent : & mut Agent ,
1121+ array : Array ,
9351122 elements : ElementsVector ,
9361123 index : u32 ,
9371124 descriptor : PropertyDescriptor ,
@@ -970,6 +1157,7 @@ fn ordinary_define_own_property_for_array(
9701157 // value otherwise.
9711158 let elem_descriptor = ElementDescriptor :: from_accessor_descriptor ( descriptor) ;
9721159 insert_element_descriptor ( agent, & elements, index, None , elem_descriptor) ;
1160+ invalidate_array_index_caches ( agent, array, index, gc) ;
9731161 }
9741162 // d. Else,
9751163 else {
@@ -984,6 +1172,7 @@ fn ordinary_define_own_property_for_array(
9841172 Some ( descriptor_value. unwrap_or ( Value :: Undefined ) ) ,
9851173 ElementDescriptor :: from_data_descriptor ( descriptor) ,
9861174 ) ;
1175+ invalidate_array_index_caches ( agent, array, index, gc) ;
9871176 }
9881177
9891178 // e. Return true.
@@ -1084,6 +1273,7 @@ fn ordinary_define_own_property_for_array(
10841273 configurable,
10851274 ) ;
10861275 insert_element_descriptor ( agent, & elements, index, None , elem_descriptor) ;
1276+ invalidate_array_index_caches ( agent, array, index, gc) ;
10871277 }
10881278 // b. Else if IsAccessorDescriptor(current) is true and IsDataDescriptor(Desc) is true, then
10891279 else if current_is_accessor_descriptor && descriptor. is_data_descriptor ( ) {
0 commit comments