| 951 | |
| 952 | #[pymethod] |
| 953 | pub fn co_positions(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> { |
| 954 | // Return an iterator over (line, end_line, column, end_column) tuples for each instruction |
| 955 | let linetable = self.code.linetable.as_ref(); |
| 956 | let mut positions = Vec::new(); |
| 957 | |
| 958 | if !linetable.is_empty() { |
| 959 | let mut reader = LineTableReader::new(linetable); |
| 960 | let mut line = self.code.first_line_number.map_or(0, |n| n.get() as i32); |
| 961 | |
| 962 | while !reader.at_end() { |
| 963 | let first_byte = match reader.read_byte() { |
| 964 | Some(b) => b, |
| 965 | None => break, |
| 966 | }; |
| 967 | |
| 968 | if (first_byte & 0x80) == 0 { |
| 969 | break; // Invalid linetable |
| 970 | } |
| 971 | |
| 972 | let code = (first_byte >> 3) & 0x0f; |
| 973 | let length = ((first_byte & 0x07) + 1) as i32; |
| 974 | |
| 975 | let kind = match PyCodeLocationInfoKind::from_code(code) { |
| 976 | Some(k) => k, |
| 977 | None => break, // Invalid code |
| 978 | }; |
| 979 | |
| 980 | let (line_delta, end_line_delta, column, end_column): ( |
| 981 | i32, |
| 982 | i32, |
| 983 | Option<i32>, |
| 984 | Option<i32>, |
| 985 | ) = match kind { |
| 986 | PyCodeLocationInfoKind::None => { |
| 987 | // No location - all values are None |
| 988 | (0, 0, None, None) |
| 989 | } |
| 990 | PyCodeLocationInfoKind::Long => { |
| 991 | // Long form |
| 992 | let delta = reader.read_signed_varint(); |
| 993 | let end_line_delta = reader.read_varint() as i32; |
| 994 | |
| 995 | let col = reader.read_varint(); |
| 996 | let column = if col == 0 { |
| 997 | None |
| 998 | } else { |
| 999 | Some((col - 1) as i32) |
| 1000 | }; |
| 1001 | |
| 1002 | let end_col = reader.read_varint(); |
| 1003 | let end_column = if end_col == 0 { |
| 1004 | None |
| 1005 | } else { |
| 1006 | Some((end_col - 1) as i32) |
| 1007 | }; |
| 1008 | |
| 1009 | // endline = line + end_line_delta (will be computed after line update) |
| 1010 | (delta, end_line_delta, column, end_column) |