1use crate::{
5 marshal::MarshalError,
6 varint::{read_varint, read_varint_with_start, write_varint_be, write_varint_with_start},
7 {OneIndexed, SourceLocation},
8};
9use alloc::{borrow::ToOwned, boxed::Box, collections::BTreeSet, fmt, string::String, vec::Vec};
10use bitflags::bitflags;
11use core::{
12 cell::UnsafeCell,
13 hash, mem,
14 ops::{Deref, Index, IndexMut},
15 sync::atomic::{AtomicU8, AtomicU16, AtomicUsize, Ordering},
16};
17use itertools::Itertools;
18use malachite_bigint::BigInt;
19use num_complex::Complex64;
20use rustpython_wtf8::{Wtf8, Wtf8Buf};
21
22pub use crate::bytecode::{
23 instruction::{
24 AnyInstruction, Arg, Instruction, InstructionMetadata, PseudoInstruction, StackEffect,
25 },
26 oparg::{
27 BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, ConvertValueOparg,
28 IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadAttr, LoadSuperAttr,
29 MakeFunctionFlag, MakeFunctionFlags, NameIdx, OpArg, OpArgByte, OpArgState, OpArgType,
30 RaiseKind, SpecialMethod, UnpackExArgs,
31 },
32};
33
34mod instruction;
35pub mod oparg;
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct ExceptionTableEntry {
41 pub start: u32,
43 pub end: u32,
45 pub target: u32,
47 pub depth: u16,
49 pub push_lasti: bool,
51}
52
53impl ExceptionTableEntry {
54 pub const fn new(start: u32, end: u32, target: u32, depth: u16, push_lasti: bool) -> Self {
55 Self {
56 start,
57 end,
58 target,
59 depth,
60 push_lasti,
61 }
62 }
63}
64
65pub fn encode_exception_table(entries: &[ExceptionTableEntry]) -> alloc::boxed::Box<[u8]> {
68 let mut data = Vec::new();
69 for entry in entries {
70 let size = entry.end.saturating_sub(entry.start);
71 let depth_lasti = ((entry.depth as u32) << 1) | (entry.push_lasti as u32);
72
73 write_varint_with_start(&mut data, entry.start);
74 write_varint_be(&mut data, size);
75 write_varint_be(&mut data, entry.target);
76 write_varint_be(&mut data, depth_lasti);
77 }
78 data.into_boxed_slice()
79}
80
81pub fn find_exception_handler(table: &[u8], offset: u32) -> Option<ExceptionTableEntry> {
83 let mut pos = 0;
84 while pos < table.len() {
85 let start = read_varint_with_start(table, &mut pos)?;
86 let size = read_varint(table, &mut pos)?;
87 let target = read_varint(table, &mut pos)?;
88 let depth_lasti = read_varint(table, &mut pos)?;
89
90 let end = start + size;
91 let depth = (depth_lasti >> 1) as u16;
92 let push_lasti = (depth_lasti & 1) != 0;
93
94 if offset >= start && offset < end {
95 return Some(ExceptionTableEntry {
96 start,
97 end,
98 target,
99 depth,
100 push_lasti,
101 });
102 }
103 }
104 None
105}
106
107pub fn decode_exception_table(table: &[u8]) -> Vec<ExceptionTableEntry> {
109 let mut entries = Vec::new();
110 let mut pos = 0;
111 while pos < table.len() {
112 let Some(start) = read_varint_with_start(table, &mut pos) else {
113 break;
114 };
115 let Some(size) = read_varint(table, &mut pos) else {
116 break;
117 };
118 let Some(target) = read_varint(table, &mut pos) else {
119 break;
120 };
121 let Some(depth_lasti) = read_varint(table, &mut pos) else {
122 break;
123 };
124 let Some(end) = start.checked_add(size) else {
125 break;
126 };
127 entries.push(ExceptionTableEntry {
128 start,
129 end,
130 target,
131 depth: (depth_lasti >> 1) as u16,
132 push_lasti: (depth_lasti & 1) != 0,
133 });
134 }
135 entries
136}
137
138pub fn build_no_location_mask(linetable: &[u8], num_units: usize) -> Vec<bool> {
141 let mut mask = Vec::new();
142 mask.resize(num_units, false);
143 let mut pos = 0;
144 let mut unit_idx = 0;
145
146 while pos < linetable.len() && unit_idx < num_units {
147 let header = linetable[pos];
148 pos += 1;
149 let code = (header >> 3) & 0xf;
150 let length = ((header & 7) + 1) as usize;
151
152 let is_no_location = code == PyCodeLocationInfoKind::None as u8;
153
154 match code {
156 0..=9 => pos += 1, 10..=12 => pos += 2, 13 => {
159 while pos < linetable.len() {
161 let b = linetable[pos];
162 pos += 1;
163 if b & 0x40 == 0 {
164 break;
165 }
166 }
167 }
168 14 => {
169 while pos < linetable.len() {
172 let b = linetable[pos];
173 pos += 1;
174 if b & 0x40 == 0 {
175 break;
176 }
177 }
178 for _ in 0..3 {
180 while pos < linetable.len() {
181 let b = linetable[pos];
182 pos += 1;
183 if b & 0x40 == 0 {
184 break;
185 }
186 }
187 }
188 }
189 15 => {} _ => {}
191 }
192
193 for _ in 0..length {
194 if unit_idx < num_units {
195 mask[unit_idx] = is_no_location;
196 unit_idx += 1;
197 }
198 }
199 }
200
201 mask
202}
203
204#[derive(Copy, Clone, Debug, PartialEq, Eq)]
206#[repr(u8)]
207pub enum PyCodeLocationInfoKind {
208 Short0 = 0,
210 Short1 = 1,
211 Short2 = 2,
212 Short3 = 3,
213 Short4 = 4,
214 Short5 = 5,
215 Short6 = 6,
216 Short7 = 7,
217 Short8 = 8,
218 Short9 = 9,
219 OneLine0 = 10,
221 OneLine1 = 11,
222 OneLine2 = 12,
223 NoColumns = 13,
224 Long = 14,
225 None = 15,
226}
227
228impl PyCodeLocationInfoKind {
229 pub fn from_code(code: u8) -> Option<Self> {
230 match code {
231 0 => Some(Self::Short0),
232 1 => Some(Self::Short1),
233 2 => Some(Self::Short2),
234 3 => Some(Self::Short3),
235 4 => Some(Self::Short4),
236 5 => Some(Self::Short5),
237 6 => Some(Self::Short6),
238 7 => Some(Self::Short7),
239 8 => Some(Self::Short8),
240 9 => Some(Self::Short9),
241 10 => Some(Self::OneLine0),
242 11 => Some(Self::OneLine1),
243 12 => Some(Self::OneLine2),
244 13 => Some(Self::NoColumns),
245 14 => Some(Self::Long),
246 15 => Some(Self::None),
247 _ => Option::None,
248 }
249 }
250
251 pub fn is_short(&self) -> bool {
252 (*self as u8) <= 9
253 }
254
255 pub fn short_column_group(&self) -> Option<u8> {
256 if self.is_short() {
257 Some(*self as u8)
258 } else {
259 Option::None
260 }
261 }
262
263 pub fn one_line_delta(&self) -> Option<i32> {
264 match self {
265 Self::OneLine0 => Some(0),
266 Self::OneLine1 => Some(1),
267 Self::OneLine2 => Some(2),
268 _ => Option::None,
269 }
270 }
271}
272
273pub trait Constant: Sized + Clone {
274 type Name: AsRef<str>;
275
276 fn borrow_constant(&self) -> BorrowedConstant<'_, Self>;
278}
279
280impl Constant for ConstantData {
281 type Name = String;
282
283 fn borrow_constant(&self) -> BorrowedConstant<'_, Self> {
284 use BorrowedConstant::*;
285
286 match self {
287 Self::Integer { value } => Integer { value },
288 Self::Float { value } => Float { value: *value },
289 Self::Complex { value } => Complex { value: *value },
290 Self::Boolean { value } => Boolean { value: *value },
291 Self::Str { value } => Str { value },
292 Self::Bytes { value } => Bytes { value },
293 Self::Code { code } => Code { code },
294 Self::Tuple { elements } => Tuple { elements },
295 Self::Slice { elements } => Slice { elements },
296 Self::Frozenset { elements } => Frozenset { elements },
297 Self::None => None,
298 Self::Ellipsis => Ellipsis,
299 }
300 }
301}
302
303pub trait ConstantBag: Sized + Copy {
305 type Constant: Constant;
306
307 fn make_constant<C: Constant>(&self, constant: BorrowedConstant<'_, C>) -> Self::Constant;
308
309 fn make_int(&self, value: BigInt) -> Self::Constant;
310
311 fn make_tuple(&self, elements: impl Iterator<Item = Self::Constant>) -> Self::Constant;
312
313 fn make_code(&self, code: CodeObject<Self::Constant>) -> Self::Constant;
314
315 fn make_name(&self, name: &str) -> <Self::Constant as Constant>::Name;
316}
317
318pub trait AsBag {
319 type Bag: ConstantBag;
320
321 #[allow(clippy::wrong_self_convention)]
322 fn as_bag(self) -> Self::Bag;
323}
324
325impl<Bag: ConstantBag> AsBag for Bag {
326 type Bag = Self;
327
328 fn as_bag(self) -> Self {
329 self
330 }
331}
332
333#[derive(Clone, Copy)]
334pub struct BasicBag;
335
336impl ConstantBag for BasicBag {
337 type Constant = ConstantData;
338
339 fn make_constant<C: Constant>(&self, constant: BorrowedConstant<'_, C>) -> Self::Constant {
340 constant.to_owned()
341 }
342
343 fn make_int(&self, value: BigInt) -> Self::Constant {
344 ConstantData::Integer { value }
345 }
346
347 fn make_tuple(&self, elements: impl Iterator<Item = Self::Constant>) -> Self::Constant {
348 ConstantData::Tuple {
349 elements: elements.collect(),
350 }
351 }
352
353 fn make_code(&self, code: CodeObject<Self::Constant>) -> Self::Constant {
354 ConstantData::Code {
355 code: Box::new(code),
356 }
357 }
358
359 fn make_name(&self, name: &str) -> <Self::Constant as Constant>::Name {
360 name.to_owned()
361 }
362}
363
364#[derive(Clone)]
365pub struct Constants<C: Constant>(Box<[C]>);
366
367impl<C: Constant> Deref for Constants<C> {
368 type Target = [C];
369
370 fn deref(&self) -> &Self::Target {
371 &self.0
372 }
373}
374
375impl<C: Constant> Index<oparg::ConstIdx> for Constants<C> {
376 type Output = C;
377
378 fn index(&self, consti: oparg::ConstIdx) -> &Self::Output {
379 &self.0[consti.as_usize()]
380 }
381}
382
383impl<C: Constant> FromIterator<C> for Constants<C> {
384 fn from_iter<T: IntoIterator<Item = C>>(iter: T) -> Self {
385 Self(iter.into_iter().collect())
386 }
387}
388
389impl<T> Index<oparg::VarNum> for [T] {
391 type Output = T;
392
393 fn index(&self, var_num: oparg::VarNum) -> &Self::Output {
394 &self[var_num.as_usize()]
395 }
396}
397
398impl<T> IndexMut<oparg::VarNum> for [T] {
400 fn index_mut(&mut self, var_num: oparg::VarNum) -> &mut Self::Output {
401 &mut self[var_num.as_usize()]
402 }
403}
404
405pub const CO_FAST_HIDDEN: u8 = 0x10;
407pub const CO_FAST_LOCAL: u8 = 0x20;
408pub const CO_FAST_CELL: u8 = 0x40;
409pub const CO_FAST_FREE: u8 = 0x80;
410
411#[derive(Clone)]
414pub struct CodeObject<C: Constant = ConstantData> {
415 pub instructions: CodeUnits,
416 pub locations: Box<[(SourceLocation, SourceLocation)]>,
417 pub flags: CodeFlags,
418 pub posonlyarg_count: u32,
420 pub arg_count: u32,
421 pub kwonlyarg_count: u32,
422 pub source_path: C::Name,
423 pub first_line_number: Option<OneIndexed>,
424 pub max_stackdepth: u32,
425 pub obj_name: C::Name,
427 pub qualname: C::Name,
429 pub constants: Constants<C>,
430 pub names: Box<[C::Name]>,
431 pub varnames: Box<[C::Name]>,
432 pub cellvars: Box<[C::Name]>,
433 pub freevars: Box<[C::Name]>,
434 pub localspluskinds: Box<[u8]>,
437 pub linetable: Box<[u8]>,
439 pub exceptiontable: Box<[u8]>,
441}
442
443bitflags! {
444 #[derive(Copy, Clone, Debug, PartialEq)]
445 pub struct CodeFlags: u32 {
446 const OPTIMIZED = 0x0001;
447 const NEWLOCALS = 0x0002;
448 const VARARGS = 0x0004;
449 const VARKEYWORDS = 0x0008;
450 const NESTED = 0x0010;
451 const GENERATOR = 0x0020;
452 const COROUTINE = 0x0080;
453 const ITERABLE_COROUTINE = 0x0100;
454 const HAS_DOCSTRING = 0x4000000;
457 }
458}
459
460#[repr(C)]
461#[derive(Copy, Clone, Debug)]
462pub struct CodeUnit {
463 pub op: Instruction,
464 pub arg: OpArgByte,
465}
466
467const _: () = assert!(mem::size_of::<CodeUnit>() == 2);
468
469pub const ADAPTIVE_WARMUP_VALUE: u16 = adaptive_counter_bits(1, 1);
473pub const ADAPTIVE_COOLDOWN_VALUE: u16 = adaptive_counter_bits(52, 0);
477pub const JUMP_BACKWARD_INITIAL_VALUE: u16 = adaptive_counter_bits(4095, 12);
479
480const BACKOFF_BITS: u16 = 4;
481const MAX_BACKOFF: u16 = 12;
482const UNREACHABLE_BACKOFF: u16 = 15;
483
484pub const fn adaptive_counter_bits(value: u16, backoff: u16) -> u16 {
486 (value << BACKOFF_BITS) | backoff
487}
488
489#[inline]
491pub const fn adaptive_counter_triggers(counter: u16) -> bool {
492 counter < UNREACHABLE_BACKOFF
493}
494
495#[inline]
497pub const fn advance_adaptive_counter(counter: u16) -> u16 {
498 counter.wrapping_sub(1 << BACKOFF_BITS)
499}
500
501#[inline]
503pub const fn adaptive_counter_backoff(counter: u16) -> u16 {
504 let backoff = counter & ((1 << BACKOFF_BITS) - 1);
505 if backoff < MAX_BACKOFF {
506 adaptive_counter_bits((1 << (backoff + 1)) - 1, backoff + 1)
507 } else {
508 adaptive_counter_bits((1 << MAX_BACKOFF) - 1, MAX_BACKOFF)
509 }
510}
511
512impl CodeUnit {
513 pub const fn new(op: Instruction, arg: OpArgByte) -> Self {
514 Self { op, arg }
515 }
516}
517
518impl TryFrom<&[u8]> for CodeUnit {
519 type Error = MarshalError;
520
521 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
522 match value.len() {
523 2 => Ok(Self::new(value[0].try_into()?, value[1].into())),
524 _ => Err(Self::Error::InvalidBytecode),
525 }
526 }
527}
528
529pub struct CodeUnits {
530 units: UnsafeCell<Box<[CodeUnit]>>,
531 adaptive_counters: Box<[AtomicU16]>,
532 pointer_cache: Box<[AtomicUsize]>,
536}
537
538unsafe impl Sync for CodeUnits {}
546
547impl Clone for CodeUnits {
548 fn clone(&self) -> Self {
549 let units = unsafe { &*self.units.get() }.clone();
552 let adaptive_counters = self
553 .adaptive_counters
554 .iter()
555 .map(|c| AtomicU16::new(c.load(Ordering::Relaxed)))
556 .collect();
557 let pointer_cache = self
558 .pointer_cache
559 .iter()
560 .map(|c| AtomicUsize::new(c.load(Ordering::Relaxed)))
561 .collect();
562 Self {
563 units: UnsafeCell::new(units),
564 adaptive_counters,
565 pointer_cache,
566 }
567 }
568}
569
570impl fmt::Debug for CodeUnits {
571 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
572 let inner = unsafe { &*self.units.get() };
574 f.debug_tuple("CodeUnits").field(inner).finish()
575 }
576}
577
578impl TryFrom<&[u8]> for CodeUnits {
579 type Error = MarshalError;
580
581 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
582 if !value.len().is_multiple_of(2) {
583 return Err(Self::Error::InvalidBytecode);
584 }
585
586 let units = value
587 .chunks_exact(2)
588 .map(CodeUnit::try_from)
589 .collect::<Result<Vec<_>, _>>()?;
590 Ok(units.into())
591 }
592}
593
594impl<const N: usize> From<[CodeUnit; N]> for CodeUnits {
595 fn from(value: [CodeUnit; N]) -> Self {
596 Self::from(Vec::from(value))
597 }
598}
599
600impl From<Vec<CodeUnit>> for CodeUnits {
601 fn from(value: Vec<CodeUnit>) -> Self {
602 let units = value.into_boxed_slice();
603 let len = units.len();
604 let adaptive_counters = (0..len)
605 .map(|_| AtomicU16::new(0))
606 .collect::<Vec<_>>()
607 .into_boxed_slice();
608 let pointer_cache = (0..len)
609 .map(|_| AtomicUsize::new(0))
610 .collect::<Vec<_>>()
611 .into_boxed_slice();
612 Self {
613 units: UnsafeCell::new(units),
614 adaptive_counters,
615 pointer_cache,
616 }
617 }
618}
619
620impl FromIterator<CodeUnit> for CodeUnits {
621 fn from_iter<T: IntoIterator<Item = CodeUnit>>(iter: T) -> Self {
622 Self::from(iter.into_iter().collect::<Vec<_>>())
623 }
624}
625
626impl Deref for CodeUnits {
627 type Target = [CodeUnit];
628
629 fn deref(&self) -> &Self::Target {
630 unsafe { &*self.units.get() }
634 }
635}
636
637impl CodeUnits {
638 pub fn disable_specialization(&self) {
641 for counter in self.adaptive_counters.iter() {
642 counter.store(UNREACHABLE_BACKOFF, Ordering::Relaxed);
643 }
644 }
645
646 pub unsafe fn replace_op(&self, index: usize, new_op: Instruction) {
654 let units = unsafe { &*self.units.get() };
655 let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU8;
656 unsafe { &*ptr }.store(new_op.into(), Ordering::Release);
657 }
658
659 pub unsafe fn compare_exchange_op(
665 &self,
666 index: usize,
667 expected: Instruction,
668 new_op: Instruction,
669 ) -> bool {
670 let units = unsafe { &*self.units.get() };
671 let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU8;
672 unsafe { &*ptr }
673 .compare_exchange(
674 expected.into(),
675 new_op.into(),
676 Ordering::Release,
677 Ordering::Relaxed,
678 )
679 .is_ok()
680 }
681
682 pub fn read_op(&self, index: usize) -> Instruction {
685 let units = unsafe { &*self.units.get() };
686 let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU8;
687 let byte = unsafe { &*ptr }.load(Ordering::Acquire);
688 unsafe { mem::transmute::<u8, Instruction>(byte) }
690 }
691
692 pub fn read_arg(&self, index: usize) -> OpArgByte {
694 let units = unsafe { &*self.units.get() };
695 let ptr = units.as_ptr().wrapping_add(index) as *const u8;
696 let arg_ptr = unsafe { ptr.add(1) } as *const AtomicU8;
697 OpArgByte::from(unsafe { &*arg_ptr }.load(Ordering::Relaxed))
698 }
699
700 pub unsafe fn write_cache_u16(&self, index: usize, value: u16) {
707 let units = unsafe { &*self.units.get() };
708 let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU16;
709 unsafe { &*ptr }.store(value, Ordering::Relaxed);
710 }
711
712 pub fn read_cache_u16(&self, index: usize) -> u16 {
718 let units = unsafe { &*self.units.get() };
719 assert!(index < units.len(), "read_cache_u16: index out of bounds");
720 let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU16;
721 unsafe { &*ptr }.load(Ordering::Relaxed)
722 }
723
724 pub unsafe fn write_cache_u32(&self, index: usize, value: u32) {
729 unsafe {
730 self.write_cache_u16(index, value as u16);
731 self.write_cache_u16(index + 1, (value >> 16) as u16);
732 }
733 }
734
735 pub fn read_cache_u32(&self, index: usize) -> u32 {
740 let lo = self.read_cache_u16(index) as u32;
741 let hi = self.read_cache_u16(index + 1) as u32;
742 lo | (hi << 16)
743 }
744
745 pub unsafe fn write_cache_ptr(&self, index: usize, value: usize) {
757 self.pointer_cache[index].store(value, Ordering::Relaxed);
758 }
759
760 pub fn read_cache_ptr(&self, index: usize) -> usize {
767 self.pointer_cache[index].load(Ordering::Relaxed)
768 }
769
770 pub fn read_adaptive_counter(&self, index: usize) -> u16 {
773 self.adaptive_counters[index].load(Ordering::Relaxed)
774 }
775
776 pub unsafe fn write_adaptive_counter(&self, index: usize, value: u16) {
782 self.adaptive_counters[index].store(value, Ordering::Relaxed);
783 }
784
785 pub fn original_bytes(&self) -> Vec<u8> {
789 let len = self.len();
790 let mut out = Vec::with_capacity(len * 2);
791 let mut i = 0;
792 while i < len {
793 let op = self.read_op(i).deoptimize();
794 let arg = self.read_arg(i);
795 let caches = op.cache_entries();
796 out.push(u8::from(op));
797 out.push(u8::from(arg));
798 for _ in 0..caches {
800 i += 1;
801 out.push(0); out.push(0); }
804 i += 1;
805 }
806 out
807 }
808
809 pub fn quicken(&self) {
814 let len = self.len();
815 let mut i = 0;
816 while i < len {
817 let op = self.read_op(i);
818 let caches = op.cache_entries();
819 if caches > 0 {
820 if !op.is_instrumented() {
823 let cache_base = i + 1;
824 if cache_base < len {
825 let initial_counter = if matches!(op, Instruction::JumpBackward { .. }) {
826 JUMP_BACKWARD_INITIAL_VALUE
827 } else {
828 ADAPTIVE_WARMUP_VALUE
829 };
830 unsafe {
831 self.write_adaptive_counter(cache_base, initial_counter);
832 }
833 }
834 }
835 i += 1 + caches;
836 } else {
837 i += 1;
838 }
839 }
840 }
841}
842
843#[derive(Debug, Clone)]
853pub enum ConstantData {
854 Tuple {
855 elements: Vec<ConstantData>,
856 },
857 Integer {
858 value: BigInt,
859 },
860 Float {
861 value: f64,
862 },
863 Complex {
864 value: Complex64,
865 },
866 Boolean {
867 value: bool,
868 },
869 Str {
870 value: Wtf8Buf,
871 },
872 Bytes {
873 value: Vec<u8>,
874 },
875 Code {
876 code: Box<CodeObject>,
877 },
878 Slice {
880 elements: Box<[ConstantData; 3]>,
881 },
882 Frozenset {
883 elements: Vec<ConstantData>,
884 },
885 None,
886 Ellipsis,
887}
888
889impl PartialEq for ConstantData {
890 fn eq(&self, other: &Self) -> bool {
891 use ConstantData::*;
892
893 match (self, other) {
894 (Integer { value: a }, Integer { value: b }) => a == b,
895 (Float { value: a }, Float { value: b }) => a.to_bits() == b.to_bits(),
898 (Complex { value: a }, Complex { value: b }) => {
899 a.re.to_bits() == b.re.to_bits() && a.im.to_bits() == b.im.to_bits()
900 }
901 (Boolean { value: a }, Boolean { value: b }) => a == b,
902 (Str { value: a }, Str { value: b }) => a == b,
903 (Bytes { value: a }, Bytes { value: b }) => a == b,
904 (Code { code: a }, Code { code: b }) => core::ptr::eq(a.as_ref(), b.as_ref()),
905 (Tuple { elements: a }, Tuple { elements: b }) => a == b,
906 (Slice { elements: a }, Slice { elements: b }) => a == b,
907 (Frozenset { elements: a }, Frozenset { elements: b }) => a == b,
908 (None, None) => true,
909 (Ellipsis, Ellipsis) => true,
910 _ => false,
911 }
912 }
913}
914
915impl Eq for ConstantData {}
916
917impl hash::Hash for ConstantData {
918 fn hash<H: hash::Hasher>(&self, state: &mut H) {
919 use ConstantData::*;
920
921 mem::discriminant(self).hash(state);
922 match self {
923 Integer { value } => value.hash(state),
924 Float { value } => value.to_bits().hash(state),
925 Complex { value } => {
926 value.re.to_bits().hash(state);
927 value.im.to_bits().hash(state);
928 }
929 Boolean { value } => value.hash(state),
930 Str { value } => value.hash(state),
931 Bytes { value } => value.hash(state),
932 Code { code } => core::ptr::hash(code.as_ref(), state),
933 Tuple { elements } => elements.hash(state),
934 Slice { elements } => elements.hash(state),
935 Frozenset { elements } => elements.hash(state),
936 None => {}
937 Ellipsis => {}
938 }
939 }
940}
941
942pub enum BorrowedConstant<'a, C: Constant> {
944 Integer { value: &'a BigInt },
945 Float { value: f64 },
946 Complex { value: Complex64 },
947 Boolean { value: bool },
948 Str { value: &'a Wtf8 },
949 Bytes { value: &'a [u8] },
950 Code { code: &'a CodeObject<C> },
951 Tuple { elements: &'a [C] },
952 Slice { elements: &'a [C; 3] },
953 Frozenset { elements: &'a [C] },
954 None,
955 Ellipsis,
956}
957
958impl<C: Constant> Copy for BorrowedConstant<'_, C> {}
959
960impl<C: Constant> Clone for BorrowedConstant<'_, C> {
961 fn clone(&self) -> Self {
962 *self
963 }
964}
965
966impl<C: Constant> BorrowedConstant<'_, C> {
967 pub fn fmt_display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
968 match self {
969 BorrowedConstant::Integer { value } => write!(f, "{value}"),
970 BorrowedConstant::Float { value } => write!(f, "{value}"),
971 BorrowedConstant::Complex { value } => write!(f, "{value}"),
972 BorrowedConstant::Boolean { value } => {
973 write!(f, "{}", if *value { "True" } else { "False" })
974 }
975 BorrowedConstant::Str { value } => write!(f, "{value:?}"),
976 BorrowedConstant::Bytes { value } => write!(f, r#"b"{}""#, value.escape_ascii()),
977 BorrowedConstant::Code { code } => write!(f, "{code:?}"),
978 BorrowedConstant::Tuple { elements } => {
979 write!(f, "(")?;
980 let mut first = true;
981 for c in *elements {
982 if first {
983 first = false
984 } else {
985 write!(f, ", ")?;
986 }
987 c.borrow_constant().fmt_display(f)?;
988 }
989 write!(f, ")")
990 }
991 BorrowedConstant::Slice { elements } => {
992 write!(f, "slice(")?;
993 elements[0].borrow_constant().fmt_display(f)?;
994 write!(f, ", ")?;
995 elements[1].borrow_constant().fmt_display(f)?;
996 write!(f, ", ")?;
997 elements[2].borrow_constant().fmt_display(f)?;
998 write!(f, ")")
999 }
1000 BorrowedConstant::Frozenset { elements } => {
1001 write!(f, "frozenset({{")?;
1002 let mut first = true;
1003 for c in *elements {
1004 if first {
1005 first = false
1006 } else {
1007 write!(f, ", ")?;
1008 }
1009 c.borrow_constant().fmt_display(f)?;
1010 }
1011 write!(f, "}})")
1012 }
1013 BorrowedConstant::None => write!(f, "None"),
1014 BorrowedConstant::Ellipsis => write!(f, "..."),
1015 }
1016 }
1017
1018 pub fn to_owned(self) -> ConstantData {
1019 use ConstantData::*;
1020
1021 match self {
1022 BorrowedConstant::Integer { value } => Integer {
1023 value: value.clone(),
1024 },
1025 BorrowedConstant::Float { value } => Float { value },
1026 BorrowedConstant::Complex { value } => Complex { value },
1027 BorrowedConstant::Boolean { value } => Boolean { value },
1028 BorrowedConstant::Str { value } => Str {
1029 value: value.to_owned(),
1030 },
1031 BorrowedConstant::Bytes { value } => Bytes {
1032 value: value.to_owned(),
1033 },
1034 BorrowedConstant::Code { code } => Code {
1035 code: Box::new(code.map_clone_bag(&BasicBag)),
1036 },
1037 BorrowedConstant::Tuple { elements } => Tuple {
1038 elements: elements
1039 .iter()
1040 .map(|c| c.borrow_constant().to_owned())
1041 .collect(),
1042 },
1043 BorrowedConstant::Slice { elements } => Slice {
1044 elements: Box::new(elements.each_ref().map(|c| c.borrow_constant().to_owned())),
1045 },
1046 BorrowedConstant::Frozenset { elements } => Frozenset {
1047 elements: elements
1048 .iter()
1049 .map(|c| c.borrow_constant().to_owned())
1050 .collect(),
1051 },
1052 BorrowedConstant::None => None,
1053 BorrowedConstant::Ellipsis => Ellipsis,
1054 }
1055 }
1056}
1057
1058pub struct Arguments<'a, N: AsRef<str>> {
1068 pub posonlyargs: &'a [N],
1069 pub args: &'a [N],
1070 pub vararg: Option<&'a N>,
1071 pub kwonlyargs: &'a [N],
1072 pub varkwarg: Option<&'a N>,
1073}
1074
1075impl<N: AsRef<str>> fmt::Debug for Arguments<'_, N> {
1076 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1077 macro_rules! fmt_slice {
1078 ($x:expr) => {
1079 format_args!("[{}]", $x.iter().map(AsRef::as_ref).format(", "))
1080 };
1081 }
1082 f.debug_struct("Arguments")
1083 .field("posonlyargs", &fmt_slice!(self.posonlyargs))
1084 .field("args", &fmt_slice!(self.posonlyargs))
1085 .field("vararg", &self.vararg.map(N::as_ref))
1086 .field("kwonlyargs", &fmt_slice!(self.kwonlyargs))
1087 .field("varkwarg", &self.varkwarg.map(N::as_ref))
1088 .finish()
1089 }
1090}
1091
1092impl<C: Constant> CodeObject<C> {
1093 pub fn arg_names(&self) -> Arguments<'_, C::Name> {
1096 let nargs = self.arg_count as usize;
1097 let nkwargs = self.kwonlyarg_count as usize;
1098 let mut varargs_pos = nargs + nkwargs;
1099 let posonlyargs = &self.varnames[..self.posonlyarg_count as usize];
1100 let args = &self.varnames[..nargs];
1101 let kwonlyargs = &self.varnames[nargs..varargs_pos];
1102
1103 let vararg = if self.flags.contains(CodeFlags::VARARGS) {
1104 let vararg = &self.varnames[varargs_pos];
1105 varargs_pos += 1;
1106 Some(vararg)
1107 } else {
1108 None
1109 };
1110 let varkwarg = if self.flags.contains(CodeFlags::VARKEYWORDS) {
1111 Some(&self.varnames[varargs_pos])
1112 } else {
1113 None
1114 };
1115
1116 Arguments {
1117 posonlyargs,
1118 args,
1119 vararg,
1120 kwonlyargs,
1121 varkwarg,
1122 }
1123 }
1124
1125 pub fn label_targets(&self) -> BTreeSet<Label> {
1127 let mut label_targets = BTreeSet::new();
1128 let mut arg_state = OpArgState::default();
1129 for instruction in &*self.instructions {
1130 let (instruction, arg) = arg_state.get(*instruction);
1131 if let Some(l) = instruction.label_arg() {
1132 label_targets.insert(l.get(arg));
1133 }
1134 }
1135 label_targets
1136 }
1137
1138 fn display_inner(
1139 &self,
1140 f: &mut fmt::Formatter<'_>,
1141 expand_code_objects: bool,
1142 level: usize,
1143 ) -> fmt::Result {
1144 let label_targets = self.label_targets();
1145 let line_digits = (3).max(self.locations.last().unwrap().0.line.digits().get());
1146 let offset_digits = (4).max(1 + self.instructions.len().ilog10() as usize);
1147 let mut last_line = OneIndexed::MAX;
1148 let mut arg_state = OpArgState::default();
1149 for (offset, &instruction) in self.instructions.iter().enumerate() {
1150 let (instruction, arg) = arg_state.get(instruction);
1151 let line = self.locations[offset].0.line;
1153 if line != last_line {
1154 if last_line != OneIndexed::MAX {
1155 writeln!(f)?;
1156 }
1157 last_line = line;
1158 write!(f, "{line:line_digits$}")?;
1159 } else {
1160 for _ in 0..line_digits {
1161 write!(f, " ")?;
1162 }
1163 }
1164 write!(f, " ")?;
1165
1166 for _ in 0..level {
1168 write!(f, " ")?;
1169 }
1170
1171 let arrow = if label_targets.contains(&Label::from_u32(offset as u32)) {
1173 ">>"
1174 } else {
1175 " "
1176 };
1177 write!(f, "{arrow} {offset:offset_digits$} ")?;
1178
1179 instruction.fmt_dis(arg, f, self, expand_code_objects, 21, level)?;
1181 writeln!(f)?;
1182 }
1183 Ok(())
1184 }
1185
1186 pub fn display_expand_code_objects(&self) -> impl fmt::Display + '_ {
1188 struct Display<'a, C: Constant>(&'a CodeObject<C>);
1189 impl<C: Constant> fmt::Display for Display<'_, C> {
1190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1191 self.0.display_inner(f, true, 1)
1192 }
1193 }
1194 Display(self)
1195 }
1196
1197 pub fn map_bag<Bag: ConstantBag>(self, bag: Bag) -> CodeObject<Bag::Constant> {
1199 let map_names = |names: Box<[C::Name]>| {
1200 names
1201 .iter()
1202 .map(|x| bag.make_name(x.as_ref()))
1203 .collect::<Box<[_]>>()
1204 };
1205 CodeObject {
1206 constants: self
1207 .constants
1208 .iter()
1209 .map(|x| bag.make_constant(x.borrow_constant()))
1210 .collect(),
1211 names: map_names(self.names),
1212 varnames: map_names(self.varnames),
1213 cellvars: map_names(self.cellvars),
1214 freevars: map_names(self.freevars),
1215 source_path: bag.make_name(self.source_path.as_ref()),
1216 obj_name: bag.make_name(self.obj_name.as_ref()),
1217 qualname: bag.make_name(self.qualname.as_ref()),
1218
1219 instructions: self.instructions,
1220 locations: self.locations,
1221 flags: self.flags,
1222 posonlyarg_count: self.posonlyarg_count,
1223 arg_count: self.arg_count,
1224 kwonlyarg_count: self.kwonlyarg_count,
1225 first_line_number: self.first_line_number,
1226 max_stackdepth: self.max_stackdepth,
1227 localspluskinds: self.localspluskinds,
1228 linetable: self.linetable,
1229 exceptiontable: self.exceptiontable,
1230 }
1231 }
1232
1233 pub fn map_clone_bag<Bag: ConstantBag>(&self, bag: &Bag) -> CodeObject<Bag::Constant> {
1235 let map_names =
1236 |names: &[C::Name]| names.iter().map(|x| bag.make_name(x.as_ref())).collect();
1237 CodeObject {
1238 constants: self
1239 .constants
1240 .iter()
1241 .map(|x| bag.make_constant(x.borrow_constant()))
1242 .collect(),
1243 names: map_names(&self.names),
1244 varnames: map_names(&self.varnames),
1245 cellvars: map_names(&self.cellvars),
1246 freevars: map_names(&self.freevars),
1247 source_path: bag.make_name(self.source_path.as_ref()),
1248 obj_name: bag.make_name(self.obj_name.as_ref()),
1249 qualname: bag.make_name(self.qualname.as_ref()),
1250
1251 instructions: self.instructions.clone(),
1252 locations: self.locations.clone(),
1253 flags: self.flags,
1254 posonlyarg_count: self.posonlyarg_count,
1255 arg_count: self.arg_count,
1256 kwonlyarg_count: self.kwonlyarg_count,
1257 first_line_number: self.first_line_number,
1258 max_stackdepth: self.max_stackdepth,
1259 localspluskinds: self.localspluskinds.clone(),
1260 linetable: self.linetable.clone(),
1261 exceptiontable: self.exceptiontable.clone(),
1262 }
1263 }
1264}
1265
1266impl<C: Constant> fmt::Display for CodeObject<C> {
1267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1268 self.display_inner(f, false, 1)?;
1269 for constant in &*self.constants {
1270 if let BorrowedConstant::Code { code } = constant.borrow_constant() {
1271 writeln!(f, "\nDisassembly of {code:?}")?;
1272 code.fmt(f)?;
1273 }
1274 }
1275 Ok(())
1276 }
1277}
1278
1279pub trait InstrDisplayContext {
1280 type Constant: Constant;
1281
1282 fn get_constant(&self, consti: oparg::ConstIdx) -> &Self::Constant;
1283
1284 fn get_name(&self, i: usize) -> &str;
1285
1286 fn get_varname(&self, var_num: oparg::VarNum) -> &str;
1287
1288 fn get_localsplus_name(&self, var_num: oparg::VarNum) -> &str;
1290}
1291
1292impl<C: Constant> InstrDisplayContext for CodeObject<C> {
1293 type Constant = C;
1294
1295 fn get_constant(&self, consti: oparg::ConstIdx) -> &C {
1296 &self.constants[consti]
1297 }
1298
1299 fn get_name(&self, i: usize) -> &str {
1300 self.names[i].as_ref()
1301 }
1302
1303 fn get_varname(&self, var_num: oparg::VarNum) -> &str {
1304 self.varnames[var_num].as_ref()
1305 }
1306
1307 fn get_localsplus_name(&self, var_num: oparg::VarNum) -> &str {
1308 let idx = var_num.as_usize();
1309 let nlocals = self.varnames.len();
1310 if idx < nlocals {
1311 self.varnames[idx].as_ref()
1312 } else {
1313 let cell_idx = idx - nlocals;
1314 self.cellvars
1315 .get(cell_idx)
1316 .unwrap_or_else(|| &self.freevars[cell_idx - self.cellvars.len()])
1317 .as_ref()
1318 }
1319 }
1320}
1321
1322impl fmt::Display for ConstantData {
1323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1324 self.borrow_constant().fmt_display(f)
1325 }
1326}
1327
1328impl<C: Constant> fmt::Debug for CodeObject<C> {
1329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1330 write!(
1331 f,
1332 "<code object {} at ??? file {:?}, line {}>",
1333 self.obj_name.as_ref(),
1334 self.source_path.as_ref(),
1335 self.first_line_number.map_or(-1, |x| x.get() as i32)
1336 )
1337 }
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342 use super::*;
1343 use alloc::{vec, vec::Vec};
1344
1345 #[test]
1346 fn test_exception_table_encode_decode() {
1347 let entries = vec![
1348 ExceptionTableEntry::new(0, 10, 20, 2, false),
1349 ExceptionTableEntry::new(15, 25, 30, 1, true),
1350 ];
1351
1352 let encoded = encode_exception_table(&entries);
1353
1354 let handler = find_exception_handler(&encoded, 5);
1356 assert!(handler.is_some());
1357 let handler = handler.unwrap();
1358 assert_eq!(handler.start, 0);
1359 assert_eq!(handler.end, 10);
1360 assert_eq!(handler.target, 20);
1361 assert_eq!(handler.depth, 2);
1362 assert!(!handler.push_lasti);
1363
1364 let handler = find_exception_handler(&encoded, 20);
1366 assert!(handler.is_some());
1367 let handler = handler.unwrap();
1368 assert_eq!(handler.start, 15);
1369 assert_eq!(handler.end, 25);
1370 assert_eq!(handler.target, 30);
1371 assert_eq!(handler.depth, 1);
1372 assert!(handler.push_lasti);
1373
1374 let handler = find_exception_handler(&encoded, 12);
1376 assert!(handler.is_none());
1377
1378 let handler = find_exception_handler(&encoded, 30);
1380 assert!(handler.is_none());
1381 }
1382
1383 #[test]
1384 fn test_exception_table_empty() {
1385 let entries: Vec<ExceptionTableEntry> = vec![];
1386 let encoded = encode_exception_table(&entries);
1387 assert!(encoded.is_empty());
1388 assert!(find_exception_handler(&encoded, 0).is_none());
1389 }
1390
1391 #[test]
1392 fn test_exception_table_single_entry() {
1393 let entries = vec![ExceptionTableEntry::new(5, 15, 100, 3, true)];
1394 let encoded = encode_exception_table(&entries);
1395
1396 let handler = find_exception_handler(&encoded, 10);
1398 assert!(handler.is_some());
1399 let handler = handler.unwrap();
1400 assert_eq!(handler.target, 100);
1401 assert_eq!(handler.depth, 3);
1402 assert!(handler.push_lasti);
1403
1404 assert!(find_exception_handler(&encoded, 5).is_some());
1406
1407 assert!(find_exception_handler(&encoded, 15).is_none());
1409 }
1410}