(args: FuncArgs, vm: &VirtualMachine)
| 185 | /// Return the list of objects that directly refer to any of the arguments. |
| 186 | #[pyfunction] |
| 187 | fn get_referrers(args: FuncArgs, vm: &VirtualMachine) -> PyListRef { |
| 188 | use std::collections::HashSet; |
| 189 | |
| 190 | // Build a set of target object pointers for fast lookup |
| 191 | let targets: HashSet<usize> = args |
| 192 | .args |
| 193 | .iter() |
| 194 | .map(|obj| obj.as_ref() as *const crate::PyObject as usize) |
| 195 | .collect(); |
| 196 | |
| 197 | // Collect pointers of frames currently on the execution stack. |
| 198 | // In CPython, executing frames (_PyInterpreterFrame) are not GC-tracked |
| 199 | // PyObjects, so they never appear in get_referrers results. Since |
| 200 | // RustPython materializes every frame as a PyObject, we must exclude |
| 201 | // them manually to match the expected behavior. |
| 202 | let stack_frames: HashSet<usize> = vm |
| 203 | .frames |
| 204 | .borrow() |
| 205 | .iter() |
| 206 | .map(|fp| { |
| 207 | let frame: &crate::PyObject = unsafe { fp.as_ref() }.as_ref(); |
| 208 | frame as *const crate::PyObject as usize |
| 209 | }) |
| 210 | .collect(); |
| 211 | |
| 212 | let mut result = Vec::new(); |
| 213 | |
| 214 | // Scan all tracked objects across all generations |
| 215 | let all_objects = gc_state::gc_state().get_objects(None); |
| 216 | for obj in all_objects { |
| 217 | let obj_ptr = obj.as_ref() as *const crate::PyObject as usize; |
| 218 | if stack_frames.contains(&obj_ptr) { |
| 219 | continue; |
| 220 | } |
| 221 | let referent_ptrs = unsafe { obj.gc_get_referent_ptrs() }; |
| 222 | for child_ptr in referent_ptrs { |
| 223 | if targets.contains(&(child_ptr.as_ptr() as usize)) { |
| 224 | result.push(obj.clone()); |
| 225 | break; |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | vm.ctx.new_list(result) |
| 231 | } |
| 232 | |
| 233 | /// Return True if the object is tracked by the garbage collector. |
| 234 | #[pyfunction] |
nothing calls this directly
no test coverage detected