(&self, vm: &VirtualMachine)
| 883 | } |
| 884 | |
| 885 | pub fn locals(&self, vm: &VirtualMachine) -> PyResult<ArgMapping> { |
| 886 | use rustpython_compiler_core::bytecode::{ |
| 887 | CO_FAST_CELL, CO_FAST_FREE, CO_FAST_HIDDEN, CO_FAST_LOCAL, |
| 888 | }; |
| 889 | // SAFETY: Either the frame is not executing (caller checked owner), |
| 890 | // or we're in a trace callback on the same thread that's executing. |
| 891 | let locals = &self.locals; |
| 892 | let code = &**self.code; |
| 893 | let locals_map = locals.mapping(vm); |
| 894 | let fastlocals = unsafe { (*self.iframe.get()).localsplus.fastlocals() }; |
| 895 | |
| 896 | // Iterate through all localsplus slots using localspluskinds |
| 897 | let nlocalsplus = code.localspluskinds.len(); |
| 898 | let nfrees = code.freevars.len(); |
| 899 | let free_start = nlocalsplus - nfrees; |
| 900 | let is_optimized = code.flags.contains(bytecode::CodeFlags::OPTIMIZED); |
| 901 | |
| 902 | // Track which non-merged cellvar index we're at |
| 903 | let mut nonmerged_cell_idx = 0; |
| 904 | |
| 905 | for (i, &kind) in code.localspluskinds.iter().enumerate() { |
| 906 | if kind & CO_FAST_HIDDEN != 0 { |
| 907 | // Hidden variables are only skipped when their slot is empty. |
| 908 | // After a comprehension restores values, they should appear in locals(). |
| 909 | let slot_empty = match fastlocals[i].as_ref() { |
| 910 | None => true, |
| 911 | Some(obj) => { |
| 912 | if kind & (CO_FAST_CELL | CO_FAST_FREE) != 0 { |
| 913 | // If it's a PyCell, check if the cell is empty. |
| 914 | // If it's a raw value (merged cell during inlined comp), not empty. |
| 915 | obj.downcast_ref::<PyCell>() |
| 916 | .is_some_and(|cell| cell.get().is_none()) |
| 917 | } else { |
| 918 | false |
| 919 | } |
| 920 | } |
| 921 | }; |
| 922 | if slot_empty { |
| 923 | continue; |
| 924 | } |
| 925 | } |
| 926 | |
| 927 | // Free variables only included for optimized (function-like) scopes. |
| 928 | // Class/module scopes should not expose free vars in locals(). |
| 929 | if kind == CO_FAST_FREE && !is_optimized { |
| 930 | continue; |
| 931 | } |
| 932 | |
| 933 | // Get the name for this slot |
| 934 | let name = if kind & CO_FAST_LOCAL != 0 { |
| 935 | code.varnames[i] |
| 936 | } else if kind & CO_FAST_FREE != 0 { |
| 937 | code.freevars[i - free_start] |
| 938 | } else if kind & CO_FAST_CELL != 0 { |
| 939 | // Non-merged cell: find the name by skipping merged cellvars |
| 940 | let mut found_name = None; |
| 941 | let mut skip = nonmerged_cell_idx; |
| 942 | for cv in code.cellvars.iter() { |
no test coverage detected