@@ -107,6 +107,18 @@ impl Drop for DenseThisGuard {
107107/// Returns nothing (void)
108108#[ no_mangle]
109109pub extern "C" fn js_array_forEach ( arr : * const ArrayHeader , callback : * const ClosureHeader ) {
110+ // #7574: `normalize_array_receiver` materializes an array-like OBJECT
111+ // receiver — a `class X extends Array` instance among them — into a fresh
112+ // dense snapshot. The spec passes the RECEIVER as the callback's 3rd
113+ // argument, so without this the callback saw the snapshot and
114+ // `self === sub` was false (the same "forEach's 3rd argument" obligation
115+ // #7573 hit for Map/Set). Gated on a one-load `GC_TYPE_OBJECT` header test,
116+ // so a genuine array pays a compare and never enters the registry probes.
117+ let self_override = if crate :: array:: subclass:: raw_receiver_is_heap_object ( arr) {
118+ crate :: array:: subclass:: array_object_receiver ( arr)
119+ } else {
120+ None
121+ } ;
110122 let arr = normalize_array_receiver ( arr) ;
111123 if arr. is_null ( ) {
112124 return ;
@@ -142,6 +154,13 @@ pub extern "C" fn js_array_forEach(arr: *const ArrayHeader, callback: *const Clo
142154 let length = ( * arr) . length ;
143155 let scope = crate :: gc:: RuntimeHandleScope :: new ( ) ;
144156 let rooted = RootedIterArray :: new ( & scope, arr) ;
157+ // The override is a movable `ObjectHeader` held across user callbacks
158+ // that allocate — root it for the duration of the loop.
159+ let self_handle = self_override. map ( |recv| scope. root_nanbox_f64 ( recv) ) ;
160+ let self_value = |rooted : & RootedIterArray | match & self_handle {
161+ Some ( h) => h. get_nanbox_f64 ( ) ,
162+ None => rooted. receiver ( ) ,
163+ } ;
145164 let _tg = DenseThisGuard :: bind_undefined ( ) ;
146165 if crate :: array:: array_iteration_is_exotic ( arr) {
147166 for i in 0 ..length as usize {
@@ -150,7 +169,7 @@ pub extern "C" fn js_array_forEach(arr: *const ArrayHeader, callback: *const Clo
150169 continue ;
151170 }
152171 let element = crate :: array:: array_spec_get ( arr, i as u32 ) ;
153- js_closure_call3 ( callback, element, i as f64 , rooted . receiver ( ) ) ;
172+ js_closure_call3 ( callback, element, i as f64 , self_value ( & rooted ) ) ;
154173 }
155174 return ;
156175 }
@@ -162,7 +181,7 @@ pub extern "C" fn js_array_forEach(arr: *const ArrayHeader, callback: *const Clo
162181 // dispatch path supports call3 safely, so bound native
163182 // methods like `array.forEach(console.log)` can observe the
164183 // source array just like Node.
165- js_closure_call3 ( callback, element, i as f64 , rooted . receiver ( ) ) ;
184+ js_closure_call3 ( callback, element, i as f64 , self_value ( & rooted ) ) ;
166185 }
167186 }
168187}
0 commit comments