1use crate::anystr::AnyStr;
4
5use crate::{
6 AsObject, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, PyStackRef,
7 TryFromObject, VirtualMachine,
8 builtins::{
9 PyBaseException, PyBaseExceptionRef, PyBaseObject, PyCode, PyCoroutine, PyDict, PyDictRef,
10 PyFloat, PyFrozenSet, PyGenerator, PyInt, PyInterpolation, PyList, PyModule, PyProperty,
11 PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback, PyType, PyUtf8Str,
12 builtin_func::PyNativeFunction,
13 descriptor::{PyMemberDescriptor, PyMethodDescriptor},
14 frame::stack_analysis,
15 function::{PyBoundMethod, PyCell, PyCellRef, PyFunction, vectorcall_function},
16 list::PyListIterator,
17 range::PyRangeIterator,
18 tuple::{PyTuple, PyTupleIterator, PyTupleRef},
19 },
20 bytecode::{
21 self, ADAPTIVE_COOLDOWN_VALUE, Instruction, LoadAttr, LoadSuperAttr, Opcode, SpecialMethod,
22 },
23 convert::{ToPyObject, ToPyResult},
24 coroutine::Coro,
25 exceptions::ExceptionCtor,
26 function::{ArgMapping, Callee, Either, FuncArgs, KwArgs, PyMethodFlags},
27 object::PyAtomicBorrow,
28 object::{Traverse, TraverseFn},
29 protocol::{PyIter, PyIterReturn},
30 scope::Scope,
31 sliceable::SliceableSequenceOp,
32 stdlib::{
33 _typing, builtins,
34 sys::monitoring::{self, MonitoringEvent},
35 },
36 types::{PyComparisonOp, PyTypeFlags},
37 vm::{Context, PyMethod},
38};
39use alloc::fmt;
40use bstr::ByteSlice;
41use core::cell::UnsafeCell;
42use core::ptr::NonNull;
43use core::sync::atomic;
44use core::sync::atomic::Ordering::{Acquire, Relaxed};
45use itertools::Itertools;
46use malachite_bigint::BigInt;
47use num_traits::{ToPrimitive, Zero};
48use rustpython_common::atomic::{PyAtomic, Radium};
49use rustpython_common::{
50 lock::{OnceCell, PyMutex},
51 wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
52};
53use rustpython_compiler_core::{OneIndexed, SourceLocation};
54
55pub type FrameObjectRef = PyRef<FrameObject>;
56
57unsafe fn owned_chain_frame(iframe: *const InterpreterFrame) -> Option<FrameObjectRef> {
72 if iframe.is_null() {
73 return None;
74 }
75 let iframe_ref = unsafe { &*iframe };
76 let fo = iframe_ref.frame_obj()?;
77 Some(fo.to_owned())
78}
79
80#[must_use]
82pub fn current_thread_frame() -> Option<FrameObjectRef> {
83 let ptr = crate::vm::thread::get_current_frame();
84 unsafe { owned_chain_frame(ptr) }
85}
86
87#[must_use]
89pub fn current_thread_iframe() -> *const InterpreterFrame {
90 crate::vm::thread::get_current_frame()
91}
92
93#[must_use]
98pub fn current_thread_frame_materialize(vm: &VirtualMachine) -> Option<FrameObjectRef> {
99 let ptr = crate::vm::thread::get_current_frame();
100 if ptr.is_null() {
101 return None;
102 }
103 let iframe = unsafe { &*ptr };
104 Some(iframe.materialize(vm).to_owned())
105}
106
107#[must_use]
110pub fn current_globals() -> Option<PyDictRef> {
111 let ptr = crate::vm::thread::get_current_frame();
112 if ptr.is_null() {
113 return None;
114 }
115 Some(unsafe { (*ptr).globals().to_owned() })
116}
117
118#[must_use]
121pub fn current_code() -> Option<PyRef<PyCode>> {
122 let ptr = crate::vm::thread::get_current_frame();
123 if ptr.is_null() {
124 return None;
125 }
126 Some(unsafe { (*ptr).code().to_owned() })
127}
128
129#[must_use]
131pub fn current_builtins() -> Option<PyObjectRef> {
132 let ptr = crate::vm::thread::get_current_frame();
133 if ptr.is_null() {
134 return None;
135 }
136 Some(unsafe { (*ptr).builtins().to_owned() })
137}
138
139#[must_use]
143pub fn frame_at_offset(offset: usize, vm: &VirtualMachine) -> Option<FrameObjectRef> {
144 let mut cur = crate::vm::thread::get_current_frame();
145 let mut remaining = offset;
146 while !cur.is_null() {
147 if remaining == 0 {
148 let iframe = unsafe { &*cur };
149 return Some(iframe.materialize(vm).to_owned());
150 }
151 remaining -= 1;
152 cur = unsafe { (*cur).previous.load(Relaxed) as *const InterpreterFrame };
153 }
154 None
155}
156
157#[must_use]
160pub fn find_owned_chain_frame_by_iframe(target: *const InterpreterFrame) -> Option<FrameObjectRef> {
161 let mut cur = crate::vm::thread::get_current_frame();
162 while !cur.is_null() {
163 if core::ptr::eq(cur, target) {
164 return unsafe { owned_chain_frame(cur) };
165 }
166 cur = unsafe { (*cur).previous.load(Relaxed) as *const InterpreterFrame };
167 }
168 None
169}
170
171#[must_use]
174pub fn find_owned_chain_frame(target: *const FrameObject) -> Option<FrameObjectRef> {
175 let mut cur = crate::vm::thread::get_current_frame();
176 while !cur.is_null() {
177 let iframe_ref = unsafe { &*cur };
178 if let Some(fo) = iframe_ref.frame_obj() {
179 let fo_payload: *const FrameObject = &**fo;
180 if core::ptr::eq(fo_payload, target) {
181 return Some(fo.to_owned());
182 }
183 }
184 cur = iframe_ref.previous.load(Relaxed) as *const InterpreterFrame;
185 }
186 None
187}
188
189pub fn for_each_current_frame(mut f: impl FnMut(&Py<FrameObject>)) {
192 let mut cur = crate::vm::thread::get_current_frame();
193 while !cur.is_null() {
194 let iframe_ref = unsafe { &*cur };
195 if let Some(fo) = iframe_ref.frame_obj() {
196 f(fo);
197 }
198 cur = iframe_ref.previous.load(Relaxed) as *const InterpreterFrame;
199 }
200}
201
202#[derive(Clone, Debug)]
206enum UnwindReason {
207 Returning { value: PyObjectRef },
209
210 Raising {
213 exception: PyBaseExceptionRef,
214 offset: u32,
216 },
217}
218
219#[repr(i8)]
222#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub(crate) enum FrameOwner {
224 Thread = 0,
226 Generator = 1,
228 FrameObject = 2,
231}
232
233impl FrameOwner {
234 pub(crate) fn from_i8(v: i8) -> Self {
235 match v {
236 0 => Self::Thread,
237 1 => Self::Generator,
238 _ => Self::FrameObject,
239 }
240 }
241}
242
243pub(crate) struct FrameUnsafeCell<T>(UnsafeCell<T>);
251
252impl<T> FrameUnsafeCell<T> {
253 fn new(value: T) -> Self {
254 Self(UnsafeCell::new(value))
255 }
256
257 #[inline(always)]
260 unsafe fn get(&self) -> *mut T {
261 self.0.get()
262 }
263
264 #[inline(always)]
266 fn get_mut(&mut self) -> &mut T {
267 self.0.get_mut()
268 }
269}
270
271#[cfg(feature = "threading")]
273unsafe impl<T: Send> Send for FrameUnsafeCell<T> {}
274#[cfg(feature = "threading")]
275unsafe impl<T: Send> Sync for FrameUnsafeCell<T> {}
276
277pub(crate) const BORROW_LOCAL_LOADS: bool = true;
290
291pub struct LocalsPlus {
302 data: LocalsPlusData,
304 nlocalsplus: u32,
306 stack_top: u32,
308}
309
310enum LocalsPlusData {
311 Heap(Box<[usize]>),
313 DataStack { ptr: *mut usize, capacity: usize },
316}
317
318#[cfg(feature = "threading")]
321unsafe impl Send for LocalsPlusData {}
322#[cfg(feature = "threading")]
323unsafe impl Sync for LocalsPlusData {}
324
325const _: () = {
326 assert!(core::mem::size_of::<Option<PyObjectRef>>() == core::mem::size_of::<usize>());
327 };
329
330impl LocalsPlus {
331 fn new(nlocalsplus: usize, stacksize: usize) -> Self {
333 let capacity = nlocalsplus
334 .checked_add(stacksize)
335 .expect("LocalsPlus capacity overflow");
336 let nlocalsplus_u32 = u32::try_from(nlocalsplus).expect("nlocalsplus exceeds u32");
337 Self {
338 data: LocalsPlusData::Heap(vec![0usize; capacity].into_boxed_slice()),
339 nlocalsplus: nlocalsplus_u32,
340 stack_top: 0,
341 }
342 }
343
344 pub(crate) fn new_on_datastack(
351 nlocalsplus: usize,
352 stacksize: usize,
353 vm: &VirtualMachine,
354 ) -> Self {
355 let capacity = nlocalsplus
356 .checked_add(stacksize)
357 .expect("LocalsPlus capacity overflow");
358 let byte_size = capacity
359 .checked_mul(core::mem::size_of::<usize>())
360 .expect("LocalsPlus byte size overflow");
361 let nlocalsplus_u32 = u32::try_from(nlocalsplus).expect("nlocalsplus exceeds u32");
362 let ptr = vm.datastack_push(byte_size) as *mut usize;
363 unsafe { core::ptr::write_bytes(ptr, 0, capacity) };
365 Self {
366 data: LocalsPlusData::DataStack { ptr, capacity },
367 nlocalsplus: nlocalsplus_u32,
368 stack_top: 0,
369 }
370 }
371
372 fn materialize_to_heap(&mut self) -> Option<*mut u8> {
376 if let LocalsPlusData::DataStack { ptr, capacity } = &self.data {
377 let base = *ptr as *mut u8;
378 let heap_data = unsafe { core::slice::from_raw_parts(*ptr, *capacity) }
379 .to_vec()
380 .into_boxed_slice();
381 self.data = LocalsPlusData::Heap(heap_data);
382 Some(base)
383 } else {
384 None
385 }
386 }
387
388 pub(crate) fn release_datastack(&mut self) -> Option<*mut u8> {
396 let LocalsPlusData::DataStack { ptr, .. } = &self.data else {
397 return None;
398 };
399 let base = *ptr as *mut u8;
400 self.drop_values();
404 self.data = LocalsPlusData::Heap(Box::default());
405 self.nlocalsplus = 0;
408 Some(base)
409 }
410
411 pub(crate) unsafe fn sync_fastlocals_from(&mut self, src: &Self) {
418 let n = core::cmp::min(self.nlocalsplus as usize, src.nlocalsplus as usize);
419 let dst = self.fastlocals_mut();
420 let source = src.fastlocals();
421 for i in 0..n {
422 let old = dst[i].take();
423 dst[i].clone_from(&source[i]);
424 drop(old);
425 }
426 }
427
428 fn drop_values(&mut self) {
430 self.stack_clear();
431 let fastlocals = self.fastlocals_mut();
432 for slot in fastlocals.iter_mut() {
433 let _ = slot.take();
434 }
435 }
436
437 #[inline(always)]
440 fn data_as_slice(&self) -> &[usize] {
441 match &self.data {
442 LocalsPlusData::Heap(b) => b,
443 LocalsPlusData::DataStack { ptr, capacity } => unsafe {
444 core::slice::from_raw_parts(*ptr, *capacity)
445 },
446 }
447 }
448
449 #[inline(always)]
450 fn data_as_mut_slice(&mut self) -> &mut [usize] {
451 match &mut self.data {
452 LocalsPlusData::Heap(b) => b,
453 LocalsPlusData::DataStack { ptr, capacity } => unsafe {
454 core::slice::from_raw_parts_mut(*ptr, *capacity)
455 },
456 }
457 }
458
459 #[inline(always)]
461 fn capacity(&self) -> usize {
462 match &self.data {
463 LocalsPlusData::Heap(b) => b.len(),
464 LocalsPlusData::DataStack { capacity, .. } => *capacity,
465 }
466 }
467
468 fn is_datastack_backed(&self) -> bool {
471 matches!(self.data, LocalsPlusData::DataStack { .. })
472 }
473
474 #[inline(always)]
476 fn stack_capacity(&self) -> usize {
477 self.capacity() - self.nlocalsplus as usize
478 }
479
480 #[inline(always)]
484 pub(crate) fn fastlocals(&self) -> &[Option<PyObjectRef>] {
485 let data = self.data_as_slice();
486 let ptr = data.as_ptr() as *const Option<PyObjectRef>;
487 unsafe { core::slice::from_raw_parts(ptr, self.nlocalsplus as usize) }
488 }
489
490 #[inline(always)]
492 pub(crate) fn fastlocals_mut(&mut self) -> &mut [Option<PyObjectRef>] {
493 let nlocalsplus = self.nlocalsplus as usize;
494 let data = self.data_as_mut_slice();
495 let ptr = data.as_mut_ptr() as *mut Option<PyObjectRef>;
496 unsafe { core::slice::from_raw_parts_mut(ptr, nlocalsplus) }
497 }
498
499 #[inline(always)]
503 fn stack_len(&self) -> usize {
504 self.stack_top as usize
505 }
506
507 #[inline(always)]
509 fn stack_is_empty(&self) -> bool {
510 self.stack_top == 0
511 }
512
513 #[inline(always)]
515 fn stack_push(&mut self, val: Option<PyStackRef>) {
516 let idx = self.nlocalsplus as usize + self.stack_top as usize;
517 debug_assert!(
518 idx < self.capacity(),
519 "stack overflow: stack_top={}, capacity={}",
520 self.stack_top,
521 self.stack_capacity()
522 );
523 let data = self.data_as_mut_slice();
524 data[idx] = unsafe { core::mem::transmute::<Option<PyStackRef>, usize>(val) };
525 self.stack_top += 1;
526 }
527
528 #[inline(always)]
530 fn stack_try_push(&mut self, val: Option<PyStackRef>) -> Result<(), Option<PyStackRef>> {
531 let idx = self.nlocalsplus as usize + self.stack_top as usize;
532 if idx >= self.capacity() {
533 return Err(val);
534 }
535 let data = self.data_as_mut_slice();
536 data[idx] = unsafe { core::mem::transmute::<Option<PyStackRef>, usize>(val) };
537 self.stack_top += 1;
538 Ok(())
539 }
540
541 pub(crate) fn push_stack(&mut self, value: PyObjectRef) {
544 self.stack_try_push(Some(PyStackRef::new_owned(value)))
545 .expect("stack overflow in push_stack");
546 }
547
548 #[inline(always)]
550 fn stack_pop(&mut self) -> Option<PyStackRef> {
551 debug_assert!(self.stack_top > 0, "stack underflow");
552 self.stack_top -= 1;
553 let idx = self.nlocalsplus as usize + self.stack_top as usize;
554 let data = self.data_as_mut_slice();
555 let raw = core::mem::replace(&mut data[idx], 0);
556 unsafe { core::mem::transmute::<usize, Option<PyStackRef>>(raw) }
557 }
558
559 #[inline(always)]
569 fn debug_audit_local_release(&self, idx: usize) {
570 #[cfg(debug_assertions)]
571 {
572 let Some(old) = self.fastlocals()[idx].as_ref() else {
573 return;
574 };
575 if old.strong_count() != 1 {
577 return;
578 }
579 let old_ptr = old.as_object() as *const PyObject;
580 for i in 0..self.stack_top as usize {
581 if let Some(stack_ref) = self.stack_index(i)
582 && stack_ref.is_borrowed()
583 && core::ptr::eq(stack_ref.as_object() as *const PyObject, old_ptr)
584 {
585 panic!(
586 "borrow invariant violated: fastlocals[{idx}] is dropping the last \
587 reference to an object that stack slot {i} still borrows"
588 );
589 }
590 }
591 }
592 #[cfg(not(debug_assertions))]
593 let _ = idx;
594 }
595
596 fn promote_stack(&mut self) {
602 for idx in 0..self.stack_top as usize {
603 if let Some(stack_ref) = self.stack_index_mut(idx) {
604 stack_ref.promote();
605 }
606 }
607 }
608
609 #[inline(always)]
611 fn stack_as_slice(&self) -> &[Option<PyStackRef>] {
612 let data = self.data_as_slice();
613 let base = self.nlocalsplus as usize;
614 let ptr = unsafe { (data.as_ptr().add(base)) as *const Option<PyStackRef> };
615 unsafe { core::slice::from_raw_parts(ptr, self.stack_top as usize) }
616 }
617
618 #[inline(always)]
620 fn stack_index(&self, idx: usize) -> Option<&PyStackRef> {
621 debug_assert!(idx < self.stack_top as usize);
622 let data = self.data_as_slice();
623 let raw_idx = self.nlocalsplus as usize + idx;
624 unsafe { (*(data.as_ptr().add(raw_idx) as *const Option<PyStackRef>)).as_ref() }
625 }
626
627 #[inline(always)]
629 fn stack_index_mut(&mut self, idx: usize) -> &mut Option<PyStackRef> {
630 debug_assert!(idx < self.stack_top as usize);
631 let raw_idx = self.nlocalsplus as usize + idx;
632 let data = self.data_as_mut_slice();
633 unsafe { &mut *(data.as_mut_ptr().add(raw_idx) as *mut Option<PyStackRef>) }
634 }
635
636 #[inline(always)]
638 fn stack_last(&self) -> Option<Option<&PyStackRef>> {
639 if self.stack_top == 0 {
640 None
641 } else {
642 Some(self.stack_index(self.stack_top as usize - 1))
643 }
644 }
645
646 #[inline(always)]
648 fn stack_last_mut(&mut self) -> Option<&mut Option<PyStackRef>> {
649 if self.stack_top == 0 {
650 None
651 } else {
652 let idx = self.stack_top as usize - 1;
653 Some(self.stack_index_mut(idx))
654 }
655 }
656
657 #[inline(always)]
659 fn stack_swap(&mut self, a: usize, b: usize) {
660 let base = self.nlocalsplus as usize;
661 let data = self.data_as_mut_slice();
662 data.swap(base + a, base + b);
663 }
664
665 fn stack_truncate(&mut self, new_len: usize) {
667 debug_assert!(new_len <= self.stack_top as usize);
668 while self.stack_top as usize > new_len {
669 let _ = self.stack_pop();
670 }
671 }
672
673 fn stack_clear(&mut self) {
675 while self.stack_top > 0 {
676 let _ = self.stack_pop();
677 }
678 }
679
680 fn take_stack_object_refs(&mut self) -> Vec<PyObjectRef> {
683 let mut refs = Vec::with_capacity(self.stack_top as usize);
684 while self.stack_top > 0 {
685 if let Some(value) = self.stack_pop() {
686 refs.push(value.to_pyobj());
687 }
688 }
689 refs
690 }
691
692 fn clear_into(&mut self, out: &mut Vec<PyObjectRef>) {
694 while self.stack_top > 0 {
695 if let Some(value) = self.stack_pop() {
696 out.push(value.to_pyobj());
697 }
698 }
699 out.extend(self.fastlocals_mut().iter_mut().filter_map(Option::take));
700 }
701
702 fn stack_drain(
705 &mut self,
706 from: usize,
707 ) -> impl ExactSizeIterator<Item = Option<PyStackRef>> + '_ {
708 let end = self.stack_top as usize;
709 debug_assert!(from <= end);
710 self.stack_top = from as u32;
712 LocalsPlusStackDrain {
713 localsplus: self,
714 current: from,
715 end,
716 }
717 }
718
719 fn stack_extend(&mut self, iter: impl Iterator<Item = Option<PyStackRef>>) {
721 for val in iter {
722 self.stack_push(val);
723 }
724 }
725}
726
727struct LocalsPlusStackDrain<'a> {
729 localsplus: &'a mut LocalsPlus,
730 current: usize,
732 end: usize,
734}
735
736impl Iterator for LocalsPlusStackDrain<'_> {
737 type Item = Option<PyStackRef>;
738
739 fn next(&mut self) -> Option<Self::Item> {
740 if self.current >= self.end {
741 return None;
742 }
743 let idx = self.localsplus.nlocalsplus as usize + self.current;
744 let data = self.localsplus.data_as_mut_slice();
745 let raw = core::mem::replace(&mut data[idx], 0);
746 self.current += 1;
747 Some(unsafe { core::mem::transmute::<usize, Option<PyStackRef>>(raw) })
748 }
749
750 fn size_hint(&self) -> (usize, Option<usize>) {
751 let remaining = self.end - self.current;
752 (remaining, Some(remaining))
753 }
754}
755
756impl ExactSizeIterator for LocalsPlusStackDrain<'_> {}
757
758impl Drop for LocalsPlusStackDrain<'_> {
759 fn drop(&mut self) {
760 while self.current < self.end {
761 let idx = self.localsplus.nlocalsplus as usize + self.current;
762 let data = self.localsplus.data_as_mut_slice();
763 let raw = core::mem::replace(&mut data[idx], 0);
764 let _ = unsafe { core::mem::transmute::<usize, Option<PyStackRef>>(raw) };
765 self.current += 1;
766 }
767 }
768}
769
770impl Drop for LocalsPlus {
771 fn drop(&mut self) {
772 self.drop_values();
778 }
779}
780
781unsafe impl Traverse for LocalsPlus {
782 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
783 self.fastlocals().traverse(tracer_fn);
784 self.stack_as_slice().traverse(tracer_fn);
785 }
786}
787
788pub struct FrameLocals {
791 inner: OnceCell<ArgMapping>,
792}
793
794impl FrameLocals {
795 pub(crate) fn with_locals(locals: ArgMapping) -> Self {
797 let cell = OnceCell::new();
798 let _ = cell.set(locals);
799 Self { inner: cell }
800 }
801
802 pub(crate) fn lazy() -> Self {
805 Self {
806 inner: OnceCell::new(),
807 }
808 }
809
810 #[inline]
812 pub fn get_or_create(&self, vm: &VirtualMachine) -> &ArgMapping {
813 self.inner
814 .get_or_init(|| ArgMapping::from_dict_exact(vm.ctx.new_dict()))
815 }
816
817 #[inline]
819 pub fn get(&self) -> Option<&ArgMapping> {
820 self.inner.get()
821 }
822
823 #[inline]
824 pub fn mapping(&self, vm: &VirtualMachine) -> crate::protocol::PyMapping<'_> {
825 self.get_or_create(vm).mapping()
826 }
827
828 #[inline]
829 pub fn clone_mapping(&self, vm: &VirtualMachine) -> ArgMapping {
830 self.get_or_create(vm).clone()
831 }
832
833 pub fn into_object(&self, vm: &VirtualMachine) -> PyObjectRef {
834 self.clone_mapping(vm).into()
835 }
836
837 pub fn as_object(&self, vm: &VirtualMachine) -> &PyObject {
838 self.get_or_create(vm).obj()
839 }
840
841 fn take(&mut self) -> Option<ArgMapping> {
842 self.inner.take()
843 }
844}
845
846impl fmt::Debug for FrameLocals {
847 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
848 f.debug_struct("FrameLocals")
849 .field("initialized", &self.inner.get().is_some())
850 .finish()
851 }
852}
853
854impl Clone for FrameLocals {
855 fn clone(&self) -> Self {
856 let cell = OnceCell::new();
857 if let Some(locals) = self.inner.get() {
858 let _ = cell.set(locals.clone());
859 }
860 Self { inner: cell }
861 }
862}
863
864unsafe impl Traverse for FrameLocals {
865 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
866 if let Some(locals) = self.inner.get() {
867 locals.traverse(tracer_fn);
868 }
869 }
870}
871
872pub(crate) struct FrameColdData {
876 pub trace: PyMutex<Option<PyObjectRef>>,
877 pub trace_opcodes: PyMutex<bool>,
878 pub temporary_refs: PyMutex<Vec<PyObjectRef>>,
879 pub f_extra_locals: PyMutex<Option<PyDictRef>>,
880 pub f_locals_cache: PyMutex<Option<PyDictRef>>,
883 pub f_overwritten_fast_locals: PyMutex<Vec<PyObjectRef>>,
884 pub retained_back: PyMutex<Option<FrameObjectRef>>,
885 pub pending_stack_pops: PyAtomic<u32>,
886 pub pending_unwind_from_stack: PyAtomic<i64>,
887 pub attached_tid: atomic::AtomicU64,
893}
894
895impl Default for FrameColdData {
896 fn default() -> Self {
897 Self {
898 trace: PyMutex::new(None),
899 trace_opcodes: PyMutex::new(false),
900 temporary_refs: PyMutex::new(Vec::new()),
901 f_extra_locals: PyMutex::new(None),
902 f_locals_cache: PyMutex::new(None),
903 f_overwritten_fast_locals: PyMutex::new(Vec::new()),
904 retained_back: PyMutex::new(None),
905 pending_stack_pops: Default::default(),
906 pending_unwind_from_stack: Default::default(),
907 attached_tid: atomic::AtomicU64::new(0),
908 }
909 }
910}
911
912#[repr(C)]
921pub struct InterpreterFrame {
922 pub(crate) code: *const Py<PyCode>,
924 pub(crate) func_obj: *const PyObject, pub(crate) globals: *const Py<PyDict>,
926 pub(crate) builtins: *const PyObject,
927
928 pub(crate) localsplus: LocalsPlus,
930 pub locals: FrameLocals,
931
932 pub lasti: PyAtomic<u32>,
934
935 pub(crate) prev_line: core::cell::Cell<u32>,
937
938 pub generator: PyAtomicBorrow,
942 pub(crate) previous: PyAtomic<usize>,
945 pub(crate) owner: atomic::AtomicI8,
949 pub(crate) datastack_base: *mut u8,
952 pub(crate) materialized: PyAtomic<usize>,
956
957 pub(crate) cold: OnceCell<Box<FrameColdData>>,
960}
961
962#[cfg(feature = "threading")]
967unsafe impl Send for InterpreterFrame {}
968#[cfg(feature = "threading")]
969unsafe impl Sync for InterpreterFrame {}
970
971impl InterpreterFrame {
972 #[allow(clippy::too_many_arguments)]
982 #[inline(always)]
983 pub(crate) unsafe fn new(
984 code: &Py<PyCode>,
985 globals: &Py<PyDict>,
986 builtins: &PyObject,
987 func_obj: Option<&PyObject>,
988 localsplus: LocalsPlus,
989 locals: FrameLocals,
990 closure: &[PyCellRef],
991 owner: FrameOwner,
992 ) -> Self {
993 let mut localsplus = localsplus;
994 let nlocalsplus = code.localspluskinds.len();
995
996 {
1000 let nfrees = code.freevars.len();
1001 if nfrees > 0 {
1002 let freevar_start = nlocalsplus - nfrees;
1003 let fastlocals = localsplus.fastlocals_mut();
1004 for (i, cell) in closure.iter().enumerate() {
1005 fastlocals[freevar_start + i] = Some(cell.clone().into());
1006 }
1007 }
1008 }
1009
1010 let prev_line = if code.flags.intersects(
1014 bytecode::CodeFlags::GENERATOR
1015 | bytecode::CodeFlags::COROUTINE
1016 | bytecode::CodeFlags::ASYNC_GENERATOR,
1017 ) {
1018 code.first_line_number.map_or(0, |line| line.get() as u32)
1019 } else {
1020 0
1021 };
1022
1023 Self {
1024 code: code as *const Py<PyCode>,
1025 func_obj: match func_obj {
1026 Some(obj) => obj as *const PyObject,
1027 None => core::ptr::null(),
1028 },
1029 globals: globals as *const Py<PyDict>,
1030 builtins: builtins as *const PyObject,
1031 localsplus,
1032 locals,
1033 lasti: Radium::new(0),
1034 prev_line: core::cell::Cell::new(prev_line),
1035 generator: PyAtomicBorrow::new(),
1036 previous: Radium::new(0),
1037 owner: atomic::AtomicI8::new(owner as i8),
1038 datastack_base: core::ptr::null_mut(),
1039 materialized: Radium::new(0),
1040 cold: OnceCell::new(),
1041 }
1042 }
1043
1044 #[allow(clippy::too_many_arguments)]
1059 #[inline(always)]
1060 pub(crate) unsafe fn new_on_datastack<'a>(
1061 code: &Py<PyCode>,
1062 globals: &Py<PyDict>,
1063 builtins: &PyObject,
1064 func_obj: Option<&PyObject>,
1065 locals: FrameLocals,
1066 closure: &[PyCellRef],
1067 vm: &VirtualMachine,
1068 ) -> &'a mut Self {
1069 let nlocalsplus = code.localspluskinds.len();
1070 let stacksize = code.max_stackdepth as usize;
1071 let capacity = nlocalsplus
1072 .checked_add(stacksize)
1073 .expect("LocalsPlus capacity overflow");
1074
1075 let total_bytes = datastack_iframe_total_bytes(nlocalsplus, stacksize);
1076 let (base, reused_cleared_frame) = vm.datastack_push_frame(total_bytes);
1077
1078 let iframe_ptr = base as *mut Self;
1080 let localsplus_data_ptr =
1082 unsafe { base.add(datastack_iframe_localsplus_offset()) } as *mut usize;
1083
1084 if !reused_cleared_frame {
1085 unsafe { core::ptr::write_bytes(localsplus_data_ptr, 0, capacity) };
1087 }
1088
1089 let nlocalsplus_u32 = u32::try_from(nlocalsplus).expect("nlocalsplus exceeds u32");
1090 let localsplus = LocalsPlus {
1091 data: LocalsPlusData::DataStack {
1092 ptr: localsplus_data_ptr,
1093 capacity,
1094 },
1095 nlocalsplus: nlocalsplus_u32,
1096 stack_top: 0,
1097 };
1098
1099 let mut iframe = unsafe {
1100 Self::new(
1103 code,
1104 globals,
1105 builtins,
1106 func_obj,
1107 localsplus,
1108 locals,
1109 closure,
1110 FrameOwner::Thread,
1111 )
1112 };
1113 iframe.datastack_base = base;
1114
1115 unsafe {
1117 core::ptr::write(iframe_ptr, iframe);
1118 &mut *iframe_ptr
1119 }
1120 }
1121
1122 pub(crate) unsafe fn release_datastack_frame(&mut self) -> Option<(*mut u8, usize)> {
1134 let base = self.datastack_base;
1135 if base.is_null() {
1136 return None;
1137 }
1138 let total_bytes = datastack_iframe_total_bytes(
1139 self.localsplus.nlocalsplus as usize,
1140 self.localsplus.stack_capacity(),
1141 );
1142 self.datastack_base = core::ptr::null_mut();
1143 self.localsplus.drop_values();
1145 self.localsplus.data = LocalsPlusData::Heap(Box::default());
1147 self.localsplus.nlocalsplus = 0;
1148 unsafe { core::ptr::drop_in_place(self) };
1154 Some((base, total_bytes))
1155 }
1156
1157 #[inline(always)]
1159 pub fn get_lasti(&self) -> u32 {
1160 self.lasti.load(Relaxed)
1161 }
1162
1163 #[inline(always)]
1165 pub fn previous(&self) -> *const Self {
1166 self.previous.load(Relaxed) as *const Self
1167 }
1168
1169 #[inline(always)]
1171 pub(crate) fn frame_obj(&self) -> Option<&Py<FrameObject>> {
1172 let ptr = self.materialized.load(Relaxed);
1173 if ptr == 0 {
1174 None
1175 } else {
1176 Some(unsafe { &*(ptr as *const Py<FrameObject>) })
1177 }
1178 }
1179
1180 #[cold]
1184 #[inline(never)]
1185 pub(crate) fn materialize(&self, vm: &VirtualMachine) -> &Py<FrameObject> {
1186 if let Some(fo) = self.frame_obj() {
1187 return fo;
1188 }
1189 self.materialize_slow(vm)
1190 }
1191
1192 #[cfg(feature = "threading")]
1204 #[cold]
1205 #[inline(never)]
1206 pub(crate) unsafe fn materialize_detached(&self, vm: &VirtualMachine) -> FrameObjectRef {
1207 let fo = self.materialize_slow_chain(vm);
1210 unsafe {
1211 fo.iframe_mut()
1212 .localsplus
1213 .sync_fastlocals_from(&self.localsplus)
1214 };
1215 fo
1216 }
1217
1218 #[cfg(feature = "threading")]
1227 #[cold]
1228 #[inline(never)]
1229 pub(crate) unsafe fn materialize_detached_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
1230 let top = unsafe { self.materialize_detached(vm) };
1231 let mut child = top.clone();
1232 let mut cur = self.previous();
1233 while !cur.is_null() {
1234 let caller = unsafe { &*cur };
1235 let caller_fo = unsafe { caller.materialize_detached(vm) };
1236 {
1237 let mut guard = child.iframe().cold().retained_back.lock();
1238 if guard.is_none() {
1239 *guard = Some(caller_fo.clone());
1240 }
1241 }
1242 child = caller_fo;
1243 cur = caller.previous();
1244 }
1245 top
1246 }
1247
1248 #[cold]
1255 #[inline(never)]
1256 pub(crate) fn materialize_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
1257 if let Some(fo) = self.frame_obj() {
1258 return fo.to_owned();
1259 }
1260 self.materialize_slow_chain(vm)
1261 }
1262
1263 #[cold]
1264 fn materialize_slow(&self, vm: &VirtualMachine) -> &Py<FrameObject> {
1265 let code: PyRef<PyCode> = self.code().to_owned();
1268 let globals: PyDictRef = self.globals().to_owned();
1269 let builtins: PyObjectRef = self.builtins().to_owned();
1270 let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
1271
1272 let nlocalsplus = code.localspluskinds.len() as u32;
1279 let localsplus = LocalsPlus {
1280 data: LocalsPlusData::Heap(vec![0usize; nlocalsplus as usize].into_boxed_slice()),
1281 nlocalsplus,
1282 stack_top: 0,
1283 };
1284
1285 let locals = match self.locals.get() {
1287 Some(mapping) => FrameLocals::with_locals(mapping.clone()),
1288 None => FrameLocals::lazy(),
1289 };
1290
1291 let inner_iframe = Self {
1294 code: core::ptr::null(),
1295 func_obj: core::ptr::null(),
1296 globals: core::ptr::null(),
1297 builtins: core::ptr::null(),
1298 localsplus,
1299 locals,
1300 lasti: Radium::new(self.lasti.load(Relaxed)),
1301 prev_line: core::cell::Cell::new(self.prev_line.get()),
1302 generator: PyAtomicBorrow::new(),
1303 previous: Radium::new(0),
1307 owner: atomic::AtomicI8::new(FrameOwner::FrameObject as i8),
1312 datastack_base: core::ptr::null_mut(),
1313 materialized: Radium::new(0),
1314 cold: OnceCell::from(Box::new(FrameColdData {
1315 attached_tid: atomic::AtomicU64::new(current_thread_ident()),
1316 ..FrameColdData::default()
1317 })),
1318 };
1319
1320 let frame_obj = FrameObject {
1321 f_trace_lines: core::sync::atomic::AtomicBool::new(self.trace_lines_flag()),
1322 owned_code: Some(code),
1323 owned_globals: Some(globals),
1324 owned_builtins: Some(builtins),
1325 owned_func_obj: func_obj,
1326 iframe: FrameUnsafeCell::new(Some(inner_iframe)),
1327 };
1328 let frame_ref = frame_obj.into_ref(&vm.ctx);
1329 FrameObject::init_iframe_ptrs(&frame_ref);
1330 unsafe {
1332 frame_ref
1333 .iframe_mut()
1334 .materialized
1335 .store(&*frame_ref as *const Py<FrameObject> as usize, Relaxed);
1336 }
1337
1338 let fo_ptr = &*frame_ref as *const Py<FrameObject> as usize;
1340 self.materialized.store(fo_ptr, Relaxed);
1341
1342 self.cold()
1348 .temporary_refs
1349 .lock()
1350 .push(frame_ref.clone().into());
1351
1352 unsafe { &*(fo_ptr as *const Py<FrameObject>) }
1355 }
1356
1357 #[cold]
1362 fn materialize_slow_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
1363 let code: PyRef<PyCode> = self.code().to_owned();
1364 let globals: PyDictRef = self.globals().to_owned();
1365 let builtins: PyObjectRef = self.builtins().to_owned();
1366 let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
1367
1368 let nlocalsplus = code.localspluskinds.len() as u32;
1370 let localsplus = LocalsPlus {
1371 data: LocalsPlusData::Heap(vec![0usize; nlocalsplus as usize].into_boxed_slice()),
1372 nlocalsplus,
1373 stack_top: 0,
1374 };
1375
1376 let locals = match self.locals.get() {
1377 Some(mapping) => FrameLocals::with_locals(mapping.clone()),
1378 None => FrameLocals::lazy(),
1379 };
1380
1381 let inner_iframe = Self {
1382 code: core::ptr::null(),
1383 func_obj: core::ptr::null(),
1384 globals: core::ptr::null(),
1385 builtins: core::ptr::null(),
1386 localsplus,
1387 locals,
1388 lasti: Radium::new(self.lasti.load(Relaxed)),
1389 prev_line: core::cell::Cell::new(self.prev_line.get()),
1390 generator: PyAtomicBorrow::new(),
1391 previous: Radium::new(0),
1392 owner: atomic::AtomicI8::new(FrameOwner::FrameObject as i8),
1393 datastack_base: core::ptr::null_mut(),
1394 materialized: Radium::new(0),
1395 cold: OnceCell::new(),
1396 };
1397
1398 let frame_obj = FrameObject {
1399 f_trace_lines: core::sync::atomic::AtomicBool::new(self.trace_lines_flag()),
1400 owned_code: Some(code),
1401 owned_globals: Some(globals),
1402 owned_builtins: Some(builtins),
1403 owned_func_obj: func_obj,
1404 iframe: FrameUnsafeCell::new(Some(inner_iframe)),
1405 };
1406 let frame_ref = frame_obj.into_ref(&vm.ctx);
1407 FrameObject::init_iframe_ptrs(&frame_ref);
1408
1409 frame_ref
1410 }
1411
1412 #[inline(always)]
1414 pub fn code(&self) -> &Py<PyCode> {
1415 unsafe { &*self.code }
1417 }
1418
1419 #[inline(always)]
1421 pub fn globals(&self) -> &Py<PyDict> {
1422 unsafe { &*self.globals }
1424 }
1425
1426 #[inline(always)]
1428 pub fn builtins(&self) -> &PyObject {
1429 unsafe { &*self.builtins }
1431 }
1432
1433 #[inline(always)]
1435 pub fn func_obj(&self) -> Option<&PyObject> {
1436 if self.func_obj.is_null() {
1437 None
1438 } else {
1439 Some(unsafe { &*self.func_obj })
1441 }
1442 }
1443
1444 pub(crate) fn sync_prev_line_from_lasti(&self) {
1456 let lasti = self.lasti.load(Relaxed);
1457 if lasti == 0 {
1458 return;
1461 }
1462 let idx = lasti as usize - 1;
1463 if matches!(
1472 self.code().instructions.read_op(idx),
1473 Instruction::Resume { .. } | Instruction::InstrumentedResume
1474 ) {
1475 return;
1476 }
1477 if let Some((loc, _)) = self.code().locations.get(idx) {
1478 self.prev_line.set(loc.line.get() as u32);
1479 }
1480 }
1481
1482 #[inline]
1484 pub(crate) fn cold(&self) -> &FrameColdData {
1485 self.cold.get_or_init(|| Box::new(FrameColdData::default()))
1486 }
1487
1488 #[inline]
1491 pub(crate) fn cold_opt(&self) -> Option<&FrameColdData> {
1492 self.cold.get().map(|b| &**b)
1493 }
1494
1495 pub(crate) fn trace_lines_flag(&self) -> bool {
1498 let mat = self.materialized.load(atomic::Ordering::Relaxed);
1499 if mat != 0 {
1500 return unsafe { &*(mat as *const Py<FrameObject>) }
1503 .f_trace_lines
1504 .load(core::sync::atomic::Ordering::Relaxed);
1505 }
1506 true
1507 }
1508
1509 #[inline]
1511 pub(crate) fn attached_tid(&self) -> u64 {
1512 self.cold_opt()
1513 .map_or(0, |c| c.attached_tid.load(atomic::Ordering::Acquire))
1514 }
1515
1516 #[inline]
1519 pub(crate) fn detach(&self) {
1520 if let Some(cold) = self.cold_opt() {
1521 cold.attached_tid.store(0, atomic::Ordering::Release);
1522 }
1523 }
1524}
1525
1526#[pyclass(module = false, name = "frame", traverse = "manual")]
1528pub struct FrameObject {
1529 #[pymember(name = "f_trace_lines", writable)]
1531 pub(crate) f_trace_lines: core::sync::atomic::AtomicBool,
1532 pub(crate) owned_code: Option<PyRef<PyCode>>,
1536 pub(crate) owned_globals: Option<PyDictRef>,
1537 pub(crate) owned_builtins: Option<PyObjectRef>,
1538 pub(crate) owned_func_obj: Option<PyObjectRef>,
1539
1540 pub(crate) iframe: FrameUnsafeCell<Option<InterpreterFrame>>,
1544}
1545
1546impl FrameObject {
1547 #[inline(always)]
1554 pub(crate) unsafe fn iframe_ref(&self) -> &InterpreterFrame {
1555 let opt = unsafe { &*self.iframe.get() };
1556 #[cfg(debug_assertions)]
1557 if opt.is_none() {
1558 cleared_frame_access();
1559 }
1560 unsafe { opt.as_ref().unwrap_unchecked() }
1562 }
1563
1564 #[inline(always)]
1570 #[allow(clippy::mut_from_ref)]
1571 pub(crate) unsafe fn iframe_mut(&self) -> &mut InterpreterFrame {
1572 let opt = unsafe { &mut *self.iframe.get() };
1573 #[cfg(debug_assertions)]
1574 if opt.is_none() {
1575 cleared_frame_access();
1576 }
1577 unsafe { opt.as_mut().unwrap_unchecked() }
1579 }
1580
1581 #[inline(always)]
1585 pub fn iframe(&self) -> &InterpreterFrame {
1586 unsafe { self.iframe_ref() }
1591 }
1592}
1593
1594#[cfg(debug_assertions)]
1597#[cold]
1598#[inline(never)]
1599fn cleared_frame_access() -> ! {
1600 panic!("frame accessed after clear");
1601}
1602
1603thread_local! {
1607 static FRAME_FREELIST: core::cell::Cell<crate::object::FreeList<FrameObject>> =
1612 const { core::cell::Cell::new(crate::object::FreeList::new()) };
1613}
1614
1615impl PyPayload for FrameObject {
1616 const MAX_FREELIST: usize = 200;
1617 const HAS_FREELIST: bool = true;
1618 const NEW_REF_UNTRACKED: bool = true;
1622
1623 #[inline]
1624 fn class(ctx: &Context) -> &'static Py<PyType> {
1625 ctx.types.frame_type
1626 }
1627
1628 #[inline]
1629 unsafe fn freelist_push(obj: *mut PyObject) -> bool {
1630 FRAME_FREELIST
1631 .try_with(|fl| {
1632 let mut list = fl.take();
1633 let stored = if list.len() < Self::MAX_FREELIST {
1634 list.push(obj);
1635 true
1636 } else {
1637 false
1638 };
1639 fl.set(list);
1640 stored
1641 })
1642 .unwrap_or(false)
1643 }
1644
1645 #[inline]
1646 unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
1647 FRAME_FREELIST
1648 .try_with(|fl| {
1649 let mut list = fl.take();
1650 let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
1651 fl.set(list);
1652 result
1653 })
1654 .ok()
1655 .flatten()
1656 }
1657}
1658
1659unsafe impl Traverse for FrameObject {
1660 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
1661 self.owned_code.traverse(tracer_fn);
1664 self.owned_func_obj.traverse(tracer_fn);
1665 self.owned_globals.traverse(tracer_fn);
1666 self.owned_builtins.traverse(tracer_fn);
1667
1668 let Some(iframe) = (unsafe { &*self.iframe.get() }) else {
1670 return;
1671 };
1672 iframe.localsplus.traverse(tracer_fn);
1673 iframe.locals.traverse(tracer_fn);
1674 if let Some(cold) = iframe.cold_opt() {
1675 cold.trace.traverse(tracer_fn);
1676 cold.temporary_refs.traverse(tracer_fn);
1677 cold.f_extra_locals.traverse(tracer_fn);
1678 cold.f_locals_cache.traverse(tracer_fn);
1679 cold.f_overwritten_fast_locals.traverse(tracer_fn);
1680 cold.retained_back.traverse(tracer_fn);
1681 }
1682 }
1683
1684 fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
1685 let slot = self.iframe.get_mut();
1692 if let Some(iframe) = slot.as_mut() {
1693 iframe.localsplus.clear_into(out);
1694 if let Some(locals) = iframe.locals.take() {
1695 out.push(locals.into());
1696 }
1697 if let Some(cold) = iframe.cold.take() {
1698 let cold = *cold;
1699 if let Some(trace) = cold.trace.into_inner() {
1700 out.push(trace);
1701 }
1702 out.extend(cold.temporary_refs.into_inner());
1703 if let Some(extra) = cold.f_extra_locals.into_inner() {
1704 out.push(extra.into());
1705 }
1706 if let Some(cache) = cold.f_locals_cache.into_inner() {
1707 out.push(cache.into());
1708 }
1709 out.extend(cold.f_overwritten_fast_locals.into_inner());
1710 if let Some(back) = cold.retained_back.into_inner() {
1711 out.push(back.into());
1712 }
1713 }
1714 }
1715 *slot = None;
1716 if let Some(code) = self.owned_code.take() {
1717 out.push(code.into());
1718 }
1719 if let Some(globals) = self.owned_globals.take() {
1720 out.push(globals.into());
1721 }
1722 if let Some(builtins) = self.owned_builtins.take() {
1723 out.push(builtins);
1724 }
1725 if let Some(func) = self.owned_func_obj.take() {
1726 out.push(func);
1727 }
1728 }
1729}
1730
1731pub enum ExecutionResult {
1733 Return(PyObjectRef),
1734 Yield(PyObjectRef),
1735 TailCall,
1739 GenResume,
1744}
1745
1746#[derive(Clone, Copy)]
1772pub(crate) struct GenCont {
1773 pub(crate) exit: u32,
1774 pub(crate) resumed_at: u32,
1779}
1780
1781impl GenCont {
1782 pub(crate) const NONE: Self = Self {
1784 exit: 0,
1785 resumed_at: 0,
1786 };
1787
1788 #[inline]
1790 pub(crate) const fn is_some(self) -> bool {
1791 self.resumed_at != 0
1792 }
1793}
1794
1795type FrameResult = PyResult<Option<ExecutionResult>>;
1797
1798impl FrameObject {
1799 pub(crate) fn new(
1800 code: PyRef<PyCode>,
1801 scope: Scope,
1802 builtins: PyObjectRef,
1803 closure: &[PyCellRef],
1804 func_obj: Option<PyObjectRef>,
1805 use_datastack: bool,
1806 vm: &VirtualMachine,
1807 ) -> Self {
1808 let nlocalsplus = code.localspluskinds.len();
1809 let max_stackdepth = code.max_stackdepth as usize;
1810 let localsplus = if use_datastack {
1811 LocalsPlus::new_on_datastack(nlocalsplus, max_stackdepth, vm)
1812 } else {
1813 LocalsPlus::new(nlocalsplus, max_stackdepth)
1814 };
1815
1816 let locals = match scope.locals {
1817 Some(locals) => FrameLocals::with_locals(locals),
1818 None if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) => FrameLocals::lazy(),
1819 None => FrameLocals::with_locals(ArgMapping::from_dict_exact(scope.globals.clone())),
1820 };
1821
1822 let iframe = unsafe {
1826 InterpreterFrame::new(
1829 &code,
1830 &scope.globals,
1831 &builtins,
1832 func_obj.as_deref(),
1833 localsplus,
1834 locals,
1835 closure,
1836 FrameOwner::FrameObject,
1837 )
1838 };
1839 Self {
1840 f_trace_lines: core::sync::atomic::AtomicBool::new(true),
1841 owned_code: Some(code),
1842 owned_globals: Some(scope.globals),
1843 owned_builtins: Some(builtins),
1844 owned_func_obj: func_obj,
1845 iframe: FrameUnsafeCell::new(Some(iframe)),
1846 }
1847 }
1848
1849 fn init_iframe_ptrs(self_: &Py<Self>) {
1853 let iframe = unsafe { self_.iframe_mut() };
1854 iframe.code = &**self_.owned_code.as_ref().unwrap() as *const Py<PyCode>;
1855 iframe.globals = &**self_.owned_globals.as_ref().unwrap() as *const Py<PyDict>;
1856 iframe.builtins = &**self_.owned_builtins.as_ref().unwrap() as *const PyObject;
1857 iframe.func_obj = match &self_.owned_func_obj {
1858 Some(obj) => &**obj as *const PyObject,
1859 None => core::ptr::null(),
1860 };
1861 iframe
1863 .materialized
1864 .store(self_ as *const Py<Self> as usize, Relaxed);
1865 }
1866
1867 pub(crate) fn new_ref(
1870 code: PyRef<PyCode>,
1871 scope: Scope,
1872 builtins: PyObjectRef,
1873 closure: &[PyCellRef],
1874 func_obj: Option<PyObjectRef>,
1875 use_datastack: bool,
1876 vm: &VirtualMachine,
1877 ) -> FrameObjectRef {
1878 let frame = Self::new(code, scope, builtins, closure, func_obj, use_datastack, vm)
1879 .into_ref(&vm.ctx);
1880 Self::init_iframe_ptrs(&frame);
1881 frame
1882 }
1883
1884 #[inline(always)]
1890 pub unsafe fn fastlocals(&self) -> &[Option<PyObjectRef>] {
1891 unsafe { self.iframe_ref().localsplus.fastlocals() }
1892 }
1893
1894 #[inline(always)]
1899 #[allow(clippy::mut_from_ref)]
1900 pub unsafe fn fastlocals_mut(&self) -> &mut [Option<PyObjectRef>] {
1901 unsafe { self.iframe_mut().localsplus.fastlocals_mut() }
1902 }
1903
1904 pub(crate) unsafe fn materialize_localsplus(&self) -> Option<*mut u8> {
1912 unsafe { self.iframe_mut().localsplus.materialize_to_heap() }
1913 }
1914
1915 pub(crate) unsafe fn release_localsplus(&self) -> Option<*mut u8> {
1925 unsafe { self.iframe_mut().localsplus.release_datastack() }
1926 }
1927
1928 pub(crate) fn localsplus_is_datastack_backed(&self) -> bool {
1933 unsafe { self.iframe_ref().localsplus.is_datastack_backed() }
1935 }
1936
1937 pub(crate) fn clear_stack_and_cells(&self) {
1940 let localsplus = unsafe { &mut self.iframe_mut().localsplus };
1943 if localsplus.stack_is_empty() {
1946 return;
1947 }
1948 let refs = localsplus.take_stack_object_refs();
1949 drop(refs);
1950 }
1951
1952 pub(crate) fn clear_locals_and_stack(&self) {
1955 self.clear_stack_and_cells();
1956 let fastlocals = {
1960 let slots = unsafe { self.iframe_mut().localsplus.fastlocals_mut() };
1962 slots
1963 .iter_mut()
1964 .filter_map(Option::take)
1965 .collect::<Vec<_>>()
1966 };
1967 let (extra_locals, locals_cache, overwritten) = match self.iframe().cold_opt() {
1973 Some(cold) => {
1974 let extra_locals = {
1975 let mut guard = cold.f_extra_locals.lock();
1976 guard.take()
1977 };
1978 let locals_cache = {
1979 let mut guard = cold.f_locals_cache.lock();
1980 guard.take()
1981 };
1982 let overwritten = {
1983 let mut guard = cold.f_overwritten_fast_locals.lock();
1984 core::mem::take(&mut *guard)
1985 };
1986 (extra_locals, locals_cache, overwritten)
1987 }
1988 None => (None, None, Vec::new()),
1989 };
1990 drop((fastlocals, extra_locals, locals_cache, overwritten));
1991 }
1992
1993 pub fn set_generator(&self, generator: &PyObject) {
1996 self.iframe().generator.store(generator);
1997 self.iframe()
1998 .owner
1999 .store(FrameOwner::Generator as i8, atomic::Ordering::Release);
2000 }
2001
2002 pub fn clear_generator(&self) {
2004 let Some(iframe) = (unsafe { &*self.iframe.get() }) else {
2010 return;
2011 };
2012 iframe.generator.clear();
2013 iframe
2014 .owner
2015 .store(FrameOwner::FrameObject as i8, atomic::Ordering::Release);
2016 }
2017
2018 pub fn current_location(&self) -> SourceLocation {
2019 let lasti = self.lasti() as usize;
2020 if lasti == 0 {
2021 return SourceLocation {
2022 line: self
2023 .iframe()
2024 .code()
2025 .first_line_number
2026 .unwrap_or(OneIndexed::MIN),
2027 character_offset: OneIndexed::from_zero_indexed(0),
2028 };
2029 }
2030 self.iframe().code().locations[lasti - 1].0
2031 }
2032
2033 pub fn previous_iframe(&self) -> *const InterpreterFrame {
2036 let iframe_opt = unsafe { &*self.iframe.get() };
2038 match iframe_opt.as_ref() {
2039 Some(iframe) => {
2040 iframe.previous.load(atomic::Ordering::Relaxed) as *const InterpreterFrame
2041 }
2042 None => core::ptr::null(),
2043 }
2044 }
2045
2046 pub fn previous_frame(&self) -> *const Self {
2049 let mut cur = self.previous_iframe();
2050 while !cur.is_null() {
2051 let iframe = unsafe { &*cur };
2052 if let Some(fo) = iframe.frame_obj() {
2053 return &**fo as *const Self;
2054 }
2055 cur = iframe.previous.load(atomic::Ordering::Relaxed) as *const InterpreterFrame;
2056 }
2057 core::ptr::null()
2058 }
2059
2060 pub fn lasti(&self) -> u32 {
2061 self.iframe().lasti.load(Relaxed)
2062 }
2063
2064 pub fn set_lasti(&self, val: u32) {
2065 self.iframe().lasti.store(val, Relaxed);
2066 }
2067
2068 unsafe fn live_fastlocals(&self) -> &[Option<PyObjectRef>] {
2078 let live = self.find_live_source_iframe();
2079 if live.is_null() {
2080 unsafe { self.iframe_ref().localsplus.fastlocals() }
2081 } else {
2082 unsafe { (*live).localsplus.fastlocals() }
2083 }
2084 }
2085
2086 pub(crate) fn has_active_hidden_locals(&self) -> bool {
2089 use rustpython_compiler_core::bytecode::{CO_FAST_CELL, CO_FAST_FREE, CO_FAST_HIDDEN};
2090 let code = self.iframe().code();
2091 let fastlocals = unsafe { self.live_fastlocals() };
2094 code.localspluskinds.iter().enumerate().any(|(i, &kind)| {
2095 if kind & CO_FAST_HIDDEN == 0 {
2096 return false;
2097 }
2098 match fastlocals[i].as_ref() {
2099 None => false,
2100 Some(obj) => {
2101 if kind & (CO_FAST_CELL | CO_FAST_FREE) != 0 {
2102 obj.downcast_ref::<PyCell>()
2103 .is_none_or(|cell| cell.get().is_some())
2104 } else {
2105 true
2106 }
2107 }
2108 }
2109 })
2110 }
2111
2112 #[inline]
2113 pub(crate) fn has_hidden_local_slots(&self) -> bool {
2114 use rustpython_compiler_core::bytecode::CO_FAST_HIDDEN;
2115 self.iframe()
2116 .code()
2117 .localspluskinds
2118 .iter()
2119 .any(|kind| kind & CO_FAST_HIDDEN != 0)
2120 }
2121
2122 pub(crate) fn uses_locals_proxy(&self, vm: &VirtualMachine) -> PyResult<bool> {
2125 let is_optimized = self
2126 .iframe()
2127 .code()
2128 .flags
2129 .contains(bytecode::CodeFlags::OPTIMIZED);
2130 if !is_optimized && !self.has_hidden_local_slots() {
2131 return Ok(false);
2132 }
2133 self.check_locals_access(vm)?;
2134 Ok(is_optimized || self.has_active_hidden_locals())
2135 }
2136
2137 pub(crate) fn check_locals_access(&self, vm: &VirtualMachine) -> PyResult<()> {
2146 let attached = self.iframe().attached_tid();
2150 if attached != 0 && attached != current_thread_ident() {
2151 return Err(vm.new_runtime_error(
2152 "cannot access frame locals while the frame is executing in another thread",
2153 ));
2154 }
2155 let owner = FrameOwner::from_i8(self.iframe().owner.load(atomic::Ordering::Acquire));
2156 if owner != FrameOwner::Thread {
2157 return Ok(());
2158 }
2159 let self_iframe = self.iframe() as *const InterpreterFrame;
2160 let self_py_ptr = unsafe { Py::<Self>::from_payload_ptr(self) } as usize;
2163 let mut cur = crate::vm::thread::get_current_frame();
2164 while !cur.is_null() {
2165 if core::ptr::eq(cur, self_iframe) {
2166 return Ok(());
2167 }
2168 let materialized = unsafe { (*cur).materialized.load(Relaxed) };
2171 if materialized == self_py_ptr {
2172 return Ok(());
2173 }
2174 cur = unsafe { (*cur).previous.load(Relaxed) as *const InterpreterFrame };
2175 }
2176 Err(vm.new_runtime_error(
2177 "cannot access frame locals while the frame is executing in another thread",
2178 ))
2179 }
2180
2181 pub fn locals(&self, vm: &VirtualMachine) -> PyResult<ArgMapping> {
2182 if self.uses_locals_proxy(vm)? {
2183 Ok(ArgMapping::from_dict_exact(
2184 self.framelocalsproxy_snapshot(vm)?,
2185 ))
2186 } else {
2187 Ok(self.iframe().locals.clone_mapping(vm))
2188 }
2189 }
2190
2191 pub fn locals_snapshot_cache(&self, vm: &VirtualMachine) -> PyDictRef {
2194 let mut cache = self.iframe().cold().f_locals_cache.lock();
2195 cache.get_or_insert_with(|| vm.ctx.new_dict()).clone()
2196 }
2197
2198 fn framelocalsproxy_getval(&self, i: usize) -> Option<PyObjectRef> {
2201 use rustpython_compiler_core::bytecode::{CO_FAST_CELL, CO_FAST_FREE};
2202 let fastlocals = unsafe { self.live_fastlocals() };
2205 let obj = fastlocals.get(i)?.as_ref()?;
2206 let kind = self
2207 .iframe()
2208 .code()
2209 .localspluskinds
2210 .get(i)
2211 .copied()
2212 .unwrap_or(0);
2213 if kind & (CO_FAST_CELL | CO_FAST_FREE) != 0 {
2214 if let Some(cell) = obj.downcast_ref::<PyCell>() {
2215 cell.get()
2216 } else {
2217 Some(obj.clone())
2218 }
2219 } else {
2220 Some(obj.clone())
2221 }
2222 }
2223
2224 fn framelocalsproxy_setval(&self, i: usize, value: PyObjectRef) {
2227 use rustpython_compiler_core::bytecode::{CO_FAST_CELL, CO_FAST_FREE};
2228 let kind = self
2229 .iframe()
2230 .code()
2231 .localspluskinds
2232 .get(i)
2233 .copied()
2234 .unwrap_or(0);
2235 let live = self.find_live_source_iframe();
2236 let old_value = if !live.is_null() {
2237 unsafe { (*live).localsplus.fastlocals()[i].clone() }
2239 } else {
2240 unsafe { self.iframe_ref().localsplus.fastlocals()[i].clone() }
2242 };
2243
2244 if kind & (CO_FAST_CELL | CO_FAST_FREE) != 0
2245 && let Some(cell) = old_value
2246 .as_ref()
2247 .and_then(|obj| obj.clone().downcast::<PyCell>().ok())
2248 {
2249 cell.set(Some(value));
2250 return;
2251 }
2252
2253 if old_value.as_ref().is_some_and(|old| old.is(&value)) {
2254 return;
2255 }
2256
2257 let old_value = if !live.is_null() {
2261 unsafe { &mut *live.cast_mut() }.localsplus.fastlocals_mut()[i].replace(value)
2263 } else {
2264 unsafe { self.iframe_mut().localsplus.fastlocals_mut()[i].replace(value) }
2266 };
2267 if let Some(old_value) = old_value {
2268 self.iframe()
2269 .cold()
2270 .f_overwritten_fast_locals
2271 .lock()
2272 .push(old_value);
2273 }
2274 }
2275
2276 fn framelocalsproxy_getkeyindex(
2281 &self,
2282 key: &PyObject,
2283 read: bool,
2284 vm: &VirtualMachine,
2285 ) -> PyResult<Option<usize>> {
2286 use rustpython_compiler_core::bytecode::CO_FAST_HIDDEN;
2287 let key_hash = key.hash(vm)?;
2289 for (i, &kind) in self.iframe().code().localspluskinds.iter().enumerate() {
2290 let name = localsplus_name(self.iframe().code(), i);
2291 if name.as_object().hash(vm)? != key_hash {
2292 continue;
2293 }
2294 if !name
2295 .as_object()
2296 .rich_compare_bool(key, PyComparisonOp::Eq, vm)?
2297 {
2298 continue;
2299 }
2300 if read {
2301 if self.framelocalsproxy_getval(i).is_some() {
2302 return Ok(Some(i));
2303 }
2304 } else if kind & CO_FAST_HIDDEN == 0 {
2305 return Ok(Some(i));
2306 }
2307 }
2308 Ok(None)
2309 }
2310
2311 pub(crate) fn framelocalsproxy_items(
2314 &self,
2315 vm: &VirtualMachine,
2316 ) -> PyResult<Vec<(PyObjectRef, PyObjectRef)>> {
2317 self.check_locals_access(vm)?;
2318 let code = self.iframe().code();
2319 let mut items = Vec::with_capacity(code.localspluskinds.len());
2320 for i in 0..code.localspluskinds.len() {
2321 if let Some(value) = self.framelocalsproxy_getval(i) {
2322 let name = localsplus_name(code, i).to_owned().into();
2323 items.push((name, value));
2324 }
2325 }
2326 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2327 if let Some(extra) = extra {
2328 items.extend(&extra);
2329 }
2330 Ok(items)
2331 }
2332
2333 pub(crate) fn framelocalsproxy_snapshot(&self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
2337 self.check_locals_access(vm)?;
2338 let dict = vm.ctx.new_dict();
2339 let code = self.iframe().code();
2340 for i in 0..code.localspluskinds.len() {
2341 if let Some(value) = self.framelocalsproxy_getval(i) {
2342 let key: PyObjectRef = localsplus_name(code, i).to_owned().into();
2343 dict.set_item(&*key, value, vm)?;
2344 }
2345 }
2346 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2347 if let Some(extra) = extra {
2348 dict.merge_object_if_missing(extra.into(), vm)?;
2349 }
2350 Ok(dict)
2351 }
2352
2353 pub(crate) fn framelocalsproxy_getitem(
2355 &self,
2356 key: PyObjectRef,
2357 vm: &VirtualMachine,
2358 ) -> PyResult {
2359 self.check_locals_access(vm)?;
2360 if let Some(i) = self.framelocalsproxy_getkeyindex(&key, true, vm)?
2361 && let Some(value) = self.framelocalsproxy_getval(i)
2362 {
2363 return Ok(value);
2364 }
2365 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2366 if let Some(extra) = extra
2367 && let Some(value) = extra.get_item_opt(&*key, vm)?
2368 {
2369 return Ok(value);
2370 }
2371 Err(vm.new_key_error(key))
2372 }
2373
2374 pub(crate) fn framelocalsproxy_contains(
2376 &self,
2377 key: &PyObject,
2378 vm: &VirtualMachine,
2379 ) -> PyResult<bool> {
2380 self.check_locals_access(vm)?;
2381 if self.framelocalsproxy_getkeyindex(key, true, vm)?.is_some() {
2382 return Ok(true);
2383 }
2384 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2385 if let Some(extra) = extra {
2386 return Ok(extra.get_item_opt(key, vm)?.is_some());
2387 }
2388 Ok(false)
2389 }
2390
2391 pub(crate) fn framelocalsproxy_setitem(
2394 &self,
2395 key: &PyObject,
2396 value: PyObjectRef,
2397 vm: &VirtualMachine,
2398 ) -> PyResult<()> {
2399 self.check_locals_access(vm)?;
2400 if let Some(i) = self.framelocalsproxy_getkeyindex(key, false, vm)? {
2401 self.framelocalsproxy_setval(i, value);
2402 return Ok(());
2403 }
2404 let extra = self.extra_locals_get_or_create(vm);
2405 extra.set_item(key, value, vm)
2406 }
2407
2408 pub(crate) fn framelocalsproxy_delitem(
2411 &self,
2412 key: PyObjectRef,
2413 vm: &VirtualMachine,
2414 ) -> PyResult<()> {
2415 self.check_locals_access(vm)?;
2416 if self
2417 .framelocalsproxy_getkeyindex(&key, false, vm)?
2418 .is_some()
2419 {
2420 return Err(vm.new_value_error("cannot remove local variables from FrameLocalsProxy"));
2421 }
2422 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2423 if let Some(extra) = extra
2424 && extra.get_item_opt(&*key, vm)?.is_some()
2425 {
2426 return extra.del_item(&*key, vm);
2427 }
2428 Err(vm.new_key_error(key))
2429 }
2430
2431 pub(crate) fn framelocalsproxy_pop(
2433 &self,
2434 key: PyObjectRef,
2435 default: Option<PyObjectRef>,
2436 vm: &VirtualMachine,
2437 ) -> PyResult {
2438 self.check_locals_access(vm)?;
2439 if self
2440 .framelocalsproxy_getkeyindex(&key, false, vm)?
2441 .is_some()
2442 {
2443 return Err(vm.new_value_error("cannot remove local variables from FrameLocalsProxy"));
2444 }
2445 let extra = self.iframe().cold().f_extra_locals.lock().clone();
2446 if let Some(extra) = extra
2447 && let Some(value) = extra.pop_item(&*key, vm)?
2448 {
2449 return Ok(value);
2450 }
2451 default.ok_or_else(|| vm.new_key_error(key))
2452 }
2453
2454 pub(crate) fn framelocalsproxy_setdefault(
2456 &self,
2457 key: &PyObject,
2458 default: PyObjectRef,
2459 vm: &VirtualMachine,
2460 ) -> PyResult {
2461 match self.framelocalsproxy_getitem(key.to_owned(), vm) {
2462 Ok(value) => Ok(value),
2463 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
2464 self.framelocalsproxy_setitem(key, default.clone(), vm)?;
2465 Ok(default)
2466 }
2467 Err(e) => Err(e),
2468 }
2469 }
2470
2471 fn extra_locals_get_or_create(&self, vm: &VirtualMachine) -> PyDictRef {
2472 let mut extra = self.iframe().cold().f_extra_locals.lock();
2473 extra.get_or_insert_with(|| vm.ctx.new_dict()).clone()
2474 }
2475}
2476
2477impl Py<FrameObject> {
2478 #[inline(always)]
2479 fn with_exec<R>(&self, vm: &VirtualMachine, f: impl FnOnce(ExecutingFrame<'_>) -> R) -> R {
2480 let iframe = unsafe { self.iframe_mut() };
2484 let (code, globals, builtins, func_obj) = unsafe {
2488 (
2489 &*iframe.code,
2490 &*iframe.globals,
2491 &*iframe.builtins,
2492 if iframe.func_obj.is_null() {
2493 None
2494 } else {
2495 Some(&*iframe.func_obj)
2496 },
2497 )
2498 };
2499 let builtins_dict = if globals.class().is(vm.ctx.types.dict_type) {
2500 builtins
2501 .downcast_ref_if_exact::<PyDict>(vm)
2502 .map(|d| unsafe { PyExact::ref_unchecked(d) })
2504 } else {
2505 None
2506 };
2507 let iframe_ptr = iframe as *const InterpreterFrame;
2508 let exec = ExecutingFrame {
2509 code,
2510 localsplus: &mut iframe.localsplus,
2511 locals: &iframe.locals,
2512 globals,
2513 builtins,
2514 builtins_dict,
2515 lasti: &iframe.lasti,
2516 iframe: iframe_ptr,
2517 func_obj,
2518 prev_line: &iframe.prev_line,
2519 monitoring_mask: 0,
2520 flatten: Flatten::Nothing,
2521 call_traced: false,
2522 };
2523 f(exec)
2524 }
2525
2526 pub fn run(&self, vm: &VirtualMachine) -> PyResult<ExecutionResult> {
2528 self.with_exec(vm, |mut exec| exec.run(vm))
2529 }
2530
2531 pub(crate) fn resume(
2538 &self,
2539 value: Option<PyObjectRef>,
2540 vm: &VirtualMachine,
2541 ) -> PyResult<ExecutionResult> {
2542 let iframe = unsafe { self.iframe_mut() };
2545 if let Some(value) = value {
2546 if let Some((delegate, cont)) = yield_from_delegate(iframe, vm)
2550 && crate::coroutine::as_builtin_coro(&delegate).is_some_and(is_delegating)
2551 && gen_collapse_allowed(vm)
2552 {
2553 park_at_send(iframe, cont);
2554 return vm.run_gen_frame_delegating(iframe, delegate, value, cont);
2555 }
2556 iframe.localsplus.push_stack(value);
2557 }
2558 vm.run_gen_frame(iframe)
2559 }
2560
2561 pub(crate) fn gen_throw(
2562 &self,
2563 vm: &VirtualMachine,
2564 exc_type: PyObjectRef,
2565 exc_val: PyObjectRef,
2566 exc_tb: PyObjectRef,
2567 ) -> PyResult<ExecutionResult> {
2568 self.with_exec(vm, |mut exec| exec.gen_throw(vm, exc_type, exc_val, exc_tb))
2569 }
2570
2571 pub fn yield_from_target(&self) -> Option<PyObjectRef> {
2572 let owner = FrameOwner::from_i8(self.iframe().owner.load(atomic::Ordering::Acquire));
2575 if owner == FrameOwner::Thread {
2576 return None;
2577 }
2578 let iframe = unsafe { self.iframe_mut() };
2580 let (code, globals, builtins, func_obj) = unsafe {
2582 (
2583 &*iframe.code,
2584 &*iframe.globals,
2585 &*iframe.builtins,
2586 if iframe.func_obj.is_null() {
2587 None
2588 } else {
2589 Some(&*iframe.func_obj)
2590 },
2591 )
2592 };
2593 let iframe_ptr = iframe as *const InterpreterFrame;
2594 let exec = ExecutingFrame {
2595 code,
2596 localsplus: &mut iframe.localsplus,
2597 locals: &iframe.locals,
2598 globals,
2599 builtins,
2600 builtins_dict: None,
2601 lasti: &iframe.lasti,
2602 iframe: iframe_ptr,
2603 func_obj,
2604 prev_line: &iframe.prev_line,
2605 monitoring_mask: 0,
2606 flatten: Flatten::Nothing,
2607 call_traced: false,
2608 };
2609 exec.yield_from_target().map(PyObject::to_owned)
2610 }
2611
2612 pub fn is_internal_frame(&self) -> bool {
2613 let code = self.f_code();
2614 let filename = code.co_filename();
2615 let filename = filename.as_bytes();
2616 filename.find(b"importlib").is_some() && filename.find(b"_bootstrap").is_some()
2617 }
2618
2619 pub fn next_external_frame(&self, vm: &VirtualMachine) -> Option<FrameObjectRef> {
2620 let mut frame = self.f_back(vm);
2621 while let Some(ref f) = frame {
2622 if !f.is_internal_frame() {
2623 break;
2624 }
2625 frame = f.f_back(vm);
2626 }
2627 frame
2628 }
2629}
2630
2631#[inline]
2635fn current_thread_ident() -> u64 {
2636 #[cfg(feature = "threading")]
2637 {
2638 crate::stdlib::_thread::get_ident()
2639 }
2640 #[cfg(not(feature = "threading"))]
2641 {
2642 0
2643 }
2644}
2645
2646#[inline]
2649fn datastack_iframe_localsplus_offset() -> usize {
2650 let iframe_size = core::mem::size_of::<InterpreterFrame>();
2651 (iframe_size + core::mem::align_of::<usize>() - 1) & !(core::mem::align_of::<usize>() - 1)
2652}
2653
2654pub(crate) fn datastack_iframe_total_bytes(nlocalsplus: usize, stacksize: usize) -> usize {
2657 let iframe_padded = datastack_iframe_localsplus_offset();
2658 let capacity = nlocalsplus
2659 .checked_add(stacksize)
2660 .expect("LocalsPlus capacity overflow");
2661 let data_bytes = capacity
2662 .checked_mul(core::mem::size_of::<usize>())
2663 .expect("LocalsPlus byte size overflow");
2664 iframe_padded
2665 .checked_add(data_bytes)
2666 .expect("datastack iframe total size overflow")
2667}
2668
2669pub(crate) fn trampoline_handle_exception(
2678 iframe: &mut InterpreterFrame,
2679 exception: &Py<PyBaseException>,
2680 vm: &VirtualMachine,
2681) -> FrameResult {
2682 let mut exec = exec_iframe(iframe, Flatten::Nothing, vm);
2683
2684 let idx = (exec.lasti() as usize).saturating_sub(1);
2687
2688 if let Some((loc, _end_loc)) = exec.code.locations.get(idx) {
2690 let next = exception.traceback();
2691 let new_traceback = PyTraceback::new(next, exec.frame_object(vm), idx as i32 * 2, loc.line);
2692 exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
2693 }
2694
2695 exec.fire_exception_trace(exception, vm)?;
2696 let exception = {
2697 let mon_events = vm.state.monitoring_events.load();
2698 if mon_events & MonitoringEvent::Raise.mask() != 0 {
2699 let offset = idx as u32 * 2;
2700 let exc_obj: PyObjectRef = exception.to_owned().into();
2701 match monitoring::fire_raise(vm, exec.code, offset, &exc_obj) {
2702 Ok(()) => exception.to_owned(),
2703 Err(monitor_exc) => monitor_exc,
2704 }
2705 } else {
2706 exception.to_owned()
2707 }
2708 };
2709
2710 exec.unwind_blocks(
2711 vm,
2712 UnwindReason::Raising {
2713 exception,
2714 offset: idx as u32,
2715 },
2716 )
2717}
2718
2719#[inline(always)]
2721fn exec_iframe<'a>(
2722 iframe: &'a mut InterpreterFrame,
2723 flatten: Flatten,
2724 vm: &VirtualMachine,
2725) -> ExecutingFrame<'a> {
2726 let (code, globals, builtins, func_obj) = unsafe {
2730 (
2731 &*iframe.code,
2732 &*iframe.globals,
2733 &*iframe.builtins,
2734 if iframe.func_obj.is_null() {
2735 None
2736 } else {
2737 Some(&*iframe.func_obj)
2738 },
2739 )
2740 };
2741 let builtins_dict = if globals.class().is(vm.ctx.types.dict_type) {
2742 builtins
2743 .downcast_ref_if_exact::<PyDict>(vm)
2744 .map(|d| unsafe { PyExact::ref_unchecked(d) })
2745 } else {
2746 None
2747 };
2748 let iframe_ptr = iframe as *const InterpreterFrame;
2749 ExecutingFrame {
2750 code,
2751 localsplus: &mut iframe.localsplus,
2752 locals: &iframe.locals,
2753 globals,
2754 builtins,
2755 builtins_dict,
2756 lasti: &iframe.lasti,
2757 iframe: iframe_ptr,
2758 func_obj,
2759 prev_line: &iframe.prev_line,
2760 monitoring_mask: 0,
2761 flatten,
2762 call_traced: false,
2763 }
2764}
2765
2766#[inline(always)]
2768pub(crate) fn run_iframe(
2769 iframe: &mut InterpreterFrame,
2770 flatten: Flatten,
2771 vm: &VirtualMachine,
2772) -> PyResult<ExecutionResult> {
2773 exec_iframe(iframe, flatten, vm).run(vm)
2774}
2775
2776#[inline]
2782pub(crate) fn gen_collapse_allowed(vm: &VirtualMachine) -> bool {
2783 !vm.use_tracing.get() && vm.state.monitoring_events.load() == 0
2784}
2785
2786pub(crate) fn yield_from_delegate(
2795 iframe: &InterpreterFrame,
2796 vm: &VirtualMachine,
2797) -> Option<(PyObjectRef, GenCont)> {
2798 let code = iframe.code();
2799 let send_caches = Instruction::from(Opcode::Send).cache_entries();
2800 let resumed_at = iframe.lasti.load(Relaxed) as usize;
2801 let send_idx = resumed_at.checked_sub(2 + send_caches)?;
2803 if !matches!(
2804 code.instructions.get(resumed_at)?.op,
2805 Instruction::Resume { .. }
2806 ) {
2807 return None;
2808 }
2809 let yield_unit = code.instructions.get(resumed_at - 1)?;
2810 if !matches!(yield_unit.op, Instruction::YieldValue { .. }) || u8::from(yield_unit.arg) < 1 {
2811 return None;
2812 }
2813 let send_unit = code.instructions.get(send_idx)?;
2814 if !matches!(send_unit.op, Instruction::SendGen) {
2815 return None;
2816 }
2817 if send_idx > 0
2819 && matches!(
2820 code.instructions.get(send_idx - 1)?.op,
2821 Instruction::ExtendedArg
2822 )
2823 {
2824 return None;
2825 }
2826 let delegate = match iframe.localsplus.stack_last() {
2827 Some(Some(top)) => top.as_object(),
2828 _ => return None,
2829 };
2830 if delegate.downcast_ref_if_exact::<PyGenerator>(vm).is_none()
2832 && delegate.downcast_ref_if_exact::<PyCoroutine>(vm).is_none()
2833 {
2834 return None;
2835 }
2836 let coro = crate::coroutine::as_builtin_coro(delegate)?;
2837 if coro.running() || coro.closed() {
2838 return None;
2839 }
2840 let exit = (send_idx + 1 + send_caches) as u32 + u32::from(u8::from(send_unit.arg));
2843 Some((
2844 delegate.to_owned(),
2845 GenCont {
2846 exit,
2847 resumed_at: resumed_at as u32,
2848 },
2849 ))
2850}
2851
2852#[inline]
2857pub(crate) fn park_at_send(iframe: &mut InterpreterFrame, cont: GenCont) {
2858 iframe.lasti.store(cont.resumed_at - 1, Relaxed);
2859}
2860
2861pub(crate) fn is_delegating(coro: &Coro) -> bool {
2869 let iframe = coro.frame_ref().iframe();
2870 let code = iframe.code();
2871 let resumed_at = iframe.lasti.load(Relaxed) as usize;
2872 if resumed_at == 0 {
2873 return false;
2874 }
2875 matches!(
2876 code.instructions.get(resumed_at).map(|u| u.op),
2877 Some(Instruction::Resume { .. })
2878 ) && code
2879 .instructions
2880 .get(resumed_at - 1)
2881 .is_some_and(|u| matches!(u.op, Instruction::YieldValue { .. }) && u8::from(u.arg) >= 1)
2882}
2883
2884#[inline]
2887pub(crate) fn park_after_yield_from(iframe: &mut InterpreterFrame, resumed_at: u32) {
2888 iframe.localsplus.promote_stack();
2891 debug_assert!(
2892 iframe
2893 .localsplus
2894 .stack_as_slice()
2895 .iter()
2896 .flatten()
2897 .all(|sr| !sr.is_borrowed()),
2898 "borrowed refs on stack at yield point"
2899 );
2900 iframe.lasti.store(resumed_at, Relaxed);
2901}
2902
2903pub(crate) fn trampoline_gen_stop(
2907 iframe: &mut InterpreterFrame,
2908 value: Option<PyObjectRef>,
2909 cont: GenCont,
2910 vm: &VirtualMachine,
2911) -> PyResult<()> {
2912 if vm.use_tracing.get() {
2913 let exec = exec_iframe(iframe, Flatten::Nothing, vm);
2917 if exec.trace_is_set(vm) {
2918 let stop_exc = vm.new_stop_iteration(value.clone());
2919 exec.fire_exception_trace(&stop_exc, vm)?;
2920 }
2921 }
2922 iframe.localsplus.push_stack(vm.unwrap_or_none(value));
2923 iframe.lasti.store(cont.exit, Relaxed);
2924 Ok(())
2925}
2926
2927pub(crate) struct ExecutingFrame<'a> {
2930 code: &'a Py<PyCode>,
2931 localsplus: &'a mut LocalsPlus,
2932 locals: &'a FrameLocals,
2933 globals: &'a Py<PyDict>,
2934 builtins: &'a PyObject,
2935 builtins_dict: Option<&'a PyExact<PyDict>>,
2940 iframe: *const InterpreterFrame,
2946 func_obj: Option<&'a PyObject>,
2948 lasti: &'a PyAtomic<u32>,
2949 prev_line: &'a core::cell::Cell<u32>,
2950 monitoring_mask: u32,
2952 flatten: Flatten,
2955 call_traced: bool,
2957}
2958
2959#[derive(Clone, Copy, PartialEq, Eq)]
2961pub(crate) enum Flatten {
2962 Nothing,
2966 GenResume,
2971 CallAndGenResume,
2973}
2974
2975#[inline]
2976fn specialization_compact_int_value(i: &Py<PyInt>) -> Option<isize> {
2977 const CPYTHON_COMPACT_LONG_ABS_MAX: i64 = (1i64 << 30) - 1;
2980 let v = i.try_to_i64_fast()?;
2981 if (-CPYTHON_COMPACT_LONG_ABS_MAX..=CPYTHON_COMPACT_LONG_ABS_MAX).contains(&v) {
2982 Some(v as isize)
2983 } else {
2984 None
2985 }
2986}
2987
2988#[inline]
2989fn compact_int_from_obj(obj: &PyObject, vm: &VirtualMachine) -> Option<isize> {
2990 obj.downcast_ref_if_exact::<PyInt>(vm)
2991 .and_then(specialization_compact_int_value)
2992}
2993
2994#[inline]
2995fn exact_float_from_obj(obj: &PyObject, vm: &VirtualMachine) -> Option<f64> {
2996 obj.downcast_ref_if_exact::<PyFloat>(vm).map(|f| f.to_f64())
2997}
2998
2999#[inline]
3000fn specialization_nonnegative_compact_index(i: &Py<PyInt>, vm: &VirtualMachine) -> Option<usize> {
3001 const CPYTHON_COMPACT_LONG_MAX: u64 = (1u64 << 30) - 1;
3003 let v = i.try_to_primitive::<u64>(vm).ok()?;
3004 if v <= CPYTHON_COMPACT_LONG_MAX {
3005 Some(v as usize)
3006 } else {
3007 None
3008 }
3009}
3010
3011fn localsplus_name(code: &Py<PyCode>, idx: usize) -> &'static PyStrInterned {
3013 code.localsplus_name(idx)
3014}
3015
3016pub(crate) fn release_datastack_frame(frame: &Py<FrameObject>, vm: &VirtualMachine) {
3024 let frame_obj = frame.as_object();
3025 debug_assert!(
3037 !frame_obj.is_gc_tracked(),
3038 "datastack frame is GC-tracked at release"
3039 );
3040 if frame_obj.strong_count() == 1 {
3041 atomic::fence(Acquire);
3045 unsafe {
3049 if let Some(base) = frame.release_localsplus() {
3050 vm.datastack_pop(base);
3051 }
3052 }
3053 return;
3054 }
3055 unsafe {
3063 if let Some(base) = frame.materialize_localsplus() {
3064 vm.datastack_pop(base);
3065 }
3066 }
3067 {
3072 let mut guard = frame.iframe().cold().retained_back.lock();
3073 if guard.is_none() {
3074 let prev = frame.previous_iframe();
3075 *guard = unsafe { owned_chain_frame(prev) };
3076 }
3077 }
3078 debug_assert!(
3089 !frame.localsplus_is_datastack_backed(),
3090 "escaped frame tracked before its localsplus was materialized"
3091 );
3092 unsafe {
3095 crate::gc_state::gc_state()
3096 .track_object(NonNull::from(frame_obj), crate::gc_state::current_owner())
3097 };
3098}
3099
3100type BinaryOpExtendGuard = fn(&PyObject, &PyObject, &VirtualMachine) -> bool;
3101type BinaryOpExtendAction = fn(&PyObject, &PyObject, &VirtualMachine) -> Option<PyObjectRef>;
3102
3103struct BinaryOpExtendSpecializationDescr {
3104 oparg: bytecode::BinaryOperator,
3105 guard: BinaryOpExtendGuard,
3106 action: BinaryOpExtendAction,
3107}
3108
3109const BINARY_OP_EXTEND_EXTERNAL_CACHE_OFFSET: usize = 1;
3110
3111const MAX_INLINE_CALL_ARGS: usize = 8;
3114
3115enum CallArgBuffer {
3118 Inline(usize, [Option<PyObjectRef>; MAX_INLINE_CALL_ARGS]),
3119 Heap(Vec<Option<PyObjectRef>>),
3120}
3121
3122impl CallArgBuffer {
3123 fn new(total_nargs: usize) -> Self {
3124 if total_nargs <= MAX_INLINE_CALL_ARGS {
3125 Self::Inline(total_nargs, [const { None }; MAX_INLINE_CALL_ARGS])
3126 } else {
3127 Self::Heap(vec![None; total_nargs])
3128 }
3129 }
3130
3131 fn slots(&mut self) -> &mut [Option<PyObjectRef>] {
3132 match self {
3133 Self::Inline(len, buf) => &mut buf[..*len],
3134 Self::Heap(buf) => buf,
3135 }
3136 }
3137}
3138
3139#[inline]
3140fn compactlongs_guard(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> bool {
3141 compact_int_from_obj(lhs, vm).is_some() && compact_int_from_obj(rhs, vm).is_some()
3142}
3143
3144#[derive(Clone, Copy)]
3149struct FusedBoolJump {
3150 jump_on: bool,
3152 taken: u32,
3154 fallthrough: u32,
3157}
3158
3159macro_rules! bitwise_longs_action {
3160 ($name:ident, $op:tt) => {
3161 #[inline]
3162 fn $name(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> Option<PyObjectRef> {
3163 let lhs_val = compact_int_from_obj(lhs, vm)?;
3164 let rhs_val = compact_int_from_obj(rhs, vm)?;
3165 Some(vm.ctx.new_int(lhs_val $op rhs_val).into())
3166 }
3167 };
3168}
3169bitwise_longs_action!(compactlongs_or, |);
3170bitwise_longs_action!(compactlongs_and, &);
3171bitwise_longs_action!(compactlongs_xor, ^);
3172
3173#[inline]
3174fn float_compactlong_guard(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> bool {
3175 exact_float_from_obj(lhs, vm).is_some_and(|f| !f.is_nan())
3176 && compact_int_from_obj(rhs, vm).is_some()
3177}
3178
3179#[inline]
3180fn nonzero_float_compactlong_guard(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> bool {
3181 float_compactlong_guard(lhs, rhs, vm) && compact_int_from_obj(rhs, vm).is_some_and(|v| v != 0)
3182}
3183
3184macro_rules! float_long_action {
3185 ($name:ident, $op:tt) => {
3186 #[inline]
3187 fn $name(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> Option<PyObjectRef> {
3188 let lhs_val = exact_float_from_obj(lhs, vm)?;
3189 let rhs_val = compact_int_from_obj(rhs, vm)?;
3190 Some(vm.ctx.new_float(lhs_val $op rhs_val as f64).into())
3191 }
3192 };
3193}
3194float_long_action!(float_compactlong_add, +);
3195float_long_action!(float_compactlong_subtract, -);
3196float_long_action!(float_compactlong_multiply, *);
3197float_long_action!(float_compactlong_true_div, /);
3198
3199#[inline]
3200fn compactlong_float_guard(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> bool {
3201 compact_int_from_obj(lhs, vm).is_some()
3202 && exact_float_from_obj(rhs, vm).is_some_and(|f| !f.is_nan())
3203}
3204
3205#[inline]
3206fn nonzero_compactlong_float_guard(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> bool {
3207 compactlong_float_guard(lhs, rhs, vm) && exact_float_from_obj(rhs, vm).is_some_and(|f| f != 0.0)
3208}
3209
3210macro_rules! long_float_action {
3211 ($name:ident, $op:tt) => {
3212 #[inline]
3213 fn $name(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> Option<PyObjectRef> {
3214 let lhs_val = compact_int_from_obj(lhs, vm)?;
3215 let rhs_val = exact_float_from_obj(rhs, vm)?;
3216 Some(vm.ctx.new_float(lhs_val as f64 $op rhs_val).into())
3217 }
3218 };
3219}
3220long_float_action!(compactlong_float_add, +);
3221long_float_action!(compactlong_float_subtract, -);
3222long_float_action!(compactlong_float_multiply, *);
3223long_float_action!(compactlong_float_true_div, /);
3224
3225static BINARY_OP_EXTEND_DESCRIPTORS: &[BinaryOpExtendSpecializationDescr] = &[
3226 BinaryOpExtendSpecializationDescr {
3228 oparg: bytecode::BinaryOperator::Or,
3229 guard: compactlongs_guard,
3230 action: compactlongs_or,
3231 },
3232 BinaryOpExtendSpecializationDescr {
3233 oparg: bytecode::BinaryOperator::And,
3234 guard: compactlongs_guard,
3235 action: compactlongs_and,
3236 },
3237 BinaryOpExtendSpecializationDescr {
3238 oparg: bytecode::BinaryOperator::Xor,
3239 guard: compactlongs_guard,
3240 action: compactlongs_xor,
3241 },
3242 BinaryOpExtendSpecializationDescr {
3243 oparg: bytecode::BinaryOperator::InplaceOr,
3244 guard: compactlongs_guard,
3245 action: compactlongs_or,
3246 },
3247 BinaryOpExtendSpecializationDescr {
3248 oparg: bytecode::BinaryOperator::InplaceAnd,
3249 guard: compactlongs_guard,
3250 action: compactlongs_and,
3251 },
3252 BinaryOpExtendSpecializationDescr {
3253 oparg: bytecode::BinaryOperator::InplaceXor,
3254 guard: compactlongs_guard,
3255 action: compactlongs_xor,
3256 },
3257 BinaryOpExtendSpecializationDescr {
3259 oparg: bytecode::BinaryOperator::Add,
3260 guard: float_compactlong_guard,
3261 action: float_compactlong_add,
3262 },
3263 BinaryOpExtendSpecializationDescr {
3264 oparg: bytecode::BinaryOperator::Subtract,
3265 guard: float_compactlong_guard,
3266 action: float_compactlong_subtract,
3267 },
3268 BinaryOpExtendSpecializationDescr {
3269 oparg: bytecode::BinaryOperator::TrueDivide,
3270 guard: nonzero_float_compactlong_guard,
3271 action: float_compactlong_true_div,
3272 },
3273 BinaryOpExtendSpecializationDescr {
3274 oparg: bytecode::BinaryOperator::Multiply,
3275 guard: float_compactlong_guard,
3276 action: float_compactlong_multiply,
3277 },
3278 BinaryOpExtendSpecializationDescr {
3280 oparg: bytecode::BinaryOperator::Add,
3281 guard: compactlong_float_guard,
3282 action: compactlong_float_add,
3283 },
3284 BinaryOpExtendSpecializationDescr {
3285 oparg: bytecode::BinaryOperator::Subtract,
3286 guard: compactlong_float_guard,
3287 action: compactlong_float_subtract,
3288 },
3289 BinaryOpExtendSpecializationDescr {
3290 oparg: bytecode::BinaryOperator::TrueDivide,
3291 guard: nonzero_compactlong_float_guard,
3292 action: compactlong_float_true_div,
3293 },
3294 BinaryOpExtendSpecializationDescr {
3295 oparg: bytecode::BinaryOperator::Multiply,
3296 guard: compactlong_float_guard,
3297 action: compactlong_float_multiply,
3298 },
3299];
3300
3301impl fmt::Debug for ExecutingFrame<'_> {
3302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3303 f.debug_struct("ExecutingFrame")
3304 .field("code", self.code)
3305 .field("stack_len", &self.localsplus.stack_len())
3306 .finish()
3307 }
3308}
3309
3310#[allow(clippy::too_many_arguments)]
3312impl ExecutingFrame<'_> {
3313 #[inline(always)]
3315 fn iframe(&self) -> &InterpreterFrame {
3316 unsafe { &*self.iframe }
3318 }
3319
3320 #[cold]
3323 #[inline(never)]
3324 fn frame_object(&self, vm: &VirtualMachine) -> FrameObjectRef {
3325 self.iframe().materialize(vm).to_owned()
3326 }
3327
3328 #[inline]
3330 fn trace_is_set(&self, _vm: &VirtualMachine) -> bool {
3331 self.iframe()
3332 .cold_opt()
3333 .is_some_and(|c| c.trace.lock().is_some())
3334 }
3335
3336 fn trace_call_from_resume(&mut self, vm: &VirtualMachine, resume_type: u32) -> PyResult<()> {
3341 if self.call_traced {
3342 return Ok(());
3343 }
3344 self.call_traced = true;
3345 if !vm.use_tracing.get() {
3346 return Ok(());
3347 }
3348 let what = if resume_type == 0 {
3350 monitoring::MonitoringEvent::PyStart
3351 } else {
3352 monitoring::MonitoringEvent::PyResume
3353 };
3354 let trace_result = vm.trace_event_what(crate::protocol::TraceEvent::Call, what, None)?;
3355 if let Some(local_trace) = trace_result {
3356 let was_unset = self.iframe().cold().trace.lock().is_none();
3357 *self.iframe().cold().trace.lock() = Some(local_trace);
3358 if was_unset {
3359 self.iframe().sync_prev_line_from_lasti();
3360 }
3361 }
3362 Ok(())
3363 }
3364
3365 #[inline]
3367 fn trace_opcodes_is_set(&self) -> bool {
3368 self.iframe()
3369 .cold_opt()
3370 .is_some_and(|c| *c.trace_opcodes.lock())
3371 }
3372
3373 #[inline]
3375 fn trace_lines_is_set(&self) -> bool {
3376 self.iframe().trace_lines_flag()
3377 }
3378
3379 #[inline]
3381 fn pending_stack_pops(&self) -> u32 {
3382 self.iframe()
3383 .cold_opt()
3384 .map_or(0, |c| c.pending_stack_pops.load(Relaxed))
3385 }
3386
3387 #[inline]
3389 fn pending_unwind_from_stack(&self) -> i64 {
3390 self.iframe()
3391 .cold_opt()
3392 .map_or(0, |c| c.pending_unwind_from_stack.load(Relaxed))
3393 }
3394
3395 #[inline]
3397 fn set_pending_stack_pops(&self, val: u32) {
3398 self.iframe().cold().pending_stack_pops.store(val, Relaxed);
3399 }
3400
3401 fn specialization_run_init(
3406 &self,
3407 new_obj: PyObjectRef,
3408 init_func: &Py<PyFunction>,
3409 args: &mut [Option<PyObjectRef>],
3410 vm: &VirtualMachine,
3411 ) -> PyResult<PyObjectRef> {
3412 args[0] = Some(new_obj.clone());
3413 let taken = args
3414 .iter_mut()
3415 .map(|slot| slot.take().expect("arg slot must be filled"));
3416
3417 let init_result = init_func.invoke_prepared_exact_args(taken, vm)?;
3418
3419 if !vm.is_none(&init_result) {
3420 return Err(vm.new_type_error(format!(
3421 "__init__() should return None, not '{:.200}'",
3422 init_result.class().name()
3423 )));
3424 }
3425 Ok(new_obj)
3426 }
3427
3428 #[inline(always)]
3429 fn update_lasti(&mut self, f: impl FnOnce(&mut u32)) {
3430 let mut val = self.lasti.load(Relaxed);
3431 f(&mut val);
3432 self.lasti.store(val, Relaxed);
3433 }
3434
3435 #[inline(always)]
3436 fn lasti(&self) -> u32 {
3437 self.lasti.load(Relaxed)
3438 }
3439
3440 #[inline(always)]
3442 fn cell_ref(&self, localsplus_idx: usize) -> &PyCell {
3443 let fastlocals = self.localsplus.fastlocals();
3444 let slot = &fastlocals[localsplus_idx];
3445 slot.as_ref()
3446 .expect("cell slot empty")
3447 .downcast_ref::<PyCell>()
3448 .expect("cell slot is not a PyCell")
3449 }
3450
3451 fn apply_pending_lineno_jump(&mut self, vm: &VirtualMachine) {
3453 let pops = self.pending_stack_pops();
3454 if pops > 0 {
3455 let from_stack = self.pending_unwind_from_stack();
3456 self.unwind_stack_for_lineno(pops as usize, from_stack, vm);
3457 self.set_pending_stack_pops(0);
3458 }
3459 }
3460
3461 fn yield_value_event(&mut self, vm: &VirtualMachine) -> PyResult<bool> {
3465 let lasti_before = self.lasti();
3466 if vm.use_tracing.get() {
3467 let value = self.top_value().to_owned();
3468 vm.trace_event_what(
3469 crate::protocol::TraceEvent::Return,
3470 monitoring::MonitoringEvent::PyYield,
3471 Some(value),
3472 )?;
3473 if self.lasti() != lasti_before {
3474 self.apply_pending_lineno_jump(vm);
3475 return Ok(true);
3476 }
3477 }
3478 if self.monitoring_mask & MonitoringEvent::PyYield.mask() != 0 {
3479 let value = self.top_value().to_owned();
3480 let offset = (self.lasti() - 1) * 2;
3481 monitoring::fire_py_yield(vm, self.code, offset, &value)?;
3482 if self.lasti() != lasti_before {
3483 self.apply_pending_lineno_jump(vm);
3484 return Ok(true);
3485 }
3486 }
3487 Ok(false)
3488 }
3489
3490 fn unwind_stack_for_lineno(&mut self, pop_count: usize, from_stack: i64, vm: &VirtualMachine) {
3497 let mut cur_stack = from_stack;
3498 for _ in 0..pop_count {
3499 let val = self.pop_value_opt();
3500 if stack_analysis::top_of_stack(cur_stack) == stack_analysis::Kind::Except as i64
3501 && let Some(exc_obj) = val
3502 {
3503 debug_assert!(
3509 self.code.has_exc_handling,
3510 "unwinding an Except slot in a frame without exc-handling opcodes"
3511 );
3512 if vm.is_none(&exc_obj) {
3513 vm.set_exception(None);
3514 } else {
3515 let exc = exc_obj.downcast::<PyBaseException>().ok();
3516 vm.set_exception(exc);
3517 }
3518 }
3519 cur_stack = stack_analysis::pop_value(cur_stack);
3520 }
3521 }
3522
3523 fn fire_exception_trace(&self, exc: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<()> {
3527 if vm.use_tracing.get() && self.trace_is_set(vm) {
3528 let exc_type: PyObjectRef = exc.class().to_owned().into();
3529 let exc_value: PyObjectRef = exc.to_owned().into();
3530 let exc_tb: PyObjectRef = exc
3531 .traceback()
3532 .map_or_else(|| vm.ctx.none(), |tb| -> PyObjectRef { tb.into() });
3533 let tuple = vm.ctx.new_tuple(vec![exc_type, exc_value, exc_tb]).into();
3534 vm.trace_event(crate::protocol::TraceEvent::Exception, Some(tuple))?;
3535 }
3536 Ok(())
3537 }
3538
3539 fn run(&mut self, vm: &VirtualMachine) -> PyResult<ExecutionResult> {
3540 flame_guard!(format!(
3541 "FrameObject::run({obj_name})",
3542 obj_name = self.code.obj_name
3543 ));
3544 let lasti_cell = self.lasti;
3557 let units_ptr = self.code.instructions.units_ptr();
3561 let mut arg_state = bytecode::OpArgState::default();
3562 let mut idx = lasti_cell.load(Relaxed) as usize;
3563 #[cfg(feature = "opcode-histogram")]
3566 let mut prev_op: u8 = 0;
3567 loop {
3568 lasti_cell.store(idx as u32 + 1, Relaxed);
3574
3575 let mut tracing = vm.use_tracing.get();
3583
3584 if tracing
3592 && self.trace_is_set(vm)
3593 && self.trace_lines_is_set()
3594 && !matches!(
3595 self.code.instructions.read_op(idx),
3596 Instruction::Resume { .. } | Instruction::InstrumentedResume
3597 )
3598 {
3599 let line = self.code.addr2line(idx as i32 * 2);
3600 if line >= 0 && line as u32 != self.prev_line.get() {
3601 self.prev_line.set(line as u32);
3602 match vm.trace_event(crate::protocol::TraceEvent::Line, None) {
3603 Ok(_) => {}
3604 Err(exception) => {
3605 if let Some((loc, _end_loc)) = self.code.locations.get(idx) {
3606 let next = exception.traceback();
3607 let new_traceback = PyTraceback::new(
3608 next,
3609 self.frame_object(vm),
3610 idx as i32 * 2,
3611 loc.line,
3612 );
3613 exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
3614 }
3615 match self.unwind_blocks(
3616 vm,
3617 UnwindReason::Raising {
3618 exception,
3619 offset: idx as u32,
3620 },
3621 ) {
3622 Ok(None) => {
3623 arg_state.reset();
3624 idx = lasti_cell.load(Relaxed) as usize;
3625 continue;
3626 }
3627 Ok(Some(value)) => break Ok(value),
3628 Err(e) => break Err(e),
3629 }
3630 }
3631 }
3632 tracing = vm.use_tracing.get();
3636 if lasti_cell.load(Relaxed) != (idx as u32 + 1) {
3639 let pops = self.pending_stack_pops();
3642 if pops > 0 {
3643 let from_stack = self.pending_unwind_from_stack();
3644 self.unwind_stack_for_lineno(pops as usize, from_stack, vm);
3645 self.set_pending_stack_pops(0);
3646 }
3647 arg_state.reset();
3648 idx = lasti_cell.load(Relaxed) as usize;
3649 continue;
3650 }
3651 }
3652 }
3653 let unit = unsafe { bytecode::CodeUnits::read_unit_from(units_ptr, idx) };
3660 let op = unit.op;
3661 let arg = arg_state.extend(unit.arg);
3662 let mut do_extend_arg = false;
3663
3664 if tracing {
3675 if self.trace_is_set(vm)
3679 && self.trace_opcodes_is_set()
3680 && !matches!(
3681 op.into(),
3682 Opcode::Resume | Opcode::InstrumentedResume | Opcode::ExtendedArg
3683 )
3684 {
3685 vm.trace_event(crate::protocol::TraceEvent::Opcode, None)?;
3686 }
3687 }
3688
3689 #[cold]
3694 #[inline(never)]
3695 fn eval_breaker_work(vm: &VirtualMachine) -> PyResult<()> {
3696 vm.check_signals()?;
3697 #[cfg(feature = "threading")]
3701 vm.run_scheduled_gc();
3702 Ok(())
3703 }
3704 if vm.eval_breaker_tripped()
3705 && let Err(exception) = eval_breaker_work(vm)
3706 {
3707 #[cold]
3708 fn handle_signal_exception(
3709 frame: &mut ExecutingFrame<'_>,
3710 exception: PyBaseExceptionRef,
3711 idx: usize,
3712 vm: &VirtualMachine,
3713 ) -> FrameResult {
3714 if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
3715 let next = exception.traceback();
3716 let new_traceback = PyTraceback::new(
3717 next,
3718 frame.frame_object(vm),
3719 idx as i32 * 2,
3720 loc.line,
3721 );
3722 exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
3723 }
3724 vm.contextualize_exception(&exception);
3725 frame.unwind_blocks(
3726 vm,
3727 UnwindReason::Raising {
3728 exception,
3729 offset: idx as u32,
3730 },
3731 )
3732 }
3733 match handle_signal_exception(self, exception, idx, vm) {
3734 Ok(None) => {}
3735 Ok(Some(value)) => {
3736 break Ok(value);
3737 }
3738 Err(exception) => {
3739 break Err(exception);
3740 }
3741 }
3742 arg_state.reset();
3747 idx = lasti_cell.load(Relaxed) as usize;
3748 continue;
3749 }
3750 let lasti_before = idx as u32 + 1;
3754 #[cfg(feature = "opcode-histogram")]
3755 {
3756 let op_byte = u8::from(op);
3757 crate::opcode_histogram::record(prev_op, op_byte);
3758 prev_op = op_byte;
3759 }
3760 let result = self.execute_instruction(op, arg, &mut do_extend_arg, vm);
3761 let caches = op.cache_entries();
3767 let mut next_idx = lasti_cell.load(Relaxed);
3768 if caches > 0 && next_idx == lasti_before {
3769 next_idx = lasti_before + caches as u32;
3770 lasti_cell.store(next_idx, Relaxed);
3771 }
3772 match result {
3773 Ok(None) => {}
3774 Ok(Some(value)) => {
3775 break Ok(value);
3776 }
3777 Err(exception) => {
3779 #[cold]
3780 fn handle_exception(
3781 frame: &mut ExecutingFrame<'_>,
3782 exception: PyBaseExceptionRef,
3783 idx: usize,
3784 is_reraise: bool,
3785 is_new_raise: bool,
3786 vm: &VirtualMachine,
3787 ) -> FrameResult {
3788 if !is_reraise {
3795 let had_prior_traceback = exception.traceback().is_some();
3799
3800 if let Some((loc, _end_loc)) = frame.code.locations.get(idx) {
3805 let next = exception.traceback();
3806
3807 let new_traceback = PyTraceback::new(
3808 next,
3809 frame.frame_object(vm),
3810 idx as i32 * 2,
3811 loc.line,
3812 );
3813 vm_trace!(
3814 "Adding to traceback: {:?} {:?}",
3815 new_traceback,
3816 loc.line
3817 );
3818 exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
3819 }
3820
3821 if is_new_raise || !had_prior_traceback {
3827 vm.contextualize_exception(&exception);
3828 }
3829 }
3830
3831 frame.unwind_blocks(
3833 vm,
3834 UnwindReason::Raising {
3835 exception,
3836 offset: idx as u32,
3837 },
3838 )
3839 }
3840
3841 let is_reraise = match op {
3847 Instruction::RaiseVarargs { argc: kind } => matches!(
3848 kind.get(arg),
3849 bytecode::RaiseKind::BareRaise | bytecode::RaiseKind::ReraiseFromStack
3850 ),
3851 Instruction::Reraise { .. }
3852 | Instruction::EndAsyncFor
3853 | Instruction::CleanupThrow => true,
3854 _ => false,
3855 };
3856
3857 let is_new_raise = matches!(
3860 op,
3861 Instruction::RaiseVarargs { argc: kind }
3862 if matches!(
3863 kind.get(arg),
3864 bytecode::RaiseKind::Raise | bytecode::RaiseKind::RaiseCause
3865 )
3866 );
3867
3868 let exception = {
3871 let mon_events = vm.state.monitoring_events.load();
3872 if is_reraise {
3873 if mon_events & MonitoringEvent::Reraise.mask() != 0 {
3874 let offset = idx as u32 * 2;
3875 let exc_obj: PyObjectRef = exception.clone().into();
3876 match monitoring::fire_reraise(vm, self.code, offset, &exc_obj) {
3877 Ok(()) => exception,
3878 Err(monitor_exc) => monitor_exc,
3879 }
3880 } else {
3881 exception
3882 }
3883 } else if mon_events & MonitoringEvent::Raise.mask() != 0 {
3884 let offset = idx as u32 * 2;
3885 let exc_obj: PyObjectRef = exception.clone().into();
3886 match monitoring::fire_raise(vm, self.code, offset, &exc_obj) {
3887 Ok(()) => exception,
3888 Err(monitor_exc) => monitor_exc,
3889 }
3890 } else {
3891 exception
3892 }
3893 };
3894
3895 if !is_reraise {
3899 self.fire_exception_trace(&exception, vm)?;
3900 }
3901
3902 match handle_exception(self, exception, idx, is_reraise, is_new_raise, vm) {
3903 Ok(None) => next_idx = lasti_cell.load(Relaxed),
3906 Ok(Some(result)) => break Ok(result),
3907 Err(exception) => {
3908 let exception = if vm.state.monitoring_events.load()
3910 & MonitoringEvent::PyUnwind.mask()
3911 != 0
3912 {
3913 let offset = idx as u32 * 2;
3914 let exc_obj: PyObjectRef = exception.clone().into();
3915 match monitoring::fire_py_unwind(vm, self.code, offset, &exc_obj) {
3916 Ok(()) => exception,
3917 Err(monitor_exc) => monitor_exc,
3918 }
3919 } else {
3920 exception
3921 };
3922
3923 break Err(exception);
3924 }
3925 }
3926 }
3927 }
3928 if !do_extend_arg {
3929 arg_state.reset()
3930 }
3931 idx = next_idx as usize;
3932 }
3933 }
3934
3935 fn yield_from_target(&self) -> Option<&PyObject> {
3936 if self.localsplus.stack_is_empty() {
3945 return None;
3946 }
3947 let lasti = self.lasti() as usize;
3948 if let Some(unit) = self.code.instructions.get(lasti) {
3949 match &unit.op {
3950 Instruction::Send { .. } => return Some(self.top_value()),
3951 Instruction::Resume { .. } | Instruction::InstrumentedResume => {
3952 if lasti > 0
3955 && let Some(prev_unit) = self.code.instructions.get(lasti - 1)
3956 && matches!(
3957 &prev_unit.op.into(),
3958 Opcode::YieldValue | Opcode::InstrumentedYieldValue
3959 )
3960 {
3961 if u8::from(prev_unit.arg) >= 1 {
3964 return Some(self.top_value());
3966 }
3967 }
3968 }
3969 _ => {}
3970 }
3971 }
3972 None
3973 }
3974
3975 fn gen_throw(
3977 &mut self,
3978 vm: &VirtualMachine,
3979 exc_type: PyObjectRef,
3980 exc_val: PyObjectRef,
3981 exc_tb: PyObjectRef,
3982 ) -> PyResult<ExecutionResult> {
3983 self.monitoring_mask = vm.state.monitoring_events.load();
3984 if let Some(jen) = self.yield_from_target() {
3985 let is_gen_exit = if let Some(typ) = exc_type.downcast_ref::<PyType>() {
3988 typ.fast_issubclass(vm.ctx.exceptions.generator_exit)
3989 } else {
3990 exc_type.fast_isinstance(vm.ctx.exceptions.generator_exit)
3991 };
3992
3993 if is_gen_exit {
3994 let close_result = if let Some(coro) = self.builtin_coro(jen) {
3998 coro.close(jen, vm).map(|_| ())
3999 } else {
4000 match vm.get_attribute_opt(jen, "close") {
4001 Ok(Some(close_meth)) => close_meth.call((), vm).map(|_| ()),
4002 Ok(None) => Ok(()),
4003 Err(e) => {
4004 let msg = jen
4005 .repr(vm)
4006 .ok()
4007 .map(|r| format!("Exception ignored while closing generator {r}"));
4008 vm.run_unraisable(e, msg, vm.ctx.none());
4009 Ok(())
4010 }
4011 }
4012 };
4013 if let Err(err) = close_result {
4014 let idx = self.lasti().saturating_sub(1) as usize;
4015 if idx < self.code.locations.len() {
4016 let (loc, _end_loc) = self.code.locations[idx];
4017 let next = err.traceback();
4018 let new_traceback =
4019 PyTraceback::new(next, self.frame_object(vm), idx as i32 * 2, loc.line);
4020 err.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
4021 }
4022
4023 self.push_value(vm.ctx.none());
4024 vm.chain_stack_item(&err);
4025 return match self.unwind_blocks(
4026 vm,
4027 UnwindReason::Raising {
4028 exception: err,
4029 offset: idx as u32,
4030 },
4031 ) {
4032 Ok(None) => {
4033 self.prev_line.set(0);
4034 self.run(vm)
4035 }
4036 Ok(Some(result)) => Ok(result),
4037 Err(exception) => Err(exception),
4038 };
4039 }
4040 } else {
4042 let thrower = if let Some(coro) = self.builtin_coro(jen) {
4044 Some(Either::A(coro))
4045 } else {
4046 vm.get_attribute_opt(jen, "throw")?.map(Either::B)
4047 };
4048 if let Some(thrower) = thrower {
4049 let ret = match thrower {
4050 Either::A(coro) => coro
4051 .throw(jen, exc_type, exc_val, exc_tb, vm)
4052 .to_pyresult(vm),
4053 Either::B(meth) => {
4054 let args = if !vm.is_none(&exc_tb) {
4058 vec![exc_type, exc_val, exc_tb]
4059 } else if !vm.is_none(&exc_val) {
4060 vec![exc_type, exc_val]
4061 } else {
4062 vec![exc_type]
4063 };
4064 meth.call(args, vm)
4065 }
4066 };
4067 return ret.map(ExecutionResult::Yield).or_else(|err| {
4068 let idx = self.lasti().saturating_sub(1) as usize;
4073 if idx < self.code.locations.len() {
4074 let (loc, _end_loc) = self.code.locations[idx];
4075 let next = err.traceback();
4076 let new_traceback = PyTraceback::new(
4077 next,
4078 self.frame_object(vm),
4079 idx as i32 * 2,
4080 loc.line,
4081 );
4082 err.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
4083 }
4084
4085 self.push_value(vm.ctx.none());
4086 vm.chain_stack_item(&err);
4087 match self.unwind_blocks(
4088 vm,
4089 UnwindReason::Raising {
4090 exception: err,
4091 offset: idx as u32,
4092 },
4093 ) {
4094 Ok(None) => {
4095 self.prev_line.set(0);
4096 self.run(vm)
4097 }
4098 Ok(Some(result)) => Ok(result),
4099 Err(exception) => Err(exception),
4100 }
4101 });
4102 }
4103 }
4104 }
4105 let ctor = ExceptionCtor::try_from_object(vm, exc_type)?;
4110 let exception = match ctor.instantiate_value(exc_val, vm) {
4111 Ok(exc) => {
4112 if let Some(tb) = Option::<PyRef<PyTraceback>>::try_from_object(vm, exc_tb)? {
4113 exc.set_traceback(Some(tb));
4114 }
4115 exc
4116 }
4117 Err(err) => err,
4118 };
4119
4120 let idx = self.lasti().saturating_sub(1) as usize;
4122 if idx < self.code.locations.len() {
4123 let (loc, _end_loc) = self.code.locations[idx];
4124 let next = exception.traceback();
4125 let new_traceback =
4126 PyTraceback::new(next, self.frame_object(vm), idx as i32 * 2, loc.line);
4127 exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
4128 }
4129
4130 if vm.use_tracing.get() {
4133 let what = monitoring::MonitoringEvent::PyThrow;
4134 if let Some(local_trace) =
4135 vm.trace_event_what(crate::protocol::TraceEvent::Call, what, None)?
4136 {
4137 *self.iframe().cold().trace.lock() = Some(local_trace);
4138 }
4139 self.fire_exception_trace(&exception, vm)?;
4140 }
4141 let exception = {
4142 let mon_events = vm.state.monitoring_events.load();
4143 let exception = if mon_events & MonitoringEvent::PyThrow.mask() != 0 {
4144 let offset = idx as u32 * 2;
4145 let exc_obj: PyObjectRef = exception.clone().into();
4146 match monitoring::fire_py_throw(vm, self.code, offset, &exc_obj) {
4147 Ok(()) => exception,
4148 Err(monitor_exc) => monitor_exc,
4149 }
4150 } else {
4151 exception
4152 };
4153 if mon_events & MonitoringEvent::Raise.mask() != 0 {
4154 let offset = idx as u32 * 2;
4155 let exc_obj: PyObjectRef = exception.clone().into();
4156 match monitoring::fire_raise(vm, self.code, offset, &exc_obj) {
4157 Ok(()) => exception,
4158 Err(monitor_exc) => monitor_exc,
4159 }
4160 } else {
4161 exception
4162 }
4163 };
4164
4165 vm.chain_stack_item(&exception);
4168
4169 self.push_value(vm.ctx.none());
4172
4173 match self.unwind_blocks(
4174 vm,
4175 UnwindReason::Raising {
4176 exception,
4177 offset: idx as u32,
4178 },
4179 ) {
4180 Ok(None) => {
4181 self.prev_line.set(0);
4185 self.run(vm)
4186 }
4187 Ok(Some(result)) => Ok(result),
4188 Err(exception) => {
4189 let exception =
4191 if vm.state.monitoring_events.load() & MonitoringEvent::PyUnwind.mask() != 0 {
4192 let offset = idx as u32 * 2;
4193 let exc_obj: PyObjectRef = exception.clone().into();
4194 match monitoring::fire_py_unwind(vm, self.code, offset, &exc_obj) {
4195 Ok(()) => exception,
4196 Err(monitor_exc) => monitor_exc,
4197 }
4198 } else {
4199 exception
4200 };
4201 Err(exception)
4202 }
4203 }
4204 }
4205
4206 fn unbound_cell_exception(
4207 &self,
4208 localsplus_idx: usize,
4209 vm: &VirtualMachine,
4210 ) -> PyBaseExceptionRef {
4211 use rustpython_compiler_core::bytecode::CO_FAST_FREE;
4212 let kind = self
4213 .code
4214 .localspluskinds
4215 .get(localsplus_idx)
4216 .copied()
4217 .unwrap_or(0);
4218 if kind & CO_FAST_FREE != 0 {
4219 let name = self.localsplus_name(localsplus_idx);
4220 vm.new_name_error(
4221 format!("cannot access free variable '{name}' where it is not associated with a value in enclosing scope"),
4222 name.to_owned(),
4223 )
4224 } else {
4225 let name = self.localsplus_name(localsplus_idx);
4227 vm.new_unbound_local_error(format!(
4228 "local variable '{name}' referenced before assignment"
4229 ))
4230 }
4231 }
4232
4233 fn localsplus_name(&self, idx: usize) -> &'static PyStrInterned {
4235 localsplus_name(self.code, idx)
4236 }
4237
4238 #[inline(always)]
4240 fn execute_instruction(
4241 &mut self,
4242 instruction: Instruction,
4243 arg: bytecode::OpArg,
4244 extend_arg: &mut bool,
4245 vm: &VirtualMachine,
4246 ) -> FrameResult {
4247 flame_guard!(format!(
4248 "FrameObject::execute_instruction({instruction:?} {arg:?})"
4249 ));
4250
4251 #[cfg(feature = "vm-tracing-logging")]
4252 {
4253 trace!("=======");
4254 trace!(" {:#?}", self);
4260 trace!(" Executing opcode: {instruction:?} {arg:?}",);
4261 trace!("=======");
4262 }
4263
4264 #[cold]
4265 fn name_error(name: &'static PyStrInterned, vm: &VirtualMachine) -> PyBaseExceptionRef {
4266 vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
4267 }
4268
4269 match instruction {
4270 Instruction::BinaryOp { op } => {
4271 let op_val = op.get(arg);
4272 self.adaptive(|s, ii, cb| s.specialize_binary_op(vm, op_val, ii, cb));
4273 self.execute_bin_op(vm, op_val)
4274 }
4275 Instruction::BinaryOpInplaceAddUnicode => {
4278 let b = self.top_value();
4279 let a = self.nth_value(1);
4280 let instr_idx = self.lasti() as usize - 1;
4281 let cache_base = instr_idx + 1;
4282 let target_local = self.binary_op_inplace_unicode_target_local(cache_base, a);
4283 if let (Some(_a_str), Some(_b_str), Some(target_local)) = (
4284 a.downcast_ref_if_exact::<PyStr>(vm),
4285 b.downcast_ref_if_exact::<PyStr>(vm),
4286 target_local,
4287 ) {
4288 let right = self.pop_value();
4289 let left = self.pop_value();
4290
4291 let local_obj = self.localsplus.fastlocals_mut()[target_local]
4292 .take()
4293 .expect("BINARY_OP_INPLACE_ADD_UNICODE target local missing");
4294 debug_assert!(local_obj.is(&left));
4295 let mut local_str = local_obj
4296 .downcast_exact::<PyStr>(vm)
4297 .expect("BINARY_OP_INPLACE_ADD_UNICODE target local not exact str")
4298 .into_pyref();
4299 drop(left);
4300 let right_str = right
4301 .downcast_ref_if_exact::<PyStr>(vm)
4302 .expect("BINARY_OP_INPLACE_ADD_UNICODE right operand not exact str");
4303 local_str.concat_in_place(right_str.as_wtf8(), vm);
4304
4305 self.localsplus.fastlocals_mut()[target_local] = Some(local_str.into());
4306 self.jump_relative_forward(
4307 1,
4308 Instruction::BinaryOpInplaceAddUnicode.cache_entries() as u32,
4309 );
4310 Ok(None)
4311 } else {
4312 self.execute_bin_op(vm, self.binary_op_from_arg(arg))
4313 }
4314 }
4315 Instruction::BinarySlice => {
4316 let stop = self.pop_value();
4318 let start = self.pop_value();
4319 let container = self.pop_value();
4320 let slice: PyObjectRef = PySlice {
4321 start: Some(start),
4322 stop,
4323 step: None,
4324 }
4325 .into_ref(&vm.ctx)
4326 .into();
4327 let result = container.get_item(&*slice, vm)?;
4328 self.push_value(result);
4329 Ok(None)
4330 }
4331 Instruction::BuildList { count: size } => {
4332 let sz = size.get(arg) as usize;
4333 let elements = self.pop_multiple(sz).collect();
4334 let list_obj = vm.ctx.new_list(elements);
4335 self.push_value(list_obj.into());
4336 Ok(None)
4337 }
4338 Instruction::BuildMap { count: size } => self.execute_build_map(vm, size.get(arg)),
4339 Instruction::BuildSet { count: size } => {
4340 let set = PySet::default().into_ref(&vm.ctx);
4341 for element in self.pop_multiple(size.get(arg) as usize) {
4342 set.add(element, vm)?;
4343 }
4344 self.push_value(set.into());
4345 Ok(None)
4346 }
4347 Instruction::BuildSlice { argc } => Ok(self.execute_build_slice(vm, argc.get(arg))),
4348 Instruction::BuildString { count: size } => {
4359 let s: Wtf8Buf = self
4360 .pop_multiple(size.get(arg) as usize)
4361 .map(|pyobj| pyobj.downcast::<PyStr>().unwrap())
4362 .collect();
4363 self.push_value(vm.ctx.new_str(s).into());
4364 Ok(None)
4365 }
4366 Instruction::BuildTuple { count: size } => {
4367 let elements = self.pop_multiple(size.get(arg) as usize).collect();
4368 let list_obj = vm.ctx.new_tuple(elements);
4369 self.push_value(list_obj.into());
4370 Ok(None)
4371 }
4372 Instruction::BuildTemplate => {
4373 let interpolations = self.pop_value();
4375 let strings = self.pop_value();
4376
4377 let strings = strings
4378 .downcast::<PyTuple>()
4379 .map_err(|_| vm.new_type_error("BUILD_TEMPLATE expected tuple for strings"))?;
4380 let interpolations = interpolations.downcast::<PyTuple>().map_err(|_| {
4381 vm.new_type_error("BUILD_TEMPLATE expected tuple for interpolations")
4382 })?;
4383
4384 let template = PyTemplate::new(strings, interpolations);
4385 self.push_value(template.into_pyobject(vm));
4386 Ok(None)
4387 }
4388 Instruction::BuildInterpolation { format: oparg } => {
4389 let oparg_val = oparg.get(arg);
4392 let has_format_spec = (oparg_val & 1) != 0;
4393 let conversion_code = oparg_val >> 2;
4394
4395 let format_spec = if has_format_spec {
4396 self.pop_value().downcast::<PyStr>().map_err(|_| {
4397 vm.new_type_error("BUILD_INTERPOLATION expected str for format_spec")
4398 })?
4399 } else {
4400 vm.ctx.empty_str.to_owned()
4401 };
4402
4403 let expression = self.pop_value().downcast::<PyStr>().map_err(|_| {
4404 vm.new_type_error("BUILD_INTERPOLATION expected str for expression")
4405 })?;
4406 let value = self.pop_value();
4407
4408 let conversion: PyObjectRef = match conversion_code {
4410 0 => vm.ctx.none(),
4411 1 => vm.ctx.new_str("s").into(),
4412 2 => vm.ctx.new_str("r").into(),
4413 3 => vm.ctx.new_str("a").into(),
4414 _ => vm.ctx.none(), };
4416
4417 let interpolation =
4418 PyInterpolation::new(value, expression, conversion, format_spec, vm)?;
4419 self.push_value(interpolation.into_pyobject(vm));
4420 Ok(None)
4421 }
4422 Instruction::Call { argc: nargs } => {
4423 let nargs_val = nargs.get(arg);
4425 self.adaptive(|s, ii, cb| s.specialize_call(vm, nargs_val, ii, cb));
4426 self.execute_call_vectorcall(nargs_val, vm)
4427 }
4428 Instruction::CallKw { argc: nargs } => {
4429 let nargs = nargs.get(arg);
4430 self.adaptive(|s, ii, cb| s.specialize_call_kw(vm, nargs, ii, cb));
4431 self.execute_call_kw_vectorcall(nargs, vm)
4433 }
4434 Instruction::CallFunctionEx => {
4435 let args = self.collect_ex_args(vm)?;
4437 self.execute_call(args, vm)
4438 }
4439 Instruction::CallIntrinsic1 { func } => {
4440 let value = self.pop_value();
4441 let result = self.call_intrinsic_1(func.get(arg), value, vm)?;
4442 self.push_value(result);
4443 Ok(None)
4444 }
4445 Instruction::CallIntrinsic2 { func } => {
4446 let value2 = self.pop_value();
4447 let value1 = self.pop_value();
4448 let result = self.call_intrinsic_2(func.get(arg), value1, value2, vm)?;
4449 self.push_value(result);
4450 Ok(None)
4451 }
4452 Instruction::CheckEgMatch => {
4453 let match_type = self.pop_value();
4454 let exc_value = self.pop_value();
4455 let (rest, matched) =
4456 crate::exceptions::exception_group_match(&exc_value, &match_type, vm)?;
4457
4458 if !vm.is_none(&matched)
4461 && let Some(exc) = matched.downcast_ref::<PyBaseException>()
4462 {
4463 vm.set_exception(Some(exc.to_owned()));
4464 }
4465
4466 self.push_value(rest);
4467 self.push_value(matched);
4468 Ok(None)
4469 }
4470 Instruction::CompareOp { opname: op } => {
4471 let op_val = op.get(arg);
4472 self.adaptive(|s, ii, cb| s.specialize_compare_op(vm, op_val, ii, cb));
4473 self.execute_compare(vm, arg)
4474 }
4475 Instruction::ContainsOp { invert } => {
4476 self.adaptive(|s, ii, cb| s.specialize_contains_op(vm, ii, cb));
4477 let b = self.pop_stackref();
4478 let a = self.pop_stackref();
4479
4480 let value = match invert.get(arg) {
4481 bytecode::Invert::No => self._in(vm, &a, &b)?,
4482 bytecode::Invert::Yes => self._not_in(vm, &a, &b)?,
4483 };
4484 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
4485 Ok(None)
4486 }
4487 Instruction::ConvertValue { oparg: conversion } => {
4488 self.convert_value(conversion.get(arg), vm)
4489 }
4490 Instruction::Copy { i: index } => {
4491 let idx = index.get(arg) as usize;
4495 let stack_len = self.localsplus.stack_len();
4496 debug_assert!(stack_len >= idx, "CopyItem: stack underflow");
4497 let value = self.localsplus.stack_index(stack_len - idx);
4498 self.push_stackref_opt(value.cloned());
4499 Ok(None)
4500 }
4501 Instruction::CopyFreeVars { n } => {
4502 let n = n.get(arg) as usize;
4503 if n > 0 {
4504 let closure = self
4505 .func_obj
4506 .and_then(|f| f.downcast_ref::<PyFunction>())
4507 .and_then(|f| f.closure.as_ref());
4508 let nlocalsplus = self.code.localspluskinds.len();
4509 let freevar_start = nlocalsplus - n;
4510 let fastlocals = self.localsplus.fastlocals_mut();
4511 if let Some(closure) = closure {
4512 for i in 0..n {
4513 fastlocals[freevar_start + i] =
4514 Some(closure.as_slice()[i].clone().into());
4515 }
4516 }
4517 }
4518 Ok(None)
4519 }
4520 Instruction::DeleteAttr { namei: idx } => self.delete_attr(vm, idx.get(arg)),
4521 Instruction::DeleteDeref { i } => {
4522 self.cell_ref(i.get(arg).as_usize()).set(None);
4523 Ok(None)
4524 }
4525 Instruction::DeleteFast { var_num } => {
4526 self.localsplus
4527 .debug_audit_local_release(var_num.get(arg).as_usize());
4528 let fastlocals = self.localsplus.fastlocals_mut();
4529 let idx = var_num.get(arg);
4530 if fastlocals[idx].is_none() {
4531 return Err(vm.new_unbound_local_error(format!(
4532 "local variable '{}' referenced before assignment",
4533 self.code.varnames[idx]
4534 )));
4535 }
4536 fastlocals[idx] = None;
4537 Ok(None)
4538 }
4539 Instruction::DeleteGlobal { namei: idx } => {
4540 let name = self.code.names[idx.get(arg) as usize];
4541 match self.globals.del_item(name, vm) {
4542 Ok(()) => {}
4543 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
4544 return Err(name_error(name, vm));
4545 }
4546 Err(e) => return Err(e),
4547 }
4548 Ok(None)
4549 }
4550 Instruction::DeleteName { namei: idx } => {
4551 let name = self.code.names[idx.get(arg) as usize];
4552 let res = self.locals.mapping(vm).ass_subscript(name, None, vm);
4553
4554 match res {
4555 Ok(()) => {}
4556 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
4557 return Err(name_error(name, vm));
4558 }
4559 Err(e) => return Err(e),
4560 }
4561 Ok(None)
4562 }
4563 Instruction::DeleteSubscr => self.execute_delete_subscript(vm),
4564 Instruction::DictUpdate { i: index } => {
4565 let idx = index.get(arg);
4570
4571 let source = self.pop_value();
4573
4574 let dict = if idx <= 1 {
4576 self.top_value()
4578 } else {
4579 self.nth_value(idx - 1)
4581 };
4582
4583 let dict = dict.downcast_ref::<PyDict>().expect("exact dict expected");
4584
4585 if vm
4588 .get_method(source.clone(), vm.ctx.intern_str("keys"))
4589 .is_none()
4590 {
4591 return Err(vm.new_type_error(format!(
4592 "'{}' object is not a mapping",
4593 source.class().name()
4594 )));
4595 }
4596
4597 dict.merge_object(source, vm)?;
4598 Ok(None)
4599 }
4600 Instruction::DictMerge { i: index } => {
4601 let source = self.pop_value();
4602 let idx = index.get(arg);
4603
4604 let dict_ref = if idx <= 1 {
4606 self.top_value()
4607 } else {
4608 self.nth_value(idx - 1)
4609 };
4610
4611 let dict: &Py<PyDict> = unsafe { dict_ref.downcast_unchecked_ref() };
4612
4613 let callable = self.nth_value(idx + 2);
4616 let func_str = Self::object_function_str(callable, vm);
4617
4618 let source = if source.class().is(vm.ctx.types.dict_type) {
4623 let src_dict = source
4624 .downcast_ref::<PyDict>()
4625 .expect("exact dict must have a PyDict payload");
4626 for (key, value) in src_dict.items_vec() {
4630 if dict.contains_key(&*key, vm) {
4631 let key_str = key.str(vm)?;
4632 return Err(vm.new_type_error(format!(
4633 "{} got multiple values for keyword argument '{}'",
4634 func_str,
4635 key_str.as_wtf8()
4636 )));
4637 }
4638 dict.set_item(&*key, value, vm)?;
4639 }
4640 return Ok(None);
4641 } else {
4642 source
4643 };
4644
4645 if vm
4647 .get_method(source.clone(), vm.ctx.intern_str("keys"))
4648 .is_none()
4649 {
4650 return Err(vm.new_type_error(format!(
4651 "{} argument after ** must be a mapping, not {}",
4652 func_str,
4653 source.class().name()
4654 )));
4655 }
4656
4657 let keys_iter = vm.call_method(&source, "keys", ())?;
4659 for key in keys_iter.try_to_value::<Vec<PyObjectRef>>(vm)? {
4660 if dict.contains_key(&*key, vm) {
4661 let key_str = key.str(vm)?;
4662 return Err(vm.new_type_error(format!(
4663 "{} got multiple values for keyword argument '{}'",
4664 func_str,
4665 key_str.as_wtf8()
4666 )));
4667 }
4668 let value = vm.call_method(&source, "__getitem__", (key.clone(),))?;
4669 dict.set_item(&*key, value, vm)?;
4670 }
4671 Ok(None)
4672 }
4673 Instruction::EndAsyncFor => {
4674 let exc = self.pop_value();
4678 let _awaitable = self.pop_value();
4679
4680 let exc = exc
4681 .downcast::<PyBaseException>()
4682 .expect("EndAsyncFor expects exception on stack");
4683
4684 if exc.fast_isinstance(vm.ctx.exceptions.stop_async_iteration) {
4685 vm.set_exception(None);
4687 Ok(None)
4688 } else {
4689 Err(exc)
4691 }
4692 }
4693 Instruction::ExtendedArg => {
4694 *extend_arg = true;
4695 Ok(None)
4696 }
4697 Instruction::ForIter { .. } => {
4698 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
4700 self.adaptive(|s, ii, cb| s.specialize_for_iter(vm, u32::from(arg), ii, cb));
4701 self.execute_for_iter(vm, target)?;
4702 Ok(None)
4703 }
4704 Instruction::FormatSimple => {
4705 let value = self.pop_value();
4706 let formatted = vm.format(&value, vm.ctx.new_str(""))?;
4707 self.push_value(formatted.into());
4708
4709 Ok(None)
4710 }
4711 Instruction::FormatWithSpec => {
4712 let spec = self.pop_value();
4713 let value = self.pop_value();
4714 let formatted = vm.format(&value, spec.downcast::<PyStr>().unwrap())?;
4715 self.push_value(formatted.into());
4716
4717 Ok(None)
4718 }
4719 Instruction::GetAiter => {
4720 let aiterable = self.pop_value();
4721 let aiter = match vm.get_special_method(&aiterable, identifier!(vm, __aiter__))? {
4722 Some(meth) => meth.invoke((), vm)?,
4723 None => {
4724 return Err(vm.new_type_error(format!(
4725 "'async for' requires an object with __aiter__ method, got {}",
4726 aiterable.class().name()
4727 )));
4728 }
4729 };
4730 if vm
4731 .get_special_method(&aiter, identifier!(vm, __anext__))?
4732 .is_none()
4733 {
4734 return Err(vm.new_type_error(format!(
4735 "'async for' received an object from __aiter__ that does not implement __anext__: {}",
4736 aiter.class().name()
4737 )));
4738 }
4739 self.push_value(aiter);
4740 Ok(None)
4741 }
4742 Instruction::GetAnext => {
4743 #[cfg(debug_assertions)] let orig_stack_len = self.localsplus.stack_len();
4745
4746 let aiter = self.top_value();
4747 let awaitable = if aiter.class().is(vm.ctx.types.async_generator) {
4748 vm.call_special_method(aiter, identifier!(vm, __anext__), ())?
4749 } else {
4750 if vm
4751 .get_special_method(aiter, identifier!(vm, __anext__))?
4752 .is_none()
4753 {
4754 let msg = format!(
4755 "'async for' requires an iterator with __anext__ method, got {:.100}",
4756 aiter.class().name()
4757 );
4758 return Err(vm.new_type_error(msg));
4759 }
4760 let next_iter =
4761 vm.call_special_method(aiter, identifier!(vm, __anext__), ())?;
4762 crate::coroutine::get_awaitable_iter(next_iter.clone(), vm).map_err(|e| {
4763 let err = vm.new_type_error(format!(
4764 "'async for' received an invalid object from __anext__: {:.200}",
4765 next_iter.class().name()
4766 ));
4767 err.set_cause(Some(e));
4768 err
4769 })?
4770 };
4771 self.push_value(awaitable);
4772 #[cfg(debug_assertions)]
4773 debug_assert_eq!(orig_stack_len + 1, self.localsplus.stack_len());
4774 Ok(None)
4775 }
4776 Instruction::GetAwaitable { r#where: oparg } => {
4777 let iterable = self.pop_value();
4778
4779 let iter = match crate::coroutine::get_awaitable_iter(iterable.clone(), vm) {
4780 Ok(iter) => iter,
4781 Err(e) => {
4782 let oparg_val = oparg.get(arg);
4785 if vm
4786 .get_method(iterable.clone(), identifier!(vm, __await__))
4787 .is_none()
4788 {
4789 if oparg_val == 1 {
4790 return Err(vm.new_type_error(format!(
4791 "'async with' received an object from __aenter__ \
4792 that does not implement __await__: {}",
4793 iterable.class().name()
4794 )));
4795 } else if oparg_val == 2 {
4796 return Err(vm.new_type_error(format!(
4797 "'async with' received an object from __aexit__ \
4798 that does not implement __await__: {}",
4799 iterable.class().name()
4800 )));
4801 }
4802 }
4803 return Err(e);
4804 }
4805 };
4806
4807 if let Some(coro) = iter.downcast_ref::<PyCoroutine>()
4809 && coro
4810 .as_coro()
4811 .frame_opt()
4812 .and_then(|f| f.yield_from_target())
4813 .is_some()
4814 {
4815 return Err(vm.new_runtime_error("coroutine is being awaited already"));
4816 }
4817
4818 self.push_value(iter);
4819 Ok(None)
4820 }
4821 Instruction::GetIter => {
4822 let iterated_obj = self.pop_value();
4823 let iter_obj = PyIter::try_from_object(vm, iterated_obj)?;
4824 self.push_value(iter_obj.into());
4825 Ok(None)
4826 }
4827 Instruction::GetYieldFromIter => {
4828 let iterable = self.pop_value();
4833 let iter = if iterable.class().is(vm.ctx.types.coroutine_type) {
4834 if !self.code.flags.intersects(
4836 bytecode::CodeFlags::COROUTINE | bytecode::CodeFlags::ITERABLE_COROUTINE,
4837 ) {
4838 return Err(vm.new_type_error(
4839 "cannot 'yield from' a coroutine object in a non-coroutine generator",
4840 ));
4841 }
4842 iterable
4843 } else if iterable.class().is(vm.ctx.types.generator_type) {
4844 iterable
4846 } else {
4847 PyIter::try_from_object(vm, iterable)?.into()
4849 };
4850 self.push_value(iter);
4851 Ok(None)
4852 }
4853 Instruction::GetLen => {
4854 let obj = self.top_value();
4856 let len = obj.length(vm)?;
4857 self.push_value(vm.ctx.new_int(len).into());
4858 Ok(None)
4859 }
4860 Instruction::ImportFrom { namei: idx } => {
4861 let obj = self.import_from(vm, idx.get(arg))?;
4862 self.push_value(obj);
4863 Ok(None)
4864 }
4865 Instruction::ImportName { namei: idx } => {
4866 self.import(vm, Some(self.code.names[idx.get(arg) as usize]))?;
4867 Ok(None)
4868 }
4869 Instruction::IsOp { invert } => {
4870 let b = self.pop_stackref();
4871 let a = self.pop_stackref();
4872 let res = a.is(b.as_object());
4873
4874 let value = match invert.get(arg) {
4875 bytecode::Invert::No => res,
4876 bytecode::Invert::Yes => !res,
4877 };
4878 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
4879 Ok(None)
4880 }
4881 Instruction::JumpForward { .. } => {
4882 self.jump_relative_forward(u32::from(arg), 0);
4883 Ok(None)
4884 }
4885 Instruction::JumpBackward { .. } => {
4886 let instr_idx = self.lasti() as usize - 1;
4889 unsafe {
4890 self.code
4891 .instructions
4892 .replace_op(instr_idx, Instruction::JumpBackwardNoJit);
4893 }
4894 self.jump_relative_backward_and_trace_line(u32::from(arg), 1, vm)?;
4895 Ok(None)
4896 }
4897 Instruction::JumpBackwardJit | Instruction::JumpBackwardNoJit => {
4898 self.jump_relative_backward_and_trace_line(u32::from(arg), 1, vm)?;
4899 Ok(None)
4900 }
4901 Instruction::JumpBackwardNoInterrupt { .. } => {
4902 self.jump_relative_backward_and_trace_line(u32::from(arg), 0, vm)?;
4903 Ok(None)
4904 }
4905 Instruction::ListAppend { i } => {
4906 let item = self.pop_value();
4907 let obj = self.nth_value(i.get(arg) - 1);
4908 let list: &Py<PyList> = unsafe {
4909 obj.downcast_unchecked_ref()
4911 };
4912 list.append(item);
4913 Ok(None)
4914 }
4915 Instruction::ListExtend { i } => {
4916 let iterable = self.pop_value();
4917 let obj = self.nth_value(i.get(arg) - 1);
4918 let list: &Py<PyList> = unsafe {
4919 obj.downcast_unchecked_ref()
4921 };
4922 let type_name = iterable.class().name().to_owned();
4923 let not_iterable = iterable.class().slots().iter.load().is_none()
4927 && iterable
4928 .get_class_attr(vm.ctx.intern_str("__getitem__"))
4929 .is_none();
4930 list.extend(iterable, vm).map_err(|e| {
4931 if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
4932 vm.new_type_error(format!(
4933 "Value after * must be an iterable, not {type_name}"
4934 ))
4935 } else {
4936 e
4937 }
4938 })?;
4939 Ok(None)
4940 }
4941 Instruction::LoadAttr { namei: idx } => self.load_attr(vm, idx.get(arg)),
4942 Instruction::LoadSuperAttr { namei: idx } => {
4943 let idx_val = idx.get(arg);
4944 self.adaptive(|s, ii, cb| s.specialize_load_super_attr(vm, idx_val, ii, cb));
4945 self.load_super_attr(vm, idx_val)
4946 }
4947 Instruction::LoadBuildClass => {
4948 let build_class = if let Some(builtins_dict) = self.builtins_dict {
4949 builtins_dict
4950 .get_item_opt(identifier!(vm, __build_class__), vm)?
4951 .ok_or_else(|| {
4952 vm.new_name_error(
4953 "__build_class__ not found",
4954 identifier!(vm, __build_class__).to_owned(),
4955 )
4956 })?
4957 } else {
4958 self.builtins
4959 .get_item(identifier!(vm, __build_class__), vm)
4960 .map_err(|e| {
4961 if e.fast_isinstance(vm.ctx.exceptions.key_error) {
4962 vm.new_name_error(
4963 "__build_class__ not found",
4964 identifier!(vm, __build_class__).to_owned(),
4965 )
4966 } else {
4967 e
4968 }
4969 })?
4970 };
4971 self.push_value(build_class);
4972 Ok(None)
4973 }
4974 Instruction::LoadLocals => {
4975 let locals = self.locals.into_object(vm);
4977 self.push_value(locals);
4978 Ok(None)
4979 }
4980 Instruction::LoadFromDictOrDeref { i } => {
4981 let class_dict = self.pop_value();
4983 let idx = i.get(arg).as_usize();
4984 let name = self.localsplus_name(idx);
4985 let value = self.mapping_get_optional(&class_dict, name, vm)?;
4986 self.push_value(match value {
4987 Some(v) => v,
4988 None => self
4989 .cell_ref(idx)
4990 .get()
4991 .ok_or_else(|| self.unbound_cell_exception(idx, vm))?,
4992 });
4993 Ok(None)
4994 }
4995 Instruction::LoadFromDictOrGlobals { i: idx } => {
4996 let dict = self.pop_value();
4998 let name = self.code.names[idx.get(arg) as usize];
4999 let value = self.mapping_get_optional(&dict, name, vm)?;
5000
5001 self.push_value(match value {
5002 Some(v) => v,
5003 None => self.load_global_or_builtin(name, vm)?,
5004 });
5005 Ok(None)
5006 }
5007 Instruction::LoadConst { consti } => {
5008 self.push_value(self.code.constants[consti.get(arg)].clone().into());
5009 let instr_idx = self.lasti() as usize - 1;
5012 unsafe {
5013 self.code
5014 .instructions
5015 .replace_op(instr_idx, Instruction::LoadConstMortal);
5016 }
5017 Ok(None)
5018 }
5019 Instruction::LoadConstMortal | Instruction::LoadConstImmortal => {
5020 self.push_value(self.code.constants[u32::from(arg).into()].clone().into());
5021 Ok(None)
5022 }
5023 Instruction::LoadCommonConstant { idx } => {
5024 use bytecode::CommonConstant;
5025 let value = match idx.get(arg) {
5026 CommonConstant::AssertionError => {
5027 vm.ctx.exceptions.assertion_error.to_owned().into()
5028 }
5029 CommonConstant::NotImplementedError => {
5030 vm.ctx.exceptions.not_implemented_error.to_owned().into()
5031 }
5032 CommonConstant::BuiltinTuple => vm.ctx.types.tuple_type.to_owned().into(),
5033 CommonConstant::BuiltinAll => vm
5034 .callable_cache
5035 .builtin_all
5036 .clone()
5037 .expect("builtin_all not initialized"),
5038 CommonConstant::BuiltinAny => vm
5039 .callable_cache
5040 .builtin_any
5041 .clone()
5042 .expect("builtin_any not initialized"),
5043 CommonConstant::BuiltinList => vm.ctx.types.list_type.to_owned().into(),
5044 CommonConstant::BuiltinSet => vm.ctx.types.set_type.to_owned().into(),
5045 };
5046 self.push_value(value);
5047 Ok(None)
5048 }
5049 Instruction::LoadSmallInt { i: idx } => {
5050 let value = vm.ctx.cached_int(idx.get(arg) as i32);
5053 unsafe { self.push_borrowed(value.as_object()) };
5054 Ok(None)
5055 }
5056 Instruction::LoadDeref { i } => {
5057 let idx = i.get(arg).as_usize();
5058 let x = self
5059 .cell_ref(idx)
5060 .get()
5061 .ok_or_else(|| self.unbound_cell_exception(idx, vm))?;
5062 self.push_value(x);
5063 Ok(None)
5064 }
5065 Instruction::LoadFast { var_num } => {
5066 #[cold]
5067 fn reference_error(
5068 varname: &'static PyStrInterned,
5069 vm: &VirtualMachine,
5070 ) -> PyBaseExceptionRef {
5071 vm.new_unbound_local_error(format!(
5072 "local variable '{varname}' referenced before assignment"
5073 ))
5074 }
5075 let idx = var_num.get(arg);
5076 let x = self.localsplus.fastlocals()[idx]
5077 .clone()
5078 .ok_or_else(|| reference_error(self.code.varnames[idx], vm))?;
5079 self.push_value(x);
5080 Ok(None)
5081 }
5082 Instruction::LoadFastAndClear { var_num } => {
5083 let idx = var_num.get(arg);
5087 let x = self.localsplus.fastlocals_mut()[idx].take();
5088 self.push_value_opt(x);
5089 Ok(None)
5090 }
5091 Instruction::LoadFastCheck { var_num } => {
5092 let idx = var_num.get(arg);
5095 let x = self.localsplus.fastlocals()[idx].clone().ok_or_else(|| {
5096 vm.new_unbound_local_error(format!(
5097 "local variable '{}' referenced before assignment",
5098 self.code.varnames[idx]
5099 ))
5100 })?;
5101 self.push_value(x);
5102 Ok(None)
5103 }
5104 Instruction::LoadFastLoadFast { var_nums } => {
5105 let oparg = var_nums.get(arg);
5108 let (idx1, idx2) = oparg.indexes();
5109 let fastlocals = self.localsplus.fastlocals();
5110 let x1 = fastlocals[idx1].clone().ok_or_else(|| {
5111 vm.new_unbound_local_error(format!(
5112 "local variable '{}' referenced before assignment",
5113 self.code.varnames[idx1]
5114 ))
5115 })?;
5116 let x2 = fastlocals[idx2].clone().ok_or_else(|| {
5117 vm.new_unbound_local_error(format!(
5118 "local variable '{}' referenced before assignment",
5119 self.code.varnames[idx2]
5120 ))
5121 })?;
5122 self.push_value(x1);
5123 self.push_value(x2);
5124 Ok(None)
5125 }
5126 Instruction::LoadFastBorrow { var_num } => {
5127 let idx = var_num.get(arg);
5128 self.push_local(idx.as_usize(), vm)?;
5129 Ok(None)
5130 }
5131 Instruction::LoadFastBorrowLoadFastBorrow { var_nums } => {
5132 let oparg = var_nums.get(arg);
5133 let (idx1, idx2) = oparg.indexes();
5134 self.push_local(idx1.as_usize(), vm)?;
5135 self.push_local(idx2.as_usize(), vm)?;
5136 Ok(None)
5137 }
5138 Instruction::LoadGlobal { namei: idx } => {
5139 let oparg = idx.get(arg);
5140 self.adaptive(|s, ii, cb| s.specialize_load_global(vm, oparg, ii, cb));
5141 let name = &self.code.names[(oparg >> 1) as usize];
5142 let x = self.load_global_or_builtin(name, vm)?;
5143 self.push_value(x);
5144 if (oparg & 1) != 0 {
5145 self.push_value_opt(None);
5146 }
5147 Ok(None)
5148 }
5149 Instruction::LoadName { namei: idx } => {
5150 let name = self.code.names[idx.get(arg) as usize];
5151 let result = self.locals.mapping(vm).subscript(name, vm);
5152 match result {
5153 Ok(x) => self.push_value(x),
5154 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
5155 self.push_value(self.load_global_or_builtin(name, vm)?);
5156 }
5157 Err(e) => return Err(e),
5158 }
5159 Ok(None)
5160 }
5161 Instruction::LoadSpecial { method } => {
5162 use crate::vm::PyMethod;
5165
5166 let obj = self.pop_value();
5167 let oparg = method.get(arg);
5168 let method_name = get_special_method_name(oparg, vm);
5169
5170 match vm.get_special_method(&obj, method_name)? {
5171 Some(PyMethod::Function { target, func }) => {
5172 self.push_value(func); self.push_value(target); }
5175 Some(PyMethod::Attribute(bound)) => {
5176 self.push_value(bound); self.push_null(); }
5179 None => {
5180 return Err(vm.new_type_error(get_special_method_error_msg(
5181 oparg,
5182 &obj.class().name(),
5183 special_method_can_suggest(&obj, oparg, vm)?,
5184 )));
5185 }
5186 };
5187 Ok(None)
5188 }
5189 Instruction::MakeFunction => self.execute_make_function(vm),
5190 Instruction::MakeCell { i } => {
5191 let idx = i.get(arg).as_usize();
5195 let fastlocals = self.localsplus.fastlocals_mut();
5196 let initial = fastlocals[idx].take();
5197 let cell = PyCell::new(initial).into_ref(&vm.ctx).into();
5198 fastlocals[idx] = Some(cell);
5199 Ok(None)
5200 }
5201 Instruction::MapAdd { i } => {
5202 let value = self.pop_value();
5203 let key = self.pop_value();
5204 let obj = self.nth_value(i.get(arg) - 1);
5205 let dict: &Py<PyDict> = unsafe {
5206 obj.downcast_unchecked_ref()
5208 };
5209 dict.set_item(&*key, value, vm)?;
5210 Ok(None)
5211 }
5212 Instruction::MatchClass { count: nargs } => {
5213 let kwd_attrs = self.pop_value();
5216 let kwd_attrs = kwd_attrs.downcast_ref::<PyTuple>().unwrap();
5217 let cls = self.pop_value();
5218 let subject = self.pop_value();
5219 let nargs_val = nargs.get(arg) as usize;
5220
5221 let Some(cls_type) = cls.downcast_ref::<PyType>() else {
5222 return Err(vm.new_type_error("called match pattern must be a class"));
5223 };
5224 let type_name = || cls_type.name().to_string();
5227
5228 if subject.is_instance(cls.as_ref(), vm)? {
5230 let mut extracted = vec![];
5231 let seen_attrs = PySet::default().into_ref(&vm.ctx);
5232
5233 if nargs_val > 0 {
5235 let match_args =
5237 vm.get_attribute_opt(&cls, identifier!(vm, __match_args__))?;
5238
5239 if let Some(match_args) = match_args {
5240 let match_args = match match_args.downcast_exact::<PyTuple>(vm) {
5242 Ok(tuple) => tuple,
5243 Err(match_args) => {
5244 let type_name = type_name();
5246 let match_args_type_name = match_args.class().__name__(vm);
5247 return Err(vm.new_type_error(format!(
5248 "{type_name}.__match_args__ must be a tuple (got {match_args_type_name})"
5249 )));
5250 }
5251 };
5252
5253 if match_args.as_slice().len() < nargs_val {
5255 let type_name = type_name();
5256 let plural = if match_args.as_slice().len() == 1 {
5257 ""
5258 } else {
5259 "s"
5260 };
5261 return Err(vm.new_type_error(format!(
5262 "{type_name}() accepts {} positional sub-pattern{} ({} given)",
5263 match_args.as_slice().len(),
5264 plural,
5265 nargs_val
5266 )));
5267 }
5268
5269 for i in 0..nargs_val {
5271 let attr_name = &match_args.as_slice()[i];
5272 let attr_name_str = match attr_name.downcast_ref::<PyStr>() {
5273 Some(s) => s,
5274 None => {
5275 let attr_type_name = attr_name.class().name();
5276 return Err(vm.new_type_error(format!(
5277 "__match_args__ elements must be strings (got {attr_type_name})"
5278 )));
5279 }
5280 };
5281 if seen_attrs.contains(attr_name.as_object(), vm)? {
5282 let type_name = type_name();
5283 let attr_repr = attr_name.as_object().repr(vm)?;
5284 return Err(vm.new_type_error(format!(
5285 "{type_name}() got multiple sub-patterns for attribute {attr_repr}"
5286 )));
5287 }
5288 seen_attrs.add(attr_name.clone(), vm)?;
5289 match subject.get_attr(attr_name_str, vm) {
5290 Ok(value) => extracted.push(value),
5291 Err(e)
5292 if e.fast_isinstance(vm.ctx.exceptions.attribute_error) =>
5293 {
5294 self.push_value(vm.ctx.none());
5296 return Ok(None);
5297 }
5298 Err(e) => return Err(e),
5299 }
5300 }
5301 } else {
5302 let is_match_self_type =
5306 cls_type.slots.flags.has_feature(PyTypeFlags::_MATCH_SELF);
5307
5308 if is_match_self_type {
5309 if nargs_val == 1 {
5310 extracted.push(subject.clone());
5312 } else if nargs_val > 1 {
5313 let type_name = type_name();
5315 return Err(vm.new_type_error(format!(
5316 "{type_name}() accepts 1 positional sub-pattern ({nargs_val} given)"
5317 )));
5318 }
5319 } else {
5320 if nargs_val > 0 {
5322 let type_name = type_name();
5323 return Err(vm.new_type_error(format!(
5324 "{type_name}() accepts 0 positional sub-patterns ({nargs_val} given)"
5325 )));
5326 }
5327 }
5328 }
5329 }
5330
5331 for name in kwd_attrs {
5333 let name_str = name.downcast_ref::<PyStr>().unwrap();
5334 if seen_attrs.contains(name_str.as_object(), vm)? {
5335 let type_name = type_name();
5336 let attr_repr = name.as_object().repr(vm)?;
5337 return Err(vm.new_type_error(format!(
5338 "{type_name}() got multiple sub-patterns for attribute {attr_repr}"
5339 )));
5340 }
5341 seen_attrs.add(name.clone(), vm)?;
5342 match subject.get_attr(name_str, vm) {
5343 Ok(value) => extracted.push(value),
5344 Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => {
5345 self.push_value(vm.ctx.none());
5346 return Ok(None);
5347 }
5348 Err(e) => return Err(e),
5349 }
5350 }
5351
5352 self.push_value(vm.ctx.new_tuple(extracted).into());
5353 } else {
5354 self.push_value(vm.ctx.none());
5356 }
5357 Ok(None)
5358 }
5359 Instruction::MatchKeys => {
5360 let keys_tuple = self.top_value(); let subject = self.nth_value(1); if subject
5366 .class()
5367 .has_patma_collection_flag(PyTypeFlags::MAPPING)
5368 {
5369 let keys = keys_tuple.downcast_ref::<PyTuple>().unwrap();
5370 let mut values = Vec::new();
5371 let mut all_match = true;
5372 let seen_keys = PySet::default().into_ref(&vm.ctx);
5373
5374 if let Some(get_method) = vm
5381 .get_method(subject.to_owned(), vm.ctx.intern_str("get"))
5382 .transpose()?
5383 {
5384 let dummy = vm
5385 .ctx
5386 .new_base_object(vm.ctx.types.object_type.to_owned(), None);
5387
5388 for key in keys {
5389 if seen_keys.contains(key.as_object(), vm)? {
5390 return Err(vm.new_value_error(format!(
5391 "mapping pattern checks duplicate key ({})",
5392 key.as_object().repr(vm)?
5393 )));
5394 }
5395 seen_keys.add(key.as_object().to_owned(), vm)?;
5396 {
5398 let value =
5399 get_method.call((key.as_object(), dummy.clone()), vm)?;
5400 if value.is(&dummy) {
5402 all_match = false;
5403 break;
5404 }
5405 values.push(value);
5406 }
5407 }
5408 } else {
5409 for key in keys {
5411 if seen_keys.contains(key.as_object(), vm)? {
5412 return Err(vm.new_value_error(format!(
5413 "mapping pattern checks duplicate key ({})",
5414 key.as_object().repr(vm)?
5415 )));
5416 }
5417 seen_keys.add(key.as_object().to_owned(), vm)?;
5418 match subject.get_item(key.as_object(), vm) {
5419 Ok(value) => values.push(value),
5420 Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
5421 all_match = false;
5422 break;
5423 }
5424 Err(e) => return Err(e),
5425 }
5426 }
5427 }
5428
5429 if all_match {
5430 self.push_value(vm.ctx.new_tuple(values).into());
5432 } else {
5433 self.push_value(vm.ctx.none());
5435 }
5436 } else {
5437 self.push_value(vm.ctx.none());
5439 }
5440 Ok(None)
5441 }
5442 Instruction::MatchMapping => {
5443 let subject = self.pop_value();
5445
5446 let is_mapping = subject
5448 .class()
5449 .has_patma_collection_flag(PyTypeFlags::MAPPING);
5450
5451 self.push_value(subject);
5452 self.push_value(vm.ctx.new_bool(is_mapping).into());
5453 Ok(None)
5454 }
5455 Instruction::MatchSequence => {
5456 let subject = self.pop_value();
5458
5459 let is_sequence = subject
5461 .class()
5462 .has_patma_collection_flag(PyTypeFlags::SEQUENCE);
5463
5464 self.push_value(subject);
5465 self.push_value(vm.ctx.new_bool(is_sequence).into());
5466 Ok(None)
5467 }
5468 Instruction::Nop => Ok(None),
5469 Instruction::NotTaken => Ok(None),
5471 Instruction::Cache => Ok(None),
5473 Instruction::ReturnGenerator => {
5474 self.push_value(vm.ctx.none());
5480 Ok(None)
5481 }
5482 Instruction::PopExcept => {
5483 let prev_exc = self.pop_value();
5485 if vm.is_none(&prev_exc) {
5486 vm.set_exception(None);
5487 } else if let Ok(exc) = prev_exc.downcast::<PyBaseException>() {
5488 vm.set_exception(Some(exc));
5489 }
5490
5491 Ok(None)
5499 }
5500 Instruction::PopJumpIfFalse { .. } => self.pop_jump_if_relative(vm, arg, 1, false),
5501 Instruction::PopJumpIfTrue { .. } => self.pop_jump_if_relative(vm, arg, 1, true),
5502 Instruction::PopJumpIfNone { .. } => {
5503 let value = self.pop_stackref();
5504 if vm.is_none(&value) {
5505 self.jump_relative_forward(u32::from(arg), 1);
5506 } else {
5507 self.skip_fallthrough_not_taken(vm, 1);
5508 }
5509 Ok(None)
5510 }
5511 Instruction::PopJumpIfNotNone { .. } => {
5512 let value = self.pop_stackref();
5513 if !vm.is_none(&value) {
5514 self.jump_relative_forward(u32::from(arg), 1);
5515 } else {
5516 self.skip_fallthrough_not_taken(vm, 1);
5517 }
5518 Ok(None)
5519 }
5520 Instruction::PopTop => {
5521 self.pop_stackref();
5523 Ok(None)
5524 }
5525 Instruction::EndFor => {
5526 self.pop_stackref();
5528 Ok(None)
5529 }
5530 Instruction::PopIter => {
5531 self.pop_stackref();
5533 Ok(None)
5534 }
5535 Instruction::PushNull => {
5536 self.push_null();
5538 Ok(None)
5539 }
5540 Instruction::RaiseVarargs { argc: kind } => self.execute_raise(vm, kind.get(arg)),
5541 Instruction::Resume { .. } | Instruction::ResumeCheck => {
5542 if !self.code.quickened.load(atomic::Ordering::Relaxed)
5546 && !self.code.quickened.swap(true, atomic::Ordering::Relaxed)
5547 {
5548 self.code.instructions.quicken();
5549 atomic::fence(atomic::Ordering::Release);
5550 }
5551 let global_ver = vm
5553 .state
5554 .instrumentation_version
5555 .load(atomic::Ordering::Acquire);
5556 let code_ver = self
5557 .code
5558 .instrumentation_version
5559 .load(atomic::Ordering::Acquire);
5560 if code_ver != global_ver {
5561 let events = {
5562 let state = vm.state.monitoring.lock();
5563 state.events_for_code(self.code.get_id())
5564 };
5565 monitoring::instrument_code(self.code, events);
5566 self.code
5567 .instrumentation_version
5568 .store(global_ver, atomic::Ordering::Release);
5569 self.update_lasti(|i| *i -= 1);
5571 } else {
5572 self.trace_call_from_resume(vm, u32::from(arg))?;
5573 }
5574 Ok(None)
5575 }
5576 Instruction::ReturnValue => {
5577 let value = self.pop_value();
5578 if vm.use_tracing.get() {
5579 vm.trace_event_what(
5580 crate::protocol::TraceEvent::Return,
5581 monitoring::MonitoringEvent::PyReturn,
5582 Some(value.clone()),
5583 )?;
5584 }
5585 self.unwind_blocks(vm, UnwindReason::Returning { value })
5586 }
5587 Instruction::SetAdd { i } => {
5588 let item = self.pop_value();
5589 let obj = self.nth_value(i.get(arg) - 1);
5590 let set: &Py<PySet> = unsafe {
5591 obj.downcast_unchecked_ref()
5593 };
5594 set.add(item, vm)?;
5595 Ok(None)
5596 }
5597 Instruction::SetUpdate { i } => {
5598 let iterable = self.pop_value();
5599 let obj = self.nth_value(i.get(arg) - 1);
5600 let set: &Py<PySet> = unsafe {
5601 obj.downcast_unchecked_ref()
5603 };
5604 let iter = PyIter::try_from_object(vm, iterable)?;
5605 while let PyIterReturn::Return(item) = iter.next(vm)? {
5606 set.add(item, vm)?;
5607 }
5608 Ok(None)
5609 }
5610 Instruction::PushExcInfo => {
5611 let exc = self.pop_value();
5613 let prev_exc = vm
5614 .current_exception()
5615 .map_or_else(|| vm.ctx.none(), |e| e.into());
5616
5617 if let Some(exc_ref) = exc.downcast_ref::<PyBaseException>() {
5619 vm.set_exception(Some(exc_ref.to_owned()));
5620 }
5621
5622 self.push_value(prev_exc);
5623 self.push_value(exc);
5624 Ok(None)
5625 }
5626 Instruction::CheckExcMatch => {
5627 let exc_type = self.pop_value();
5629 let exc = self.top_value();
5630
5631 if let Some(tuple_of_exceptions) = exc_type.downcast_ref::<PyTuple>() {
5633 for exception in tuple_of_exceptions {
5634 if !exception
5635 .is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)?
5636 {
5637 return Err(vm.new_type_error(
5638 "catching classes that do not inherit from BaseException is not allowed",
5639 ));
5640 }
5641 }
5642 } else if !exc_type.is_subclass(vm.ctx.exceptions.base_exception_type.into(), vm)? {
5643 return Err(vm.new_type_error(
5644 "catching classes that do not inherit from BaseException is not allowed",
5645 ));
5646 }
5647
5648 let result = exc.is_instance(&exc_type, vm)?;
5649 self.push_value(vm.ctx.new_bool(result).into());
5650 Ok(None)
5651 }
5652 Instruction::Reraise { depth } => {
5653 let oparg = depth.get(arg);
5660 let exc = self.pop_value();
5661 self.restore_reraise_lasti(oparg);
5662
5663 if let Some(exc_ref) = exc.downcast_ref::<PyBaseException>() {
5664 Err(exc_ref.to_owned())
5665 } else {
5666 let exc = vm
5668 .topmost_exception()
5669 .ok_or_else(|| vm.new_runtime_error("No active exception to re-raise"))?;
5670 Err(exc)
5671 }
5672 }
5673 Instruction::SetFunctionAttribute { flag: attr } => {
5674 self.execute_set_function_attribute(vm, attr.get(arg))
5675 }
5676 Instruction::SetupAnnotations => self.setup_annotations(vm),
5677 Instruction::StoreAttr { namei: idx } => {
5678 let idx_val = idx.get(arg);
5679 self.adaptive(|s, ii, cb| s.specialize_store_attr(vm, idx_val, ii, cb));
5680 self.store_attr(vm, idx_val)
5681 }
5682 Instruction::StoreDeref { i } => {
5683 let value = self.pop_value();
5684 self.cell_ref(i.get(arg).as_usize()).set(Some(value));
5685 Ok(None)
5686 }
5687 Instruction::StoreFast { var_num } => {
5688 let value = self.pop_value_opt();
5690 let idx = var_num.get(arg);
5691 self.localsplus.debug_audit_local_release(idx.as_usize());
5692 let fastlocals = self.localsplus.fastlocals_mut();
5693 fastlocals[idx] = value;
5694 Ok(None)
5695 }
5696 Instruction::StoreFastLoadFast { var_nums } => {
5697 let value = self.pop_value_opt();
5699 let oparg = var_nums.get(arg);
5700 let (store_idx, load_idx) = oparg.indexes();
5701 self.localsplus
5702 .debug_audit_local_release(store_idx.as_usize());
5703 let load_value = {
5704 let locals = self.localsplus.fastlocals_mut();
5705 locals[store_idx] = value;
5706 locals[load_idx].clone()
5707 };
5708 self.push_value_opt(load_value);
5709 Ok(None)
5710 }
5711 Instruction::StoreFastStoreFast { var_nums } => {
5712 let oparg = var_nums.get(arg);
5713 let (idx1, idx2) = oparg.indexes();
5714 let value1 = self.pop_value_opt();
5716 let value2 = self.pop_value_opt();
5717 self.localsplus.debug_audit_local_release(idx1.as_usize());
5718 self.localsplus.debug_audit_local_release(idx2.as_usize());
5719 let fastlocals = self.localsplus.fastlocals_mut();
5720 fastlocals[idx1] = value1;
5721 fastlocals[idx2] = value2;
5722 Ok(None)
5723 }
5724 Instruction::StoreGlobal { namei: idx } => {
5725 let value = self.pop_value();
5726 self.globals
5727 .set_item(self.code.names[idx.get(arg) as usize], value, vm)?;
5728 Ok(None)
5729 }
5730 Instruction::StoreName { namei: idx } => {
5731 let name = self.code.names[idx.get(arg) as usize];
5732 let value = self.pop_value();
5733 self.locals
5734 .mapping(vm)
5735 .ass_subscript(name, Some(value), vm)?;
5736 Ok(None)
5737 }
5738 Instruction::StoreSlice => {
5739 let stop = self.pop_value();
5741 let start = self.pop_value();
5742 let container = self.pop_value();
5743 let value = self.pop_value();
5744 let slice: PyObjectRef = PySlice {
5745 start: Some(start),
5746 stop,
5747 step: None,
5748 }
5749 .into_ref(&vm.ctx)
5750 .into();
5751 container.set_item(&*slice, value, vm)?;
5752 Ok(None)
5753 }
5754 Instruction::StoreSubscr => {
5755 self.adaptive(|s, ii, cb| s.specialize_store_subscr(vm, ii, cb));
5756 self.execute_store_subscript(vm)
5757 }
5758 Instruction::Swap { i: index } => {
5759 let len = self.localsplus.stack_len();
5760 debug_assert!(len > 0, "stack underflow in SWAP");
5761 let i = len - 1; let index_val = index.get(arg) as usize;
5763 debug_assert!(
5766 index_val <= len,
5767 "SWAP index {index_val} exceeds stack size {len}"
5768 );
5769 let j = len - index_val;
5770 self.localsplus.stack_swap(i, j);
5771 Ok(None)
5772 }
5773 Instruction::ToBool => {
5774 self.adaptive(|s, ii, cb| s.specialize_to_bool(vm, ii, cb));
5775 let obj = self.pop_stackref();
5776 let bool_val = obj.try_to_bool(vm)?;
5777 self.push_bool_or_fused_jump(instruction.cache_entries(), bool_val, vm);
5778 Ok(None)
5779 }
5780 Instruction::UnpackEx { counts: args } => {
5781 let args = args.get(arg);
5782 self.execute_unpack_ex(vm, args.before, args.after)
5783 }
5784 Instruction::UnpackSequence { count: size } => {
5785 let expected = size.get(arg);
5786 self.adaptive(|s, ii, cb| s.specialize_unpack_sequence(vm, expected, ii, cb));
5787 self.unpack_sequence(expected, vm)
5788 }
5789 Instruction::WithExceptStart => {
5790 let exc = vm.current_exception();
5793
5794 let stack_len = self.localsplus.stack_len();
5795 let exit_func = expect_unchecked(
5796 self.localsplus.stack_index(stack_len - 5),
5797 "WithExceptStart: exit_func is NULL",
5798 );
5799 let self_or_null = self.localsplus.stack_index(stack_len - 4);
5800
5801 let (tp, val, tb) = if let Some(ref exc) = exc {
5802 vm.split_exception(exc.clone())
5803 } else {
5804 (vm.ctx.none(), vm.ctx.none(), vm.ctx.none())
5805 };
5806
5807 let exit_res = if let Some(self_exit) = self_or_null {
5808 exit_func.call((self_exit.clone().to_pyobj(), tp, val, tb), vm)?
5809 } else {
5810 exit_func.call((tp, val, tb), vm)?
5811 };
5812 self.push_value(exit_res);
5813
5814 Ok(None)
5815 }
5816 Instruction::YieldValue { .. } => {
5817 self.localsplus.promote_stack();
5820 debug_assert!(
5821 self.localsplus
5822 .stack_as_slice()
5823 .iter()
5824 .flatten()
5825 .all(|sr| !sr.is_borrowed()),
5826 "borrowed refs on stack at yield point"
5827 );
5828 if self.yield_value_event(vm)? {
5831 return Ok(None);
5832 }
5833 Ok(Some(ExecutionResult::Yield(self.pop_value())))
5834 }
5835 Instruction::Send { .. } => {
5836 self.adaptive(|s, ii, cb| s.specialize_send(vm, ii, cb));
5838 let exit_label = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
5839 let receiver = self.nth_value(1);
5840 let can_fast_send = !self.specialization_eval_frame_active(vm)
5841 && (receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
5842 || receiver.downcast_ref_if_exact::<PyCoroutine>(vm).is_some())
5843 && self
5844 .builtin_coro(receiver)
5845 .is_some_and(|coro| !coro.running() && !coro.closed());
5846 let val = self.pop_value();
5847 let receiver = self.top_value();
5848 let ret = if can_fast_send {
5849 let coro = self.builtin_coro(receiver).unwrap();
5850 if vm.is_none(&val) {
5851 coro.send_none(receiver, vm)?
5852 } else {
5853 coro.send(receiver, val, vm)?
5854 }
5855 } else {
5856 self._send(receiver, val, vm)?
5857 };
5858 match ret {
5859 PyIterReturn::Return(value) => {
5860 self.push_value(value);
5861 Ok(None)
5862 }
5863 PyIterReturn::StopIteration(value) => {
5864 if vm.use_tracing.get() && self.trace_is_set(vm) {
5865 let stop_exc = vm.new_stop_iteration(value.clone());
5866 self.fire_exception_trace(&stop_exc, vm)?;
5867 }
5868 let value = vm.unwrap_or_none(value);
5869 self.push_value(value);
5870 self.jump(exit_label);
5871 Ok(None)
5872 }
5873 }
5874 }
5875 Instruction::SendGen => {
5876 let exit_label = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
5877 let receiver = self.nth_value(1);
5879 let mut started = false;
5880 let can_fast_send = !self.specialization_eval_frame_active(vm)
5881 && (receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
5882 || receiver.downcast_ref_if_exact::<PyCoroutine>(vm).is_some())
5883 && self.builtin_coro(receiver).is_some_and(|coro| {
5884 started = coro.started();
5885 !coro.running() && !coro.closed()
5886 });
5887 let val = self.pop_value();
5888
5889 if can_fast_send {
5890 let receiver = self.top_value();
5891 if self.flatten != Flatten::Nothing
5896 && started
5897 && let Some(cont) = self.send_yield_from_cont(receiver, exit_label)
5898 {
5899 vm.set_pending_gen_resume(receiver.to_owned(), val, cont);
5900 return Ok(Some(ExecutionResult::GenResume));
5901 }
5902 let coro = self.builtin_coro(receiver).unwrap();
5903 let ret = if vm.is_none(&val) {
5904 coro.send_none(receiver, vm)?
5905 } else {
5906 coro.send(receiver, val, vm)?
5907 };
5908 match ret {
5909 PyIterReturn::Return(value) => {
5910 self.push_value(value);
5911 return Ok(None);
5912 }
5913 PyIterReturn::StopIteration(value) => {
5914 if vm.use_tracing.get() && self.trace_is_set(vm) {
5915 let stop_exc = vm.new_stop_iteration(value.clone());
5916 self.fire_exception_trace(&stop_exc, vm)?;
5917 }
5918 let value = vm.unwrap_or_none(value);
5919 self.push_value(value);
5920 self.jump(exit_label);
5921 return Ok(None);
5922 }
5923 }
5924 }
5925 let receiver = self.top_value();
5926 match self._send(receiver, val, vm)? {
5927 PyIterReturn::Return(value) => {
5928 self.push_value(value);
5929 Ok(None)
5930 }
5931 PyIterReturn::StopIteration(value) => {
5932 if vm.use_tracing.get() && self.trace_is_set(vm) {
5933 let stop_exc = vm.new_stop_iteration(value.clone());
5934 self.fire_exception_trace(&stop_exc, vm)?;
5935 }
5936 let value = vm.unwrap_or_none(value);
5937 self.push_value(value);
5938 self.jump(exit_label);
5939 Ok(None)
5940 }
5941 }
5942 }
5943 Instruction::EndSend => {
5944 let value = self.pop_value();
5947 self.pop_stackref(); self.push_value(value);
5949 Ok(None)
5950 }
5951 Instruction::ExitInitCheck => {
5952 let should_be_none = self.pop_value();
5954 if !vm.is_none(&should_be_none) {
5955 return Err(vm.new_type_error(format!(
5956 "__init__() should return None, not '{:.200}'",
5957 should_be_none.class().name()
5958 )));
5959 }
5960 Ok(None)
5961 }
5962 Instruction::CleanupThrow => {
5963 let exc = self.top_value();
5974
5975 if let Some(exc_ref) = exc.downcast_ref::<PyBaseException>()
5977 && exc_ref.fast_isinstance(vm.ctx.exceptions.stop_iteration)
5978 {
5979 let value = exc_ref.get_arg(0).unwrap_or_else(|| vm.ctx.none());
5981 self.pop_stackref(); self.pop_stackref(); self.pop_stackref(); self.push_value(vm.ctx.none());
5986 self.push_value(value);
5987 return Ok(None);
5988 }
5989
5990 let exc = self.pop_value(); self.pop_stackref(); self.pop_stackref(); let exc = exc
5996 .downcast::<PyBaseException>()
5997 .map_err(|_| vm.new_type_error("exception expected"))?;
5998 Err(exc)
5999 }
6000 Instruction::UnaryInvert => {
6001 let a = self.pop_value();
6002 let value = vm._invert(&a)?;
6003 self.push_value(value);
6004 Ok(None)
6005 }
6006 Instruction::UnaryNegative => {
6007 let a = self.pop_value();
6008 let value = vm._neg(&a)?;
6009 self.push_value(value);
6010 Ok(None)
6011 }
6012 Instruction::UnaryNot => {
6013 let obj = self.pop_value();
6014 let value = obj.try_to_bool(vm)?;
6015 self.push_value(vm.ctx.new_bool(!value).into());
6016 Ok(None)
6017 }
6018 Instruction::LoadAttrMethodNoDict => {
6020 let oparg = LoadAttr::from_u32(u32::from(arg));
6021 let cache_base = self.lasti() as usize;
6022
6023 let owner = self.top_value();
6024 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6025
6026 if type_version != 0
6027 && owner.class().tp_version_tag().load(Acquire) == type_version
6028 && let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
6029 {
6030 let owner = self.pop_stackref();
6031 self.push_value(func);
6032 self.push_stackref_opt(Some(owner));
6033 Ok(None)
6034 } else {
6035 self.load_attr_slow(vm, oparg)
6036 }
6037 }
6038 Instruction::LoadAttrMethodLazyDict => {
6039 let oparg = LoadAttr::from_u32(u32::from(arg));
6040 let cache_base = self.lasti() as usize;
6041
6042 let owner = self.top_value();
6043 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6044
6045 if type_version != 0
6046 && owner.class().tp_version_tag().load(Acquire) == type_version
6047 && !owner.has_instance_dict()
6048 && let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
6049 {
6050 let owner = self.pop_stackref();
6051 self.push_value(func);
6052 self.push_stackref_opt(Some(owner));
6053 Ok(None)
6054 } else {
6055 self.load_attr_slow(vm, oparg)
6056 }
6057 }
6058 Instruction::LoadAttrMethodWithValues => {
6059 let oparg = LoadAttr::from_u32(u32::from(arg));
6060 let cache_base = self.lasti() as usize;
6061 let attr_name = self.code.names[oparg.name_idx() as usize];
6062
6063 let owner = self.top_value();
6064 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6065
6066 if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
6067 {
6068 let shadowed = match self.shadowing_instance_attr(cache_base, attr_name, vm) {
6070 Ok(shadowed) => shadowed.is_some(),
6071 Err(_) => return self.load_attr_slow(vm, oparg),
6073 };
6074
6075 if !shadowed
6076 && let Some(func) =
6077 self.try_read_cached_descriptor(cache_base, type_version)
6078 {
6079 let owner = self.pop_stackref();
6080 self.push_value(func);
6081 self.push_stackref_opt(Some(owner));
6082 return Ok(None);
6083 }
6084 }
6085 self.load_attr_slow(vm, oparg)
6086 }
6087 Instruction::LoadAttrInstanceValue => {
6088 let oparg = LoadAttr::from_u32(u32::from(arg));
6089 let cache_base = self.lasti() as usize;
6090 let attr_name = self.code.names[oparg.name_idx() as usize];
6091
6092 let owner = self.top_value();
6093 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6094
6095 if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
6096 {
6097 if let Some(dict) = owner.dict()
6100 && let Some(value) = dict.get_item_opt(attr_name, vm)?
6101 {
6102 self.pop_stackref();
6103 self.push_value(value);
6104 return Ok(None);
6105 }
6106 }
6108 self.load_attr_slow(vm, oparg)
6109 }
6110 Instruction::LoadAttrWithHint => {
6111 let oparg = LoadAttr::from_u32(u32::from(arg));
6112 let cache_base = self.lasti() as usize;
6113 let attr_name = self.code.names[oparg.name_idx() as usize];
6114
6115 let owner = self.top_value();
6116 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6117
6118 if type_version != 0
6119 && owner.class().tp_version_tag().load(Acquire) == type_version
6120 && let Some(dict) = owner.dict()
6121 {
6122 let hint = self.code.instructions.read_cache_u16(cache_base + 3);
6125 if let Some((value, refreshed)) =
6126 dict.get_item_opt_refresh_hint(attr_name, hint, vm)?
6127 {
6128 if let Some(new_hint) = refreshed {
6129 unsafe {
6130 self.code
6131 .instructions
6132 .write_cache_u16(cache_base + 3, new_hint);
6133 }
6134 }
6135 self.pop_stackref();
6136 if oparg.is_method() {
6137 self.push_value(value);
6138 self.push_value_opt(None);
6139 } else {
6140 self.push_value(value);
6141 }
6142 return Ok(None);
6143 }
6144 }
6145
6146 self.load_attr_slow(vm, oparg)
6147 }
6148 Instruction::LoadAttrModule => {
6149 let oparg = LoadAttr::from_u32(u32::from(arg));
6150 let cache_base = self.lasti() as usize;
6151 let attr_name = self.code.names[oparg.name_idx() as usize];
6152 let owner = self.top_value();
6153 let type_version = self.code.instructions.read_cache_ptr(cache_base + 6);
6154 let keys_version = self.code.instructions.read_cache_ptr(cache_base + 7);
6155 let index = self.code.instructions.read_cache_ptr(cache_base + 8);
6156
6157 if type_version != 0
6158 && keys_version != 0
6159 && owner.class().tp_version_tag().load(Acquire) as usize == type_version
6160 && let Some(module) = owner.downcast_ref_if_exact::<PyModule>(vm)
6161 && let Some(value) =
6162 module
6163 .dict()
6164 .get_cached_module_attr(attr_name, keys_version, index, vm)
6165 {
6166 self.pop_stackref();
6167 if oparg.is_method() {
6168 self.push_value(value);
6169 self.push_value_opt(None);
6170 } else {
6171 self.push_value(value);
6172 }
6173 return Ok(None);
6174 }
6175 self.load_attr_slow(vm, oparg)
6176 }
6177 Instruction::LoadAttrNondescriptorNoDict => {
6178 let oparg = LoadAttr::from_u32(u32::from(arg));
6179 let cache_base = self.lasti() as usize;
6180
6181 let owner = self.top_value();
6182 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6183
6184 if type_version != 0
6185 && owner.class().tp_version_tag().load(Acquire) == type_version
6186 && let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
6187 {
6188 self.pop_stackref();
6189 if oparg.is_method() {
6190 self.push_value(attr);
6191 self.push_value_opt(None);
6192 } else {
6193 self.push_value(attr);
6194 }
6195 return Ok(None);
6196 }
6197 self.load_attr_slow(vm, oparg)
6198 }
6199 Instruction::LoadAttrNondescriptorWithValues => {
6200 let oparg = LoadAttr::from_u32(u32::from(arg));
6201 let cache_base = self.lasti() as usize;
6202 let attr_name = self.code.names[oparg.name_idx() as usize];
6203
6204 let owner = self.top_value();
6205 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6206
6207 if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
6208 {
6209 if let Some(value) = self.shadowing_instance_attr(cache_base, attr_name, vm)? {
6211 self.pop_stackref();
6212 if oparg.is_method() {
6213 self.push_value(value);
6214 self.push_value_opt(None);
6215 } else {
6216 self.push_value(value);
6217 }
6218 return Ok(None);
6219 }
6220 let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
6222 else {
6223 return self.load_attr_slow(vm, oparg);
6224 };
6225 self.pop_stackref();
6226 if oparg.is_method() {
6227 self.push_value(attr);
6228 self.push_value_opt(None);
6229 } else {
6230 self.push_value(attr);
6231 }
6232 return Ok(None);
6233 }
6234 self.load_attr_slow(vm, oparg)
6235 }
6236 Instruction::LoadAttrClass => {
6237 let oparg = LoadAttr::from_u32(u32::from(arg));
6238 let cache_base = self.lasti() as usize;
6239
6240 let owner = self.top_value();
6241 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6242
6243 if type_version != 0
6244 && let Some(owner_type) = owner.downcast_ref::<PyType>()
6245 && owner_type.tp_version_tag.load(Acquire) == type_version
6246 && let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
6247 {
6248 self.pop_stackref();
6249 if oparg.is_method() {
6250 self.push_value(attr);
6251 self.push_value_opt(None);
6252 } else {
6253 self.push_value(attr);
6254 }
6255 return Ok(None);
6256 }
6257 self.load_attr_slow(vm, oparg)
6258 }
6259 Instruction::LoadAttrClassWithMetaclassCheck => {
6260 let oparg = LoadAttr::from_u32(u32::from(arg));
6261 let cache_base = self.lasti() as usize;
6262
6263 let owner = self.top_value();
6264 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6265 let metaclass_version = self.code.instructions.read_cache_u32(cache_base + 3);
6266
6267 if type_version != 0
6268 && metaclass_version != 0
6269 && let Some(owner_type) = owner.downcast_ref::<PyType>()
6270 && owner_type.tp_version_tag.load(Acquire) == type_version
6271 && owner.class().tp_version_tag().load(Acquire) == metaclass_version
6272 && let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
6273 {
6274 self.pop_stackref();
6275 if oparg.is_method() {
6276 self.push_value(attr);
6277 self.push_value_opt(None);
6278 } else {
6279 self.push_value(attr);
6280 }
6281 return Ok(None);
6282 }
6283 self.load_attr_slow(vm, oparg)
6284 }
6285 Instruction::LoadAttrGetattributeOverridden => {
6286 let oparg = LoadAttr::from_u32(u32::from(arg));
6287 let cache_base = self.lasti() as usize;
6288 let owner = self.top_value();
6289 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6290 let func_version = self.code.instructions.read_cache_u32(cache_base + 3);
6291
6292 if !oparg.is_method()
6293 && !self.specialization_eval_frame_active(vm)
6294 && type_version != 0
6295 && func_version != 0
6296 && owner.class().tp_version_tag().load(Acquire) == type_version
6297 && let Some(func_obj) =
6298 self.try_read_cached_descriptor(cache_base, type_version)
6299 && let Some(func) = func_obj.downcast_ref_if_exact::<PyFunction>(vm)
6300 && func.func_version() == func_version
6301 && self.specialization_has_datastack_space_for_func(vm, func)
6302 {
6303 debug_assert!(func.has_exact_argcount(2));
6304 let owner = self.pop_value();
6305 let attr_name = self.code.names[oparg.name_idx() as usize].to_owned().into();
6306 let result =
6307 func.invoke_exact_args_slots(&mut [Some(owner), Some(attr_name)], vm)?;
6308 self.push_value(result);
6309 return Ok(None);
6310 }
6311 self.load_attr_slow(vm, oparg)
6312 }
6313 Instruction::LoadAttrSlot => {
6314 let oparg = LoadAttr::from_u32(u32::from(arg));
6315 let cache_base = self.lasti() as usize;
6316
6317 let owner = self.top_value();
6318 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6319
6320 if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
6321 {
6322 let slot_offset =
6323 self.code.instructions.read_cache_u32(cache_base + 3) as i32 as isize;
6324 if let Some(value) = owner.get_slot(slot_offset) {
6325 self.pop_stackref();
6326 if oparg.is_method() {
6327 self.push_value(value);
6328 self.push_value_opt(None);
6329 } else {
6330 self.push_value(value);
6331 }
6332 return Ok(None);
6333 }
6334 }
6336 self.load_attr_slow(vm, oparg)
6337 }
6338 Instruction::LoadAttrProperty => {
6339 let oparg = LoadAttr::from_u32(u32::from(arg));
6340 let cache_base = self.lasti() as usize;
6341
6342 let owner = self.top_value();
6343 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6344
6345 if type_version != 0
6346 && !self.specialization_eval_frame_active(vm)
6347 && owner.class().tp_version_tag().load(Acquire) == type_version
6348 && let Some(fget_obj) =
6349 self.try_read_cached_descriptor(cache_base, type_version)
6350 && let Some(func) = fget_obj.downcast_ref_if_exact::<PyFunction>(vm)
6351 && func.can_specialize_call(1)
6352 && self.specialization_has_datastack_space_for_func(vm, func)
6353 {
6354 let owner = self.pop_value();
6355 let result = func.invoke_exact_args_slots(&mut [Some(owner)], vm)?;
6356 self.push_value(result);
6357 return Ok(None);
6358 }
6359 self.load_attr_slow(vm, oparg)
6360 }
6361 Instruction::StoreAttrInstanceValue => {
6362 let attr_idx = u32::from(arg);
6363 let instr_idx = self.lasti() as usize - 1;
6364 let cache_base = instr_idx + 1;
6365 let attr_name = self.code.names[attr_idx as usize];
6366 let owner = self.top_value();
6367 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6368
6369 if type_version != 0
6370 && owner.class().tp_version_tag().load(Acquire) == type_version
6371 && let Some(dict) = owner.dict()
6372 {
6373 self.pop_stackref(); let value = self.pop_value();
6375 self.store_attr_dict_hinted(&dict, attr_name, value, cache_base, vm)?;
6379 return Ok(None);
6380 }
6381 self.store_attr(vm, attr_idx)
6382 }
6383 Instruction::StoreAttrWithHint => {
6384 let attr_idx = u32::from(arg);
6385 let instr_idx = self.lasti() as usize - 1;
6386 let cache_base = instr_idx + 1;
6387 let attr_name = self.code.names[attr_idx as usize];
6388 let owner = self.top_value();
6389 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6390
6391 if type_version != 0
6392 && owner.class().tp_version_tag().load(Acquire) == type_version
6393 && let Some(dict) = owner.dict()
6394 {
6395 self.pop_stackref(); let value = self.pop_value();
6397 self.store_attr_dict_hinted(&dict, attr_name, value, cache_base, vm)?;
6398 return Ok(None);
6399 }
6400 self.store_attr(vm, attr_idx)
6401 }
6402 Instruction::StoreAttrSlot => {
6403 let instr_idx = self.lasti() as usize - 1;
6404 let cache_base = instr_idx + 1;
6405 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6406 let version_match = type_version != 0 && {
6407 let owner = self.top_value();
6408 owner.class().tp_version_tag().load(Acquire) == type_version
6409 };
6410
6411 if version_match {
6412 let slot_offset =
6413 self.code.instructions.read_cache_u16(cache_base + 3) as i16 as isize;
6414 let owner = self.pop_value();
6415 let value = self.pop_value();
6416 owner.set_slot(slot_offset, Some(value));
6417 return Ok(None);
6418 }
6419 let attr_idx = u32::from(arg);
6420 self.store_attr(vm, attr_idx)
6421 }
6422 Instruction::StoreSubscrListInt => {
6423 let idx = self.pop_stackref();
6425 let obj = self.pop_stackref();
6426 let value = self.pop_value();
6427 if let Some(list) = obj.downcast_ref_if_exact::<PyList>(vm)
6428 && let Some(int_idx) = idx.downcast_ref_if_exact::<PyInt>(vm)
6429 && let Some(i) = specialization_nonnegative_compact_index(int_idx, vm)
6430 {
6431 let mut vec = list.borrow_vec_mut();
6432 if i < vec.len() {
6433 vec[i] = value;
6434 return Ok(None);
6435 }
6436 }
6437 obj.set_item(idx.as_object(), value, vm)?;
6438 Ok(None)
6439 }
6440 Instruction::StoreSubscrDict => {
6441 let idx = self.pop_stackref();
6443 let obj = self.pop_stackref();
6444 let value = self.pop_value();
6445 if let Some(dict) = obj.downcast_ref_if_exact::<PyDict>(vm) {
6446 dict.set_item(idx.as_object(), value, vm)?;
6447 Ok(None)
6448 } else {
6449 obj.set_item(idx.as_object(), value, vm)?;
6450 Ok(None)
6451 }
6452 }
6453 Instruction::BinaryOpAddInt => {
6455 self.execute_binary_op_int(vm, Self::int_add, bytecode::BinaryOperator::Add)
6456 }
6457 Instruction::BinaryOpSubtractInt => {
6458 self.execute_binary_op_int(vm, Self::int_sub, bytecode::BinaryOperator::Subtract)
6459 }
6460 Instruction::BinaryOpMultiplyInt => {
6461 self.execute_binary_op_int(vm, Self::int_mul, bytecode::BinaryOperator::Multiply)
6462 }
6463 Instruction::BinaryOpAddFloat => {
6464 self.execute_binary_op_float(vm, |a, b| a + b, bytecode::BinaryOperator::Add)
6465 }
6466 Instruction::BinaryOpSubtractFloat => {
6467 self.execute_binary_op_float(vm, |a, b| a - b, bytecode::BinaryOperator::Subtract)
6468 }
6469 Instruction::BinaryOpMultiplyFloat => {
6470 self.execute_binary_op_float(vm, |a, b| a * b, bytecode::BinaryOperator::Multiply)
6471 }
6472 Instruction::BinaryOpAddUnicode => {
6473 let b = self.top_value();
6474 let a = self.nth_value(1);
6475 if let (Some(a_str), Some(b_str)) = (
6476 a.downcast_ref_if_exact::<PyStr>(vm),
6477 b.downcast_ref_if_exact::<PyStr>(vm),
6478 ) {
6479 let result = a_str.as_wtf8().py_add(b_str.as_wtf8());
6480 self.pop_stackref();
6481 self.pop_stackref();
6482 self.push_value(result.to_pyobject(vm));
6483 Ok(None)
6484 } else {
6485 self.execute_bin_op(vm, bytecode::BinaryOperator::Add)
6486 }
6487 }
6488 Instruction::BinaryOpSubscrGetitem => {
6489 let cache_base = self.lasti() as usize;
6490 let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
6491 let owner = self.nth_value(1);
6492 if !self.specialization_eval_frame_active(vm)
6493 && type_version != 0
6494 && owner.class().tp_version_tag().load(Acquire) == type_version
6495 && let Some((func, func_version)) =
6496 owner.class().get_cached_getitem_for_specialization()
6497 && func.func_version() == func_version
6498 && self.specialization_has_datastack_space_for_func(vm, &func)
6499 {
6500 debug_assert!(func.has_exact_argcount(2));
6501 let sub = self.pop_value();
6502 let owner = self.pop_value();
6503 let result = func.invoke_exact_args_slots(&mut [Some(owner), Some(sub)], vm)?;
6504 self.push_value(result);
6505 return Ok(None);
6506 }
6507 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6508 }
6509 Instruction::BinaryOpExtend => {
6510 let op = self.binary_op_from_arg(arg);
6511 let b = self.top_value();
6512 let a = self.nth_value(1);
6513 let cache_base = self.lasti() as usize;
6514 if let Some(descr) = self.read_cached_binary_op_extend_descr(cache_base)
6515 && descr.oparg == op
6516 && (descr.guard)(a, b, vm)
6517 && let Some(result) = (descr.action)(a, b, vm)
6518 {
6519 self.pop_stackref();
6520 self.pop_stackref();
6521 self.push_value(result);
6522 Ok(None)
6523 } else {
6524 self.execute_bin_op(vm, op)
6525 }
6526 }
6527 Instruction::BinaryOpSubscrListInt => {
6528 let b = self.top_value();
6529 let a = self.nth_value(1);
6530 if let (Some(list), Some(idx)) = (
6531 a.downcast_ref_if_exact::<PyList>(vm),
6532 b.downcast_ref_if_exact::<PyInt>(vm),
6533 ) && let Some(i) = specialization_nonnegative_compact_index(idx, vm)
6534 {
6535 let vec = list.borrow_vec();
6536 if i < vec.len() {
6537 let value = vec.do_get(i);
6538 drop(vec);
6539 self.pop_stackref();
6540 self.pop_stackref();
6541 self.push_value(value);
6542 return Ok(None);
6543 }
6544 }
6545 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6546 }
6547 Instruction::BinaryOpSubscrTupleInt => {
6548 let b = self.top_value();
6549 let a = self.nth_value(1);
6550 if let (Some(tuple), Some(idx)) = (
6551 a.downcast_ref_if_exact::<PyTuple>(vm),
6552 b.downcast_ref_if_exact::<PyInt>(vm),
6553 ) && let Some(i) = specialization_nonnegative_compact_index(idx, vm)
6554 {
6555 let elements = tuple.as_slice();
6556 if i < elements.len() {
6557 let value = elements[i].clone();
6558 self.pop_stackref();
6559 self.pop_stackref();
6560 self.push_value(value);
6561 return Ok(None);
6562 }
6563 }
6564 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6565 }
6566 Instruction::BinaryOpSubscrDict => {
6567 let b = self.top_value();
6568 let a = self.nth_value(1);
6569 if let Some(dict) = a.downcast_ref_if_exact::<PyDict>(vm) {
6570 match dict.get_item_opt(b, vm) {
6571 Ok(Some(value)) => {
6572 self.pop_stackref();
6573 self.pop_stackref();
6574 self.push_value(value);
6575 return Ok(None);
6576 }
6577 Ok(None) => {
6578 let key = self.pop_value();
6579 self.pop_stackref();
6580 return Err(vm.new_key_error(key));
6581 }
6582 Err(e) => {
6583 return Err(e);
6584 }
6585 }
6586 }
6587 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6588 }
6589 Instruction::BinaryOpSubscrStrInt => {
6590 let b = self.top_value();
6591 let a = self.nth_value(1);
6592 if let (Some(a_str), Some(b_int)) = (
6593 a.downcast_ref_if_exact::<PyStr>(vm),
6594 b.downcast_ref_if_exact::<PyInt>(vm),
6595 ) && let Some(i) = specialization_nonnegative_compact_index(b_int, vm)
6596 && let Ok(ch) = a_str.getitem_by_index(vm, i as isize)
6597 && ch.is_ascii()
6598 {
6599 let ascii_idx = ch.to_u32() as usize;
6600 self.pop_stackref();
6601 self.pop_stackref();
6602 self.push_value(vm.ctx.ascii_char_cache[ascii_idx].clone().into());
6603 return Ok(None);
6604 }
6605 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6606 }
6607 Instruction::BinaryOpSubscrListSlice => {
6608 let b = self.top_value();
6609 let a = self.nth_value(1);
6610 if a.downcast_ref_if_exact::<PyList>(vm).is_some()
6611 && b.downcast_ref::<PySlice>().is_some()
6612 {
6613 let b_owned = self.pop_value();
6614 let a_owned = self.pop_value();
6615 let result = a_owned.get_item(b_owned.as_object(), vm)?;
6616 self.push_value(result);
6617 return Ok(None);
6618 }
6619 self.execute_bin_op(vm, bytecode::BinaryOperator::Subscr)
6620 }
6621 Instruction::CallPyExactArgs => {
6622 let instr_idx = self.lasti() as usize - 1;
6623 let cache_base = instr_idx + 1;
6624 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
6625 let nargs: u32 = arg.into();
6626 if self.specialization_eval_frame_active(vm) {
6627 return self.execute_call_vectorcall(nargs, vm);
6628 }
6629 let stack_len = self.localsplus.stack_len();
6631 let self_or_null_is_some = self
6632 .localsplus
6633 .stack_index(stack_len - nargs as usize - 1)
6634 .is_some();
6635 let callable = self.nth_value(nargs + 1);
6636 if let Some(func) = callable.downcast_ref_if_exact::<PyFunction>(vm)
6637 && func.func_version() == cached_version
6638 && cached_version != 0
6639 {
6640 if func.is_jitted() {
6641 return self.execute_call_vectorcall(nargs, vm);
6642 }
6643 let effective_nargs = nargs + u32::from(self_or_null_is_some);
6644 if !func.has_exact_argcount(effective_nargs) {
6645 return self.execute_call_vectorcall(nargs, vm);
6646 }
6647 if !self.specialization_has_datastack_space_for_func(vm, func) {
6648 return self.execute_call_vectorcall(nargs, vm);
6649 }
6650 if self.specialization_call_recursion_guard(vm) {
6651 return self.execute_call_vectorcall(nargs, vm);
6652 }
6653 if self.flatten == Flatten::CallAndGenResume && !func.is_generator_like() {
6654 self.tailcall_prepare_frame(nargs, self_or_null_is_some, vm);
6655 return Ok(Some(ExecutionResult::TailCall));
6656 }
6657 let base = usize::from(self_or_null_is_some);
6659 let mut arg_buf = CallArgBuffer::new(nargs as usize + base);
6660 let args = arg_buf.slots();
6661 for (slot, arg) in args[base..]
6662 .iter_mut()
6663 .zip(self.pop_multiple(nargs as usize))
6664 {
6665 *slot = Some(arg);
6666 }
6667 let self_or_null = self.pop_value_opt();
6668 debug_assert_eq!(self_or_null.is_some(), self_or_null_is_some);
6669 if self_or_null.is_some() {
6670 args[0] = self_or_null;
6671 }
6672 let callable = self.pop_value();
6673 let func = callable.downcast_ref_if_exact::<PyFunction>(vm).unwrap();
6674 let result = func.invoke_exact_args_slots(args, vm)?;
6675 self.push_value(result);
6676 Ok(None)
6677 } else {
6678 self.execute_call_vectorcall(nargs, vm)
6679 }
6680 }
6681 Instruction::CallBoundMethodExactArgs => {
6682 let instr_idx = self.lasti() as usize - 1;
6683 let cache_base = instr_idx + 1;
6684 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
6685 let nargs: u32 = arg.into();
6686 if self.specialization_eval_frame_active(vm) {
6687 return self.execute_call_vectorcall(nargs, vm);
6688 }
6689 let stack_len = self.localsplus.stack_len();
6691 let self_or_null_is_some = self
6692 .localsplus
6693 .stack_index(stack_len - nargs as usize - 1)
6694 .is_some();
6695 let callable = self.nth_value(nargs + 1);
6696 if !self_or_null_is_some
6697 && let Some(bound_method) = callable.downcast_ref_if_exact::<PyBoundMethod>(vm)
6698 {
6699 let bound_function = bound_method.function_obj().to_owned();
6700 let bound_self = bound_method.self_obj().to_owned();
6701 if let Some(func) = bound_function.downcast_ref_if_exact::<PyFunction>(vm)
6702 && func.func_version() == cached_version
6703 && cached_version != 0
6704 {
6705 if func.is_jitted() {
6706 return self.execute_call_vectorcall(nargs, vm);
6707 }
6708 if !func.has_exact_argcount(nargs + 1) {
6709 return self.execute_call_vectorcall(nargs, vm);
6710 }
6711 if !self.specialization_has_datastack_space_for_func(vm, func) {
6712 return self.execute_call_vectorcall(nargs, vm);
6713 }
6714 if self.specialization_call_recursion_guard(vm) {
6715 return self.execute_call_vectorcall(nargs, vm);
6716 }
6717 if self.flatten == Flatten::CallAndGenResume && !func.is_generator_like() {
6718 self.tailcall_prepare_bound_method_frame(
6719 nargs,
6720 bound_function,
6721 bound_self,
6722 vm,
6723 );
6724 return Ok(Some(ExecutionResult::TailCall));
6725 }
6726 let mut arg_buf = CallArgBuffer::new(nargs as usize + 1);
6729 let args = arg_buf.slots();
6730 for (slot, arg) in
6731 args[1..].iter_mut().zip(self.pop_multiple(nargs as usize))
6732 {
6733 *slot = Some(arg);
6734 }
6735 self.pop_stackref_opt(); self.pop_stackref(); args[0] = Some(bound_self);
6738 let result = func.invoke_exact_args_slots(args, vm)?;
6739 self.push_value(result);
6740 return Ok(None);
6741 }
6742 }
6743 self.execute_call_vectorcall(nargs, vm)
6744 }
6745 Instruction::CallLen => {
6746 let nargs: u32 = arg.into();
6747 if nargs == 1 {
6748 let obj = self.pop_value(); let null = self.pop_value_opt();
6751 let callable = self.pop_value();
6752 if null.is_none()
6753 && vm
6754 .callable_cache
6755 .len
6756 .as_ref()
6757 .is_some_and(|len_callable| callable.is(len_callable))
6758 {
6759 let len = obj.length(vm)?;
6760 self.push_value(vm.ctx.new_int(len).into());
6761 return Ok(None);
6762 }
6763 self.push_value(callable);
6765 self.push_value_opt(null);
6766 self.push_value(obj);
6767 }
6768 self.execute_call_vectorcall(nargs, vm)
6769 }
6770 Instruction::CallIsinstance => {
6771 let nargs: u32 = arg.into();
6772 let stack_len = self.localsplus.stack_len();
6773 let self_or_null_is_some = self
6774 .localsplus
6775 .stack_index(stack_len - nargs as usize - 1)
6776 .is_some();
6777 let effective_nargs = nargs + u32::from(self_or_null_is_some);
6778 if effective_nargs == 2 {
6779 let callable = self.nth_value(nargs + 1);
6780 if vm
6781 .callable_cache
6782 .isinstance
6783 .as_ref()
6784 .is_some_and(|isinstance_callable| callable.is(isinstance_callable))
6785 {
6786 let cls = self.pop_value();
6789 let inst = if nargs == 2 {
6790 let inst = self.pop_value();
6791 self.pop_stackref_opt(); inst
6793 } else {
6794 self.pop_value() };
6796 self.pop_stackref(); let result = inst.is_instance(&cls, vm)?;
6798 self.push_value(vm.ctx.new_bool(result).into());
6799 return Ok(None);
6800 }
6801 }
6802 self.execute_call_vectorcall(nargs, vm)
6803 }
6804 Instruction::CallType1 => {
6805 let nargs: u32 = arg.into();
6806 if nargs == 1 {
6807 let obj = self.pop_value();
6809 let null = self.pop_value_opt();
6810 let callable = self.pop_value();
6811 if null.is_none() && callable.is(vm.ctx.types.type_type.as_object()) {
6812 let tp = obj.class().to_owned().into();
6813 self.push_value(tp);
6814 return Ok(None);
6815 }
6816 self.push_value(callable);
6818 self.push_value_opt(null);
6819 self.push_value(obj);
6820 }
6821 self.execute_call_vectorcall(nargs, vm)
6822 }
6823 Instruction::CallStr1 => {
6824 let nargs: u32 = arg.into();
6825 if nargs == 1 {
6826 let obj = self.pop_value();
6827 let null = self.pop_value_opt();
6828 let callable = self.pop_value();
6829 if null.is_none() && callable.is(vm.ctx.types.str_type.as_object()) {
6830 let result = obj.str(vm)?;
6831 self.push_value(result.into());
6832 return Ok(None);
6833 }
6834 self.push_value(callable);
6835 self.push_value_opt(null);
6836 self.push_value(obj);
6837 }
6838 self.execute_call_vectorcall(nargs, vm)
6839 }
6840 Instruction::CallTuple1 => {
6841 let nargs: u32 = arg.into();
6842 if nargs == 1 {
6843 let obj = self.pop_value();
6844 let null = self.pop_value_opt();
6845 let callable = self.pop_value();
6846 if null.is_none() && callable.is(vm.ctx.types.tuple_type.as_object()) {
6847 if let Ok(tuple) = obj.clone().downcast_exact::<PyTuple>(vm) {
6849 self.push_value(tuple.into_pyref().into());
6850 } else {
6851 let elements: Vec<PyObjectRef> = vm.extract_elements_with(&obj, Ok)?;
6852 self.push_value(vm.ctx.new_tuple(elements).into());
6853 }
6854 return Ok(None);
6855 }
6856 self.push_value(callable);
6857 self.push_value_opt(null);
6858 self.push_value(obj);
6859 }
6860 self.execute_call_vectorcall(nargs, vm)
6861 }
6862 Instruction::CallBuiltinO => {
6863 let nargs: u32 = arg.into();
6864 let stack_len = self.localsplus.stack_len();
6865 let self_or_null_is_some = self
6866 .localsplus
6867 .stack_index(stack_len - nargs as usize - 1)
6868 .is_some();
6869 let effective_nargs = nargs + u32::from(self_or_null_is_some);
6870 let callable = self.nth_value(nargs + 1);
6871 if let Some(native) = callable.downcast_ref_if_exact::<PyNativeFunction>(vm) {
6872 let call_conv = native.value.flags
6873 & (PyMethodFlags::VARARGS
6874 | PyMethodFlags::FASTCALL
6875 | PyMethodFlags::NOARGS
6876 | PyMethodFlags::O
6877 | PyMethodFlags::KEYWORDS);
6878 if call_conv == PyMethodFlags::O && effective_nargs == 1 {
6879 let (callable, args_vec) = self.take_call_args(nargs as usize);
6880 debug_assert_eq!(args_vec.len(), effective_nargs as usize);
6881 let result =
6882 callable.vectorcall(args_vec, effective_nargs as usize, None, vm)?;
6883 self.push_value(result);
6884 return Ok(None);
6885 }
6886 }
6887 self.execute_call_vectorcall(nargs, vm)
6888 }
6889 Instruction::CallBuiltinFast => {
6890 let nargs: u32 = arg.into();
6891 let stack_len = self.localsplus.stack_len();
6892 let self_or_null_is_some = self
6893 .localsplus
6894 .stack_index(stack_len - nargs as usize - 1)
6895 .is_some();
6896 let effective_nargs = nargs + u32::from(self_or_null_is_some);
6897 let callable = self.nth_value(nargs + 1);
6898 if let Some(native) = callable.downcast_ref_if_exact::<PyNativeFunction>(vm) {
6899 let call_conv = native.value.flags
6900 & (PyMethodFlags::VARARGS
6901 | PyMethodFlags::FASTCALL
6902 | PyMethodFlags::NOARGS
6903 | PyMethodFlags::O
6904 | PyMethodFlags::KEYWORDS);
6905 if call_conv == PyMethodFlags::FASTCALL {
6906 let (callable, args_vec) = self.take_call_args(nargs as usize);
6907 debug_assert_eq!(args_vec.len(), effective_nargs as usize);
6908 let result =
6909 callable.vectorcall(args_vec, effective_nargs as usize, None, vm)?;
6910 self.push_value(result);
6911 return Ok(None);
6912 }
6913 }
6914 self.execute_call_vectorcall(nargs, vm)
6915 }
6916 Instruction::CallPyGeneral => {
6917 let instr_idx = self.lasti() as usize - 1;
6918 let cache_base = instr_idx + 1;
6919 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
6920 let nargs: u32 = arg.into();
6921 if self.specialization_eval_frame_active(vm) {
6922 return self.execute_call_vectorcall(nargs, vm);
6923 }
6924 let callable = self.nth_value(nargs + 1);
6925 if let Some(func) = callable.downcast_ref_if_exact::<PyFunction>(vm)
6926 && func.func_version() == cached_version
6927 && cached_version != 0
6928 {
6929 if func.is_jitted() {
6930 return self.execute_call_vectorcall(nargs, vm);
6931 }
6932 if self.specialization_call_recursion_guard(vm) {
6933 return self.execute_call_vectorcall(nargs, vm);
6934 }
6935 let (callable, args_vec) = self.take_call_args(nargs as usize);
6936 let effective_nargs = args_vec.len();
6937 let result =
6938 vectorcall_function(&callable, args_vec, effective_nargs, None, vm)?;
6939 self.push_value(result);
6940 Ok(None)
6941 } else {
6942 self.execute_call_vectorcall(nargs, vm)
6943 }
6944 }
6945 Instruction::CallBoundMethodGeneral => {
6946 let instr_idx = self.lasti() as usize - 1;
6947 let cache_base = instr_idx + 1;
6948 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
6949 let nargs: u32 = arg.into();
6950 if self.specialization_eval_frame_active(vm) {
6951 return self.execute_call_vectorcall(nargs, vm);
6952 }
6953 let stack_len = self.localsplus.stack_len();
6954 let self_or_null_is_some = self
6955 .localsplus
6956 .stack_index(stack_len - nargs as usize - 1)
6957 .is_some();
6958 let callable = self.nth_value(nargs + 1);
6959 if !self_or_null_is_some
6960 && let Some(bound_method) = callable.downcast_ref_if_exact::<PyBoundMethod>(vm)
6961 {
6962 let bound_function = bound_method.function_obj().to_owned();
6963 let bound_self = bound_method.self_obj().to_owned();
6964 if let Some(func) = bound_function.downcast_ref_if_exact::<PyFunction>(vm)
6965 && func.func_version() == cached_version
6966 && cached_version != 0
6967 {
6968 if func.is_jitted() {
6969 return self.execute_call_vectorcall(nargs, vm);
6970 }
6971 if self.specialization_call_recursion_guard(vm) {
6972 return self.execute_call_vectorcall(nargs, vm);
6973 }
6974 let nargs_usize = nargs as usize;
6975 let mut args_vec = Vec::with_capacity(nargs_usize + 1);
6976 args_vec.push(bound_self);
6977 args_vec.extend(self.pop_multiple(nargs_usize));
6978 self.pop_stackref_opt(); self.pop_stackref(); let result = vectorcall_function(
6981 &bound_function,
6982 args_vec,
6983 nargs_usize + 1,
6984 None,
6985 vm,
6986 )?;
6987 self.push_value(result);
6988 return Ok(None);
6989 }
6990 }
6991 self.execute_call_vectorcall(nargs, vm)
6992 }
6993 Instruction::CallListAppend => {
6994 let nargs: u32 = arg.into();
6995 if nargs == 1 {
6996 let stack_len = self.localsplus.stack_len();
6998 let self_or_null_is_some = self.localsplus.stack_index(stack_len - 2).is_some();
6999 let callable = self.nth_value(2);
7000 let self_is_list = self
7001 .localsplus
7002 .stack_index(stack_len - 2)
7003 .as_ref()
7004 .is_some_and(|obj| obj.downcast_ref::<PyList>().is_some());
7005 if vm
7006 .callable_cache
7007 .list_append
7008 .as_ref()
7009 .is_some_and(|list_append| callable.is(list_append))
7010 && self_or_null_is_some
7011 && self_is_list
7012 {
7013 let item = self.pop_value();
7014 let self_or_null = self.pop_value_opt();
7015 let callable = self.pop_value();
7016 if let Some(list_obj) = self_or_null.as_ref()
7017 && let Some(list) = list_obj.downcast_ref::<PyList>()
7018 {
7019 list.append(item);
7020 self.jump_relative_forward(
7022 1,
7023 Instruction::CallListAppend.cache_entries() as u32,
7024 );
7025 return Ok(None);
7026 }
7027 self.push_value(callable);
7028 self.push_value_opt(self_or_null);
7029 self.push_value(item);
7030 }
7031 }
7032 self.execute_call_vectorcall(nargs, vm)
7033 }
7034 Instruction::CallMethodDescriptorNoargs => {
7035 let nargs: u32 = arg.into();
7036 let stack_len = self.localsplus.stack_len();
7037 let self_or_null_is_some = self
7038 .localsplus
7039 .stack_index(stack_len - nargs as usize - 1)
7040 .is_some();
7041 let total_nargs = nargs + u32::from(self_or_null_is_some);
7042 if total_nargs == 1 {
7043 let callable = self.nth_value(nargs + 1);
7044 let self_index =
7045 stack_len - nargs as usize - 1 + usize::from(!self_or_null_is_some);
7046 if let Some(descr) = callable.downcast_ref_if_exact::<PyMethodDescriptor>(vm)
7047 && (descr.method.flags
7048 & (PyMethodFlags::VARARGS
7049 | PyMethodFlags::FASTCALL
7050 | PyMethodFlags::NOARGS
7051 | PyMethodFlags::O
7052 | PyMethodFlags::KEYWORDS))
7053 == PyMethodFlags::NOARGS
7054 && self
7055 .localsplus
7056 .stack_index(self_index)
7057 .as_ref()
7058 .is_some_and(|self_obj| self_obj.class().is(descr.common.typ))
7059 {
7060 let func = descr.method.func;
7061 let callee = Callee::named(descr.method.name).with_instance_arg(true);
7062 let (_callable, all_args) = self.take_call_args(nargs as usize);
7063 debug_assert_eq!(all_args.len(), total_nargs as usize);
7064 let args = FuncArgs {
7065 args: all_args,
7066 kwargs: Default::default(),
7067 };
7068 let result = func(vm, args, callee)?;
7069 self.push_value(result);
7070 return Ok(None);
7071 }
7072 }
7073 self.execute_call_vectorcall(nargs, vm)
7074 }
7075 Instruction::CallMethodDescriptorO => {
7076 let nargs: u32 = arg.into();
7077 let stack_len = self.localsplus.stack_len();
7078 let self_or_null_is_some = self
7079 .localsplus
7080 .stack_index(stack_len - nargs as usize - 1)
7081 .is_some();
7082 let total_nargs = nargs + u32::from(self_or_null_is_some);
7083 if total_nargs == 2 {
7084 let callable = self.nth_value(nargs + 1);
7085 let self_index =
7086 stack_len - nargs as usize - 1 + usize::from(!self_or_null_is_some);
7087 if let Some(descr) = callable.downcast_ref_if_exact::<PyMethodDescriptor>(vm)
7088 && (descr.method.flags
7089 & (PyMethodFlags::VARARGS
7090 | PyMethodFlags::FASTCALL
7091 | PyMethodFlags::NOARGS
7092 | PyMethodFlags::O
7093 | PyMethodFlags::KEYWORDS))
7094 == PyMethodFlags::O
7095 && self
7096 .localsplus
7097 .stack_index(self_index)
7098 .as_ref()
7099 .is_some_and(|self_obj| self_obj.class().is(descr.common.typ))
7100 {
7101 let func = descr.method.func;
7102 let callee = Callee::named(descr.method.name).with_instance_arg(true);
7103 let (_callable, all_args) = self.take_call_args(nargs as usize);
7104 debug_assert_eq!(all_args.len(), total_nargs as usize);
7105 let args = FuncArgs {
7106 args: all_args,
7107 kwargs: Default::default(),
7108 };
7109 let result = func(vm, args, callee)?;
7110 self.push_value(result);
7111 return Ok(None);
7112 }
7113 }
7114 self.execute_call_vectorcall(nargs, vm)
7115 }
7116 Instruction::CallMethodDescriptorFast => {
7117 let nargs: u32 = arg.into();
7118 let stack_len = self.localsplus.stack_len();
7119 let self_or_null_is_some = self
7120 .localsplus
7121 .stack_index(stack_len - nargs as usize - 1)
7122 .is_some();
7123 let total_nargs = nargs + u32::from(self_or_null_is_some);
7124 let callable = self.nth_value(nargs + 1);
7125 let self_index =
7126 stack_len - nargs as usize - 1 + usize::from(!self_or_null_is_some);
7127 if total_nargs > 0
7128 && let Some(descr) = callable.downcast_ref_if_exact::<PyMethodDescriptor>(vm)
7129 && (descr.method.flags
7130 & (PyMethodFlags::VARARGS
7131 | PyMethodFlags::FASTCALL
7132 | PyMethodFlags::NOARGS
7133 | PyMethodFlags::O
7134 | PyMethodFlags::KEYWORDS))
7135 == PyMethodFlags::FASTCALL
7136 && self
7137 .localsplus
7138 .stack_index(self_index)
7139 .as_ref()
7140 .is_some_and(|self_obj| self_obj.class().is(descr.common.typ))
7141 {
7142 let func = descr.method.func;
7143 let callee = Callee::named(descr.method.name).with_instance_arg(true);
7144 let (_callable, all_args) = self.take_call_args(nargs as usize);
7145 debug_assert_eq!(all_args.len(), total_nargs as usize);
7146 let args = FuncArgs {
7147 args: all_args,
7148 kwargs: Default::default(),
7149 };
7150 let result = func(vm, args, callee)?;
7151 self.push_value(result);
7152 return Ok(None);
7153 }
7154 self.execute_call_vectorcall(nargs, vm)
7155 }
7156 Instruction::CallBuiltinClass => {
7157 let nargs: u32 = arg.into();
7158 let callable = self.nth_value(nargs + 1);
7159 if let Some(cls) = callable.downcast_ref::<PyType>()
7160 && cls.slots().vectorcall.load().is_some()
7161 {
7162 let (callable, args_vec) = self.take_call_args(nargs as usize);
7163 let effective_nargs = args_vec.len();
7164 let result = callable.vectorcall(args_vec, effective_nargs, None, vm)?;
7165 self.push_value(result);
7166 return Ok(None);
7167 }
7168 self.execute_call_vectorcall(nargs, vm)
7169 }
7170 Instruction::CallAllocAndEnterInit => {
7171 let instr_idx = self.lasti() as usize - 1;
7172 let cache_base = instr_idx + 1;
7173 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
7174 let nargs: u32 = arg.into();
7175 let callable = self.nth_value(nargs + 1);
7176 let stack_len = self.localsplus.stack_len();
7177 let self_or_null_is_some = self
7178 .localsplus
7179 .stack_index(stack_len - nargs as usize - 1)
7180 .is_some();
7181 if !self.specialization_eval_frame_active(vm)
7182 && !self_or_null_is_some
7183 && cached_version != 0
7184 && let Some(cls) = callable.downcast_ref::<PyType>()
7185 && cls.tp_version_tag().load(Acquire) == cached_version
7186 && let Some((init_func, init_func_version)) =
7187 cls.get_cached_init_for_specialization(cached_version)
7188 && init_func.func_version() == init_func_version
7189 && init_func.has_exact_argcount(nargs + 1)
7190 && let Some(cls_alloc) = cls.slots().alloc.load()
7191 {
7192 if !self.specialization_has_datastack_space_for_func(vm, &init_func) {
7197 return self.execute_call_vectorcall(nargs, vm);
7198 }
7199 if self.specialization_call_recursion_guard(vm) {
7200 return self.execute_call_vectorcall(nargs, vm);
7201 }
7202 let cls_ref = cls.to_owned();
7204 let new_obj = cls_alloc(cls_ref, 0, vm)?;
7205
7206 let mut arg_buf = CallArgBuffer::new(nargs as usize + 1);
7209 let args = arg_buf.slots();
7210 for (slot, arg) in args[1..].iter_mut().zip(self.pop_multiple(nargs as usize)) {
7211 *slot = Some(arg);
7212 }
7213 let _null = self.pop_value_opt(); let _callable = self.pop_value(); let result = self.specialization_run_init(new_obj, &init_func, args, vm)?;
7216 self.push_value(result);
7217 return Ok(None);
7218 }
7219 self.execute_call_vectorcall(nargs, vm)
7220 }
7221 Instruction::CallMethodDescriptorFastWithKeywords => {
7222 let nargs: u32 = arg.into();
7224 let stack_len = self.localsplus.stack_len();
7225 let self_or_null_is_some = self
7226 .localsplus
7227 .stack_index(stack_len - nargs as usize - 1)
7228 .is_some();
7229 let total_nargs = nargs + u32::from(self_or_null_is_some);
7230 let callable = self.nth_value(nargs + 1);
7231 let self_index =
7232 stack_len - nargs as usize - 1 + usize::from(!self_or_null_is_some);
7233 if total_nargs > 0
7234 && let Some(descr) = callable.downcast_ref_if_exact::<PyMethodDescriptor>(vm)
7235 && (descr.method.flags
7236 & (PyMethodFlags::VARARGS
7237 | PyMethodFlags::FASTCALL
7238 | PyMethodFlags::NOARGS
7239 | PyMethodFlags::O
7240 | PyMethodFlags::KEYWORDS))
7241 == (PyMethodFlags::FASTCALL | PyMethodFlags::KEYWORDS)
7242 && self
7243 .localsplus
7244 .stack_index(self_index)
7245 .as_ref()
7246 .is_some_and(|self_obj| self_obj.class().is(descr.common.typ))
7247 {
7248 let func = descr.method.func;
7249 let callee = Callee::named(descr.method.name).with_instance_arg(true);
7250 let (_callable, all_args) = self.take_call_args(nargs as usize);
7251 debug_assert_eq!(all_args.len(), total_nargs as usize);
7252 let args = FuncArgs {
7253 args: all_args,
7254 kwargs: Default::default(),
7255 };
7256 let result = func(vm, args, callee)?;
7257 self.push_value(result);
7258 return Ok(None);
7259 }
7260 self.execute_call_vectorcall(nargs, vm)
7261 }
7262 Instruction::CallBuiltinFastWithKeywords => {
7263 let nargs: u32 = arg.into();
7265 let stack_len = self.localsplus.stack_len();
7266 let self_or_null_is_some = self
7267 .localsplus
7268 .stack_index(stack_len - nargs as usize - 1)
7269 .is_some();
7270 let effective_nargs = nargs + u32::from(self_or_null_is_some);
7271 let callable = self.nth_value(nargs + 1);
7272 if let Some(native) = callable.downcast_ref_if_exact::<PyNativeFunction>(vm) {
7273 let call_conv = native.value.flags
7274 & (PyMethodFlags::VARARGS
7275 | PyMethodFlags::FASTCALL
7276 | PyMethodFlags::NOARGS
7277 | PyMethodFlags::O
7278 | PyMethodFlags::KEYWORDS);
7279 if call_conv == (PyMethodFlags::FASTCALL | PyMethodFlags::KEYWORDS) {
7280 let (callable, args_vec) = self.take_call_args(nargs as usize);
7281 debug_assert_eq!(args_vec.len(), effective_nargs as usize);
7282 let result =
7283 callable.vectorcall(args_vec, effective_nargs as usize, None, vm)?;
7284 self.push_value(result);
7285 return Ok(None);
7286 }
7287 }
7288 self.execute_call_vectorcall(nargs, vm)
7289 }
7290 Instruction::CallNonPyGeneral => {
7291 let nargs: u32 = arg.into();
7292 let stack_len = self.localsplus.stack_len();
7293 let self_or_null_is_some = self
7294 .localsplus
7295 .stack_index(stack_len - nargs as usize - 1)
7296 .is_some();
7297 let callable = self.nth_value(nargs + 1);
7298 if callable.downcast_ref_if_exact::<PyFunction>(vm).is_some()
7299 || callable
7300 .downcast_ref_if_exact::<PyBoundMethod>(vm)
7301 .is_some()
7302 {
7303 return self.execute_call_vectorcall(nargs, vm);
7304 }
7305 let (callable, args_vec) = self.take_call_args(nargs as usize);
7306 debug_assert_eq!(
7307 args_vec.len(),
7308 nargs as usize + usize::from(self_or_null_is_some)
7309 );
7310 let effective_nargs = args_vec.len();
7311 let result = callable.vectorcall(args_vec, effective_nargs, None, vm)?;
7312 self.push_value(result);
7313 Ok(None)
7314 }
7315 Instruction::CallKwPy => {
7316 let instr_idx = self.lasti() as usize - 1;
7317 let cache_base = instr_idx + 1;
7318 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
7319 let nargs: u32 = arg.into();
7320 if self.specialization_eval_frame_active(vm) {
7321 return self.execute_call_kw_vectorcall(nargs, vm);
7322 }
7323 let callable = self.nth_value(nargs + 2);
7325 if let Some(func) = callable.downcast_ref_if_exact::<PyFunction>(vm)
7326 && func.func_version() == cached_version
7327 && cached_version != 0
7328 {
7329 if func.is_jitted() {
7330 return self.execute_call_kw_vectorcall(nargs, vm);
7331 }
7332 if self.specialization_call_recursion_guard(vm) {
7333 return self.execute_call_kw_vectorcall(nargs, vm);
7334 }
7335 let nargs_usize = nargs as usize;
7336 let kwarg_names_obj = self.pop_value();
7337 let kwarg_names_tuple = kwarg_names_obj
7338 .downcast_ref::<PyTuple>()
7339 .expect("kwarg names should be tuple");
7340 let kw_count = kwarg_names_tuple.as_slice().len();
7341 let all_args: Vec<PyObjectRef> = self.pop_multiple(nargs_usize).collect();
7342 let self_or_null = self.pop_value_opt();
7343 let callable = self.pop_value();
7344 let pos_count = nargs_usize - kw_count;
7345 let (args_vec, effective_nargs) = if let Some(self_val) = self_or_null {
7346 let mut v = Vec::with_capacity(nargs_usize + 1);
7347 v.push(self_val);
7348 v.extend(all_args);
7349 (v, pos_count + 1)
7350 } else {
7351 (all_args, pos_count)
7352 };
7353 let kwnames = kwarg_names_tuple.as_slice();
7354 let result = vectorcall_function(
7355 &callable,
7356 args_vec,
7357 effective_nargs,
7358 Some(kwnames),
7359 vm,
7360 )?;
7361 self.push_value(result);
7362 return Ok(None);
7363 }
7364 self.execute_call_kw_vectorcall(nargs, vm)
7365 }
7366 Instruction::CallKwBoundMethod => {
7367 let instr_idx = self.lasti() as usize - 1;
7368 let cache_base = instr_idx + 1;
7369 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
7370 let nargs: u32 = arg.into();
7371 if self.specialization_eval_frame_active(vm) {
7372 return self.execute_call_kw_vectorcall(nargs, vm);
7373 }
7374 let stack_len = self.localsplus.stack_len();
7376 let self_or_null_is_some = self
7377 .localsplus
7378 .stack_index(stack_len - nargs as usize - 2)
7379 .is_some();
7380 let callable = self.nth_value(nargs + 2);
7381 if !self_or_null_is_some
7382 && let Some(bound_method) = callable.downcast_ref_if_exact::<PyBoundMethod>(vm)
7383 {
7384 let bound_function = bound_method.function_obj().to_owned();
7385 let bound_self = bound_method.self_obj().to_owned();
7386 if let Some(func) = bound_function.downcast_ref_if_exact::<PyFunction>(vm)
7387 && func.func_version() == cached_version
7388 && cached_version != 0
7389 {
7390 if func.is_jitted() {
7391 return self.execute_call_kw_vectorcall(nargs, vm);
7392 }
7393 let nargs_usize = nargs as usize;
7394 let kwarg_names_obj = self.pop_value();
7395 let kwarg_names_tuple = kwarg_names_obj
7396 .downcast_ref::<PyTuple>()
7397 .expect("kwarg names should be tuple");
7398 let kw_count = kwarg_names_tuple.as_slice().len();
7399 let all_args: Vec<PyObjectRef> = self.pop_multiple(nargs_usize).collect();
7400 self.pop_stackref_opt(); self.pop_stackref(); let pos_count = nargs_usize - kw_count;
7403 let mut args_vec = Vec::with_capacity(nargs_usize + 1);
7404 args_vec.push(bound_self);
7405 args_vec.extend(all_args);
7406 let kwnames = kwarg_names_tuple.as_slice();
7407 let result = vectorcall_function(
7408 &bound_function,
7409 args_vec,
7410 pos_count + 1,
7411 Some(kwnames),
7412 vm,
7413 )?;
7414 self.push_value(result);
7415 return Ok(None);
7416 }
7417 }
7418 self.execute_call_kw_vectorcall(nargs, vm)
7419 }
7420 Instruction::CallKwNonPy => {
7421 let nargs: u32 = arg.into();
7422 let stack_len = self.localsplus.stack_len();
7423 let self_or_null_is_some = self
7424 .localsplus
7425 .stack_index(stack_len - nargs as usize - 2)
7426 .is_some();
7427 let callable = self.nth_value(nargs + 2);
7428 if callable.downcast_ref_if_exact::<PyFunction>(vm).is_some()
7429 || callable
7430 .downcast_ref_if_exact::<PyBoundMethod>(vm)
7431 .is_some()
7432 {
7433 return self.execute_call_kw_vectorcall(nargs, vm);
7434 }
7435 let nargs_usize = nargs as usize;
7436 let kwarg_names_obj = self.pop_value();
7437 let kwarg_names_tuple = kwarg_names_obj
7438 .downcast_ref::<PyTuple>()
7439 .expect("kwarg names should be tuple");
7440 let kw_count = kwarg_names_tuple.as_slice().len();
7441 let all_args: Vec<PyObjectRef> = self.pop_multiple(nargs_usize).collect();
7442 let self_or_null = self.pop_value_opt();
7443 let callable = self.pop_value();
7444 let pos_count = nargs_usize - kw_count;
7445 let mut args_vec =
7446 Vec::with_capacity(nargs_usize + usize::from(self_or_null_is_some));
7447 if let Some(self_val) = self_or_null {
7448 args_vec.push(self_val);
7449 }
7450 args_vec.extend(all_args);
7451 let result = callable.vectorcall(
7452 args_vec,
7453 pos_count + usize::from(self_or_null_is_some),
7454 Some(kwarg_names_tuple.as_slice()),
7455 vm,
7456 )?;
7457 self.push_value(result);
7458 Ok(None)
7459 }
7460 Instruction::LoadSuperAttrAttr => {
7461 let oparg = u32::from(arg);
7462 let attr_name = self.code.names[(oparg >> 2) as usize];
7463 let self_obj = self.top_value();
7465 let class_obj = self.nth_value(1);
7466 let global_super = self.nth_value(2);
7467 if global_super.is(&vm.ctx.types.super_type.as_object())
7469 && class_obj.downcast_ref::<PyType>().is_some()
7470 {
7471 let class = class_obj.downcast_ref::<PyType>().unwrap();
7472 let start_type = self_obj.class();
7473 let mro: Vec<PyRef<PyType>> = start_type.mro_map_collect(|x| x.to_owned());
7475 let mut found = None;
7476 let mut past_class = false;
7477 for cls in &mro {
7478 if !past_class {
7479 if cls.is(class) {
7480 past_class = true;
7481 }
7482 continue;
7483 }
7484 if let Some(descr) = cls.get_direct_attr(attr_name) {
7485 let obj_arg = if self_obj.is(start_type.as_object()) {
7488 None
7489 } else {
7490 Some(self_obj)
7491 };
7492 let result = vm
7493 .call_get_descriptor_specific(
7494 &descr,
7495 obj_arg,
7496 Some(start_type.as_object()),
7497 )
7498 .unwrap_or(Ok(descr))?;
7499 found = Some(result);
7500 break;
7501 }
7502 }
7503 if let Some(attr) = found {
7504 self.pop_stackref(); self.pop_stackref(); self.pop_stackref(); self.push_value(attr);
7508 return Ok(None);
7509 }
7510 }
7511 let oparg = LoadSuperAttr::from_u32(oparg);
7512 self.load_super_attr(vm, oparg)
7513 }
7514 Instruction::LoadSuperAttrMethod => {
7515 let oparg = u32::from(arg);
7516 let attr_name = self.code.names[(oparg >> 2) as usize];
7517 let self_obj = self.top_value();
7519 let class_obj = self.nth_value(1);
7520 let global_super = self.nth_value(2);
7521 if global_super.is(&vm.ctx.types.super_type.as_object())
7523 && class_obj.downcast_ref::<PyType>().is_some()
7524 {
7525 let class = class_obj.downcast_ref::<PyType>().unwrap();
7526 let self_val = self_obj.to_owned();
7527 let start_type = self_obj.class();
7528 let mro: Vec<PyRef<PyType>> = start_type.mro_map_collect(|x| x.to_owned());
7530 let mut found = None;
7531 let mut past_class = false;
7532 for cls in &mro {
7533 if !past_class {
7534 if cls.is(class) {
7535 past_class = true;
7536 }
7537 continue;
7538 }
7539 if let Some(descr) = cls.get_direct_attr(attr_name) {
7540 let descr_cls = descr.class();
7541 if descr_cls
7542 .slots()
7543 .flags
7544 .has_feature(PyTypeFlags::METHOD_DESCRIPTOR)
7545 {
7546 found = Some((descr, true));
7549 } else if let Some(descr_get) = descr_cls.slots().descr_get.load() {
7550 let bound = descr_get(
7552 &descr,
7553 Some(&self_val),
7554 Some(start_type.as_object()),
7555 vm,
7556 )?;
7557 found = Some((bound, false));
7558 } else {
7559 found = Some((descr, false));
7561 }
7562 break;
7563 }
7564 }
7565 if let Some((attr, is_method)) = found {
7566 self.pop_stackref(); self.pop_stackref(); self.pop_stackref(); self.push_value(attr);
7570 if is_method {
7571 self.push_value(self_val);
7572 } else {
7573 self.push_null();
7574 }
7575 return Ok(None);
7576 }
7577 }
7578 let oparg = LoadSuperAttr::from_u32(oparg);
7579 self.load_super_attr(vm, oparg)
7580 }
7581 Instruction::CompareOpInt => {
7582 let b = self.top_value();
7583 let a = self.nth_value(1);
7584 if let (Some(a_int), Some(b_int)) = (
7585 a.downcast_ref_if_exact::<PyInt>(vm),
7586 b.downcast_ref_if_exact::<PyInt>(vm),
7587 ) && let (Some(a_val), Some(b_val)) = (
7588 specialization_compact_int_value(a_int),
7589 specialization_compact_int_value(b_int),
7590 ) {
7591 let op = self.compare_op_from_arg(arg);
7592 let result = op.eval_ord(a_val.cmp(&b_val));
7593 self.pop_stackref();
7594 self.pop_stackref();
7595 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7596 Ok(None)
7597 } else {
7598 self.execute_compare(vm, arg)
7599 }
7600 }
7601 Instruction::CompareOpFloat => {
7602 let b = self.top_value();
7603 let a = self.nth_value(1);
7604 if let (Some(a_f), Some(b_f)) = (
7605 a.downcast_ref_if_exact::<PyFloat>(vm),
7606 b.downcast_ref_if_exact::<PyFloat>(vm),
7607 ) {
7608 let op = self.compare_op_from_arg(arg);
7609 let (a, b) = (a_f.to_f64(), b_f.to_f64());
7610 let result = match a.partial_cmp(&b) {
7612 Some(ord) => op.eval_ord(ord),
7613 None => op == PyComparisonOp::Ne, };
7615 self.pop_stackref();
7616 self.pop_stackref();
7617 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7618 Ok(None)
7619 } else {
7620 self.execute_compare(vm, arg)
7621 }
7622 }
7623 Instruction::CompareOpStr => {
7624 let b = self.top_value();
7625 let a = self.nth_value(1);
7626 if let (Some(a_str), Some(b_str)) = (
7627 a.downcast_ref_if_exact::<PyStr>(vm),
7628 b.downcast_ref_if_exact::<PyStr>(vm),
7629 ) {
7630 let op = self.compare_op_from_arg(arg);
7631 let Some(result) = op.eval_eq(|| a.is(b) || a_str.as_wtf8() == b_str.as_wtf8())
7635 else {
7636 return self.execute_compare(vm, arg);
7637 };
7638 self.pop_stackref();
7639 self.pop_stackref();
7640 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7641 Ok(None)
7642 } else {
7643 self.execute_compare(vm, arg)
7644 }
7645 }
7646 Instruction::ToBoolBool => {
7647 let obj = self.top_value();
7648 if obj.class().is(vm.ctx.types.bool_type) {
7649 if let Some(jump) = self.fused_bool_jump(instruction.cache_entries(), vm) {
7653 let result = obj.is(&vm.ctx.true_value);
7654 self.pop_stackref();
7655 self.take_fused_bool_jump(jump, result);
7656 }
7657 Ok(None)
7658 } else {
7659 let obj = self.pop_stackref();
7660 let result = obj.try_to_bool(vm)?;
7661 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7662 Ok(None)
7663 }
7664 }
7665 Instruction::ToBoolInt => {
7666 let obj = self.top_value();
7667 if let Some(int_val) = obj.downcast_ref_if_exact::<PyInt>(vm) {
7668 let result = !int_val.as_bigint().is_zero();
7669 self.pop_stackref();
7670 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7671 Ok(None)
7672 } else {
7673 let obj = self.pop_stackref();
7674 let result = obj.try_to_bool(vm)?;
7675 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7676 Ok(None)
7677 }
7678 }
7679 Instruction::ToBoolNone => {
7680 let obj = self.top_value();
7681 if obj.class().is(vm.ctx.types.none_type) {
7682 self.pop_stackref();
7683 self.push_bool_or_fused_jump(instruction.cache_entries(), false, vm);
7684 Ok(None)
7685 } else {
7686 let obj = self.pop_stackref();
7687 let result = obj.try_to_bool(vm)?;
7688 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7689 Ok(None)
7690 }
7691 }
7692 Instruction::ToBoolList => {
7693 let obj = self.top_value();
7694 if let Some(list) = obj.downcast_ref_if_exact::<PyList>(vm) {
7695 let result = !list.borrow_vec().is_empty();
7696 self.pop_stackref();
7697 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7698 Ok(None)
7699 } else {
7700 let obj = self.pop_stackref();
7701 let result = obj.try_to_bool(vm)?;
7702 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7703 Ok(None)
7704 }
7705 }
7706 Instruction::ToBoolStr => {
7707 let obj = self.top_value();
7708 if let Some(s) = obj.downcast_ref_if_exact::<PyStr>(vm) {
7709 let result = !s.is_empty();
7710 self.pop_stackref();
7711 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7712 Ok(None)
7713 } else {
7714 let obj = self.pop_stackref();
7715 let result = obj.try_to_bool(vm)?;
7716 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7717 Ok(None)
7718 }
7719 }
7720 Instruction::ToBoolAlwaysTrue => {
7721 let instr_idx = self.lasti() as usize - 1;
7724 let cache_base = instr_idx + 1;
7725 let obj = self.top_value();
7726 let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
7727 if cached_version != 0
7728 && obj.class().tp_version_tag().load(Acquire) == cached_version
7729 {
7730 self.pop_stackref();
7731 self.push_bool_or_fused_jump(instruction.cache_entries(), true, vm);
7732 Ok(None)
7733 } else {
7734 let obj = self.pop_stackref();
7735 let result = obj.try_to_bool(vm)?;
7736 self.push_bool_or_fused_jump(instruction.cache_entries(), result, vm);
7737 Ok(None)
7738 }
7739 }
7740 Instruction::ContainsOpDict => {
7741 let b = self.top_value(); if let Some(dict) = b.downcast_ref_if_exact::<PyDict>(vm) {
7743 let a = self.nth_value(1); let found = dict.get_item_opt(a, vm)?.is_some();
7745 self.pop_stackref();
7746 self.pop_stackref();
7747 let invert = bytecode::Invert::try_from(u32::from(arg) as u8)
7748 .unwrap_or(bytecode::Invert::No);
7749 let value = match invert {
7750 bytecode::Invert::No => found,
7751 bytecode::Invert::Yes => !found,
7752 };
7753 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
7754 Ok(None)
7755 } else {
7756 let b = self.pop_value();
7757 let a = self.pop_value();
7758 let invert = bytecode::Invert::try_from(u32::from(arg) as u8)
7759 .unwrap_or(bytecode::Invert::No);
7760 let value = match invert {
7761 bytecode::Invert::No => self._in(vm, &a, &b)?,
7762 bytecode::Invert::Yes => self._not_in(vm, &a, &b)?,
7763 };
7764 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
7765 Ok(None)
7766 }
7767 }
7768 Instruction::ContainsOpSet => {
7769 let b = self.top_value(); if b.downcast_ref_if_exact::<PySet>(vm).is_some()
7771 || b.downcast_ref_if_exact::<PyFrozenSet>(vm).is_some()
7772 {
7773 let a = self.nth_value(1); let found = vm._contains(b, a)?;
7775 self.pop_stackref();
7776 self.pop_stackref();
7777 let invert = bytecode::Invert::try_from(u32::from(arg) as u8)
7778 .unwrap_or(bytecode::Invert::No);
7779 let value = match invert {
7780 bytecode::Invert::No => found,
7781 bytecode::Invert::Yes => !found,
7782 };
7783 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
7784 Ok(None)
7785 } else {
7786 let b = self.pop_value();
7787 let a = self.pop_value();
7788 let invert = bytecode::Invert::try_from(u32::from(arg) as u8)
7789 .unwrap_or(bytecode::Invert::No);
7790 let value = match invert {
7791 bytecode::Invert::No => self._in(vm, &a, &b)?,
7792 bytecode::Invert::Yes => self._not_in(vm, &a, &b)?,
7793 };
7794 self.push_bool_or_fused_jump(instruction.cache_entries(), value, vm);
7795 Ok(None)
7796 }
7797 }
7798 Instruction::UnpackSequenceTwoTuple => {
7799 let obj = self.top_value();
7800 if let Some(tuple) = obj.downcast_ref_if_exact::<PyTuple>(vm) {
7801 let elements = tuple.as_slice();
7802 if elements.len() == 2 {
7803 let e0 = elements[0].clone();
7804 let e1 = elements[1].clone();
7805 self.pop_stackref();
7806 self.push_value(e1);
7807 self.push_value(e0);
7808 return Ok(None);
7809 }
7810 }
7811 let size = u32::from(arg);
7812 self.unpack_sequence(size, vm)
7813 }
7814 Instruction::UnpackSequenceTuple => {
7815 let size = u32::from(arg) as usize;
7816 let obj = self.top_value();
7817 if let Some(tuple) = obj.downcast_ref_if_exact::<PyTuple>(vm) {
7818 let elements = tuple.as_slice();
7819 if elements.len() == size {
7820 let elems: Vec<_> = elements.to_vec();
7821 self.pop_stackref();
7822 for elem in elems.into_iter().rev() {
7823 self.push_value(elem);
7824 }
7825 return Ok(None);
7826 }
7827 }
7828 self.unpack_sequence(size as u32, vm)
7829 }
7830 Instruction::UnpackSequenceList => {
7831 let size = u32::from(arg) as usize;
7832 let obj = self.top_value();
7833 if let Some(list) = obj.downcast_ref_if_exact::<PyList>(vm) {
7834 let vec = list.borrow_vec();
7835 if vec.len() == size {
7836 let elems: Vec<_> = vec.to_vec();
7837 drop(vec);
7838 self.pop_stackref();
7839 for elem in elems.into_iter().rev() {
7840 self.push_value(elem);
7841 }
7842 return Ok(None);
7843 }
7844 }
7845 self.unpack_sequence(size as u32, vm)
7846 }
7847 Instruction::ForIterRange => {
7848 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
7849 let iter = self.top_value();
7850 if let Some(range_iter) = iter.downcast_ref_if_exact::<PyRangeIterator>(vm) {
7851 if let Some(value) = range_iter.fast_next() {
7852 self.push_value(vm.ctx.new_int(value).into());
7853 } else {
7854 self.for_iter_jump_on_exhausted(target);
7855 }
7856 Ok(None)
7857 } else {
7858 self.execute_for_iter(vm, target)?;
7859 Ok(None)
7860 }
7861 }
7862 Instruction::ForIterList => {
7863 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
7864 let iter = self.top_value();
7865 if let Some(list_iter) = iter.downcast_ref_if_exact::<PyListIterator>(vm) {
7866 if let Some(value) = list_iter.fast_next() {
7867 self.push_value(value);
7868 } else {
7869 self.for_iter_jump_on_exhausted(target);
7870 }
7871 Ok(None)
7872 } else {
7873 self.execute_for_iter(vm, target)?;
7874 Ok(None)
7875 }
7876 }
7877 Instruction::ForIterTuple => {
7878 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
7879 let iter = self.top_value();
7880 if let Some(tuple_iter) = iter.downcast_ref_if_exact::<PyTupleIterator>(vm) {
7881 if let Some(value) = tuple_iter.fast_next() {
7882 self.push_value(value);
7883 } else {
7884 self.for_iter_jump_on_exhausted(target);
7885 }
7886 Ok(None)
7887 } else {
7888 self.execute_for_iter(vm, target)?;
7889 Ok(None)
7890 }
7891 }
7892 Instruction::ForIterGen => {
7893 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
7894 let iter = self.top_value();
7895 if self.specialization_eval_frame_active(vm) {
7896 self.execute_for_iter(vm, target)?;
7897 return Ok(None);
7898 }
7899 if let Some(generator) = iter.downcast_ref_if_exact::<PyGenerator>(vm) {
7900 if generator.as_coro().running() || generator.as_coro().closed() {
7901 self.execute_for_iter(vm, target)?;
7902 return Ok(None);
7903 }
7904 match generator.as_coro().send_none(iter, vm) {
7905 Ok(PyIterReturn::Return(value)) => {
7906 self.push_value(value);
7907 }
7908 Ok(PyIterReturn::StopIteration(value)) => {
7909 if vm.state.monitoring_events.load()
7912 & MonitoringEvent::StopIteration.mask()
7913 != 0
7914 {
7915 let offset = (self.lasti() - 1) * 2;
7916 let val = vm.unwrap_or_none(value.clone());
7917 monitoring::fire_stop_iteration(vm, self.code, offset, &val)?;
7918 }
7919 if vm.use_tracing.get() && self.trace_is_set(vm) {
7920 let stop_exc = vm.new_stop_iteration(value);
7921 self.fire_exception_trace(&stop_exc, vm)?;
7922 }
7923 self.for_iter_jump_on_exhausted(target);
7924 }
7925 Err(e) => return Err(e),
7926 }
7927 Ok(None)
7928 } else {
7929 self.execute_for_iter(vm, target)?;
7930 Ok(None)
7931 }
7932 }
7933 Instruction::LoadGlobalModule => {
7934 let oparg = u32::from(arg);
7935 let cache_base = self.lasti() as usize;
7936 let cached_version = self.code.instructions.read_cache_u16(cache_base + 1);
7938 let cached_index = self.code.instructions.read_cache_u16(cache_base + 3);
7939 if cached_version != 0
7940 && let Some(x) = self
7941 .globals
7942 .get_item_by_index_and_keys_version(cached_version, cached_index)
7943 {
7944 self.push_value(x);
7945 if (oparg & 1) != 0 {
7946 self.push_value_opt(None);
7947 }
7948 return Ok(None);
7949 }
7950 let name = self.code.names[(oparg >> 1) as usize];
7951 let x = self.load_global_or_builtin(name, vm)?;
7952 self.push_value(x);
7953 if (oparg & 1) != 0 {
7954 self.push_value_opt(None);
7955 }
7956 Ok(None)
7957 }
7958 Instruction::LoadGlobalBuiltin => {
7959 let oparg = u32::from(arg);
7960 let cache_base = self.lasti() as usize;
7961 let cached_globals_ver = self.code.instructions.read_cache_u16(cache_base + 1);
7962 let cached_builtins_ver = self.code.instructions.read_cache_u16(cache_base + 2);
7963 let cached_index = self.code.instructions.read_cache_u16(cache_base + 3);
7964 if cached_globals_ver != 0
7965 && cached_builtins_ver != 0
7966 && let Ok(current_globals_ver) = u16::try_from(self.globals.keys_version())
7967 && cached_globals_ver == current_globals_ver
7968 && let Some(builtins_dict) = self.builtins.downcast_ref_if_exact::<PyDict>(vm)
7969 && let Some(x) = builtins_dict
7970 .get_item_by_index_and_keys_version(cached_builtins_ver, cached_index)
7971 {
7972 self.push_value(x);
7973 if (oparg & 1) != 0 {
7974 self.push_value_opt(None);
7975 }
7976 return Ok(None);
7977 }
7978 let name = self.code.names[(oparg >> 1) as usize];
7979 let x = self.load_global_or_builtin(name, vm)?;
7980 self.push_value(x);
7981 if (oparg & 1) != 0 {
7982 self.push_value_opt(None);
7983 }
7984 Ok(None)
7985 }
7986 _ => self.execute_instrumented(instruction, arg, vm),
7989 }
7990 }
7991
7992 #[cold]
7995 fn execute_instrumented(
7996 &mut self,
7997 instruction: Instruction,
7998 arg: bytecode::OpArg,
7999 vm: &VirtualMachine,
8000 ) -> FrameResult {
8001 debug_assert!(
8002 instruction.is_instrumented(),
8003 "execute_instrumented called with non-instrumented opcode {instruction:?}"
8004 );
8005 if !matches!(
8015 instruction,
8016 Instruction::InstrumentedResume | Instruction::InstrumentedLine
8017 ) {
8018 let idx = self.lasti() as usize - 1;
8019 if let Some((loc, _)) = self.code.locations.get(idx) {
8020 self.prev_line.set(loc.line.get() as u32);
8021 }
8022 }
8023 self.monitoring_mask = vm.state.monitoring_events.load();
8024 match instruction {
8025 Instruction::InstrumentedResume => {
8026 let global_ver = vm
8028 .state
8029 .instrumentation_version
8030 .load(atomic::Ordering::Acquire);
8031 let code_ver = self
8032 .code
8033 .instrumentation_version
8034 .load(atomic::Ordering::Acquire);
8035 if code_ver != global_ver {
8036 let events = {
8037 let state = vm.state.monitoring.lock();
8038 state.events_for_code(self.code.get_id())
8039 };
8040 monitoring::instrument_code(self.code, events);
8041 self.code
8042 .instrumentation_version
8043 .store(global_ver, atomic::Ordering::Release);
8044 self.update_lasti(|i| *i -= 1);
8046 return Ok(None);
8047 }
8048 let resume_type = u32::from(arg);
8049 let offset = (self.lasti() - 1) * 2;
8050 if resume_type == 0 {
8051 if self.monitoring_mask & MonitoringEvent::PyStart.mask() != 0 {
8052 monitoring::fire_py_start(vm, self.code, offset)?;
8053 }
8054 } else if self.monitoring_mask & MonitoringEvent::PyResume.mask() != 0 {
8055 monitoring::fire_py_resume(vm, self.code, offset)?;
8056 }
8057 self.trace_call_from_resume(vm, resume_type)?;
8058 Ok(None)
8059 }
8060 Instruction::InstrumentedReturnValue => {
8061 let value = self.pop_value();
8062 if vm.use_tracing.get() {
8063 vm.trace_event_what(
8064 crate::protocol::TraceEvent::Return,
8065 monitoring::MonitoringEvent::PyReturn,
8066 Some(value.clone()),
8067 )?;
8068 }
8069 if self.monitoring_mask & MonitoringEvent::PyReturn.mask() != 0 {
8070 let offset = (self.lasti() - 1) * 2;
8071 monitoring::fire_py_return(vm, self.code, offset, &value)?;
8072 }
8073 self.unwind_blocks(vm, UnwindReason::Returning { value })
8074 }
8075 Instruction::InstrumentedYieldValue => {
8076 self.localsplus.promote_stack();
8077 debug_assert!(
8078 self.localsplus
8079 .stack_as_slice()
8080 .iter()
8081 .flatten()
8082 .all(|sr| !sr.is_borrowed()),
8083 "borrowed refs on stack at yield point"
8084 );
8085 if self.yield_value_event(vm)? {
8086 return Ok(None);
8087 }
8088 Ok(Some(ExecutionResult::Yield(self.pop_value())))
8089 }
8090 Instruction::InstrumentedCall => {
8091 let args = self.collect_positional_args(u32::from(arg));
8092 self.execute_call_instrumented(args, vm)
8093 }
8094 Instruction::InstrumentedCallKw => {
8095 let args = self.collect_keyword_args(u32::from(arg));
8096 self.execute_call_instrumented(args, vm)
8097 }
8098 Instruction::InstrumentedCallFunctionEx => {
8099 let args = self.collect_ex_args(vm)?;
8100 self.execute_call_instrumented(args, vm)
8101 }
8102 Instruction::InstrumentedLoadSuperAttr => {
8103 let oparg = bytecode::LoadSuperAttr::from(u32::from(arg));
8104 let offset = (self.lasti() - 1) * 2;
8105 let call_args = if self.monitoring_mask & MonitoringEvent::Call.mask() != 0 {
8107 let global_super: PyObjectRef = self.nth_value(2).to_owned();
8108 let arg0 = if oparg.has_class() {
8109 self.nth_value(1).to_owned()
8110 } else {
8111 monitoring::get_missing(vm)
8112 };
8113 monitoring::fire_call(vm, self.code, offset, &global_super, arg0.clone())?;
8114 Some((global_super, arg0))
8115 } else {
8116 None
8117 };
8118 match self.load_super_attr(vm, oparg) {
8119 Ok(result) => {
8120 if let Some((global_super, arg0)) = call_args {
8122 monitoring::fire_c_return(vm, self.code, offset, &global_super, arg0)?;
8123 }
8124 Ok(result)
8125 }
8126 Err(exc) => {
8127 let exc = if let Some((global_super, arg0)) = call_args {
8129 match monitoring::fire_c_raise(
8130 vm,
8131 self.code,
8132 offset,
8133 &global_super,
8134 arg0,
8135 ) {
8136 Ok(()) => exc,
8137 Err(monitor_exc) => monitor_exc,
8138 }
8139 } else {
8140 exc
8141 };
8142 Err(exc)
8143 }
8144 }
8145 }
8146 Instruction::InstrumentedJumpForward => {
8147 let src_offset = (self.lasti() - 1) * 2;
8148 let target_idx = self.lasti() + u32::from(arg);
8149 let target = bytecode::Label::from_u32(target_idx);
8150 self.jump(target);
8151 if self.monitoring_mask & MonitoringEvent::Jump.mask() != 0 {
8152 monitoring::fire_jump(vm, self.code, src_offset, target.as_u32() * 2)?;
8153 }
8154 Ok(None)
8155 }
8156 Instruction::InstrumentedJumpBackward => {
8157 let src_offset = (self.lasti() - 1) * 2;
8158 let from_idx = self.lasti().saturating_sub(1) as usize;
8159 let target_idx = self.lasti() + 1 - u32::from(arg);
8160 let target = bytecode::Label::from_u32(target_idx);
8161 self.jump(target);
8162 if self.monitoring_mask & MonitoringEvent::Jump.mask() != 0 {
8163 monitoring::fire_jump(vm, self.code, src_offset, target.as_u32() * 2)?;
8164 }
8165 self.trace_backward_same_line(from_idx, vm)?;
8166 Ok(None)
8167 }
8168 Instruction::InstrumentedForIter => {
8169 let src_offset = (self.lasti() - 1) * 2;
8170 let target = bytecode::Label::from_u32(self.lasti() + 1 + u32::from(arg));
8171 let continued = self.execute_for_iter(vm, target)?;
8172 if continued {
8173 if self.monitoring_mask & MonitoringEvent::BranchLeft.mask() != 0 {
8174 let dest_offset = (self.lasti() + 1) * 2; monitoring::fire_branch_left(vm, self.code, src_offset, dest_offset)?;
8176 }
8177 } else if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8178 let dest_offset = (self.lasti() + 1) * 2;
8181 monitoring::fire_branch_right(vm, self.code, src_offset, dest_offset)?;
8182 }
8183 Ok(None)
8184 }
8185 Instruction::InstrumentedEndFor => {
8186 let is_gen = self
8189 .nth_value(1)
8190 .downcast_ref::<crate::builtins::PyGenerator>()
8191 .is_some();
8192 let value = self.pop_value();
8193 if is_gen && self.monitoring_mask & MonitoringEvent::StopIteration.mask() != 0 {
8194 let offset = (self.lasti() - 1) * 2;
8195 monitoring::fire_stop_iteration(vm, self.code, offset, &value)?;
8196 }
8197 Ok(None)
8198 }
8199 Instruction::InstrumentedEndSend => {
8200 let value = self.pop_value();
8201 let receiver = self.pop_value();
8202 let is_gen_or_coro = receiver
8204 .downcast_ref::<crate::builtins::PyGenerator>()
8205 .is_some()
8206 || receiver
8207 .downcast_ref::<crate::builtins::PyCoroutine>()
8208 .is_some();
8209 if is_gen_or_coro
8210 && self.monitoring_mask & MonitoringEvent::StopIteration.mask() != 0
8211 {
8212 let offset = (self.lasti() - 1) * 2;
8213 monitoring::fire_stop_iteration(vm, self.code, offset, &value)?;
8214 }
8215 self.push_value(value);
8216 Ok(None)
8217 }
8218 Instruction::InstrumentedPopJumpIfTrue => {
8219 let src_offset = (self.lasti() - 1) * 2;
8220 let target_idx = self.lasti() + 1 + u32::from(arg);
8221 let obj = self.pop_value();
8222 let value = obj.try_to_bool(vm)?;
8223 if value {
8224 self.jump(bytecode::Label::from_u32(target_idx));
8225 if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8226 monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
8227 }
8228 }
8229 Ok(None)
8230 }
8231 Instruction::InstrumentedPopJumpIfFalse => {
8232 let src_offset = (self.lasti() - 1) * 2;
8233 let target_idx = self.lasti() + 1 + u32::from(arg);
8234 let obj = self.pop_value();
8235 let value = obj.try_to_bool(vm)?;
8236 if !value {
8237 self.jump(bytecode::Label::from_u32(target_idx));
8238 if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8239 monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
8240 }
8241 }
8242 Ok(None)
8243 }
8244 Instruction::InstrumentedPopJumpIfNone => {
8245 let src_offset = (self.lasti() - 1) * 2;
8246 let target_idx = self.lasti() + 1 + u32::from(arg);
8247 let value = self.pop_value();
8248 if vm.is_none(&value) {
8249 self.jump(bytecode::Label::from_u32(target_idx));
8250 if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8251 monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
8252 }
8253 }
8254 Ok(None)
8255 }
8256 Instruction::InstrumentedPopJumpIfNotNone => {
8257 let src_offset = (self.lasti() - 1) * 2;
8258 let target_idx = self.lasti() + 1 + u32::from(arg);
8259 let value = self.pop_value();
8260 if !vm.is_none(&value) {
8261 self.jump(bytecode::Label::from_u32(target_idx));
8262 if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8263 monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
8264 }
8265 }
8266 Ok(None)
8267 }
8268 Instruction::InstrumentedNotTaken => {
8269 if self.monitoring_mask & MonitoringEvent::BranchLeft.mask() != 0 {
8270 let not_taken_idx = self.lasti() as usize - 1;
8271 let mut branch_idx = not_taken_idx.saturating_sub(1);
8273 while branch_idx > 0
8274 && matches!(
8275 self.code.instructions.read_op(branch_idx),
8276 Instruction::Cache
8277 )
8278 {
8279 branch_idx -= 1;
8280 }
8281 let src_offset = (branch_idx as u32) * 2;
8282 let dest_offset = self.lasti() * 2;
8283 monitoring::fire_branch_left(vm, self.code, src_offset, dest_offset)?;
8284 }
8285 Ok(None)
8286 }
8287 Instruction::InstrumentedPopIter => {
8288 self.pop_stackref();
8290 Ok(None)
8291 }
8292 Instruction::InstrumentedEndAsyncFor => {
8293 if self.monitoring_mask & MonitoringEvent::BranchRight.mask() != 0 {
8294 let oparg_val = u32::from(arg);
8295 let src_offset = (self.lasti() - oparg_val) * 2;
8297 let dest_offset = self.lasti() * 2;
8299 monitoring::fire_branch_right(vm, self.code, src_offset, dest_offset)?;
8300 }
8301 let exc = self.pop_value();
8302 let _awaitable = self.pop_value();
8303 let exc = exc
8304 .downcast::<PyBaseException>()
8305 .expect("EndAsyncFor expects exception on stack");
8306 if exc.fast_isinstance(vm.ctx.exceptions.stop_async_iteration) {
8307 vm.set_exception(None);
8308 Ok(None)
8309 } else {
8310 Err(exc)
8311 }
8312 }
8313 Instruction::InstrumentedLine => {
8314 let idx = self.lasti() as usize - 1;
8315 let offset = idx as u32 * 2;
8316
8317 let (real_op_byte, also_instruction) = {
8320 let data = self.code.monitoring_data.lock();
8321 let line_op = data.as_ref().map_or(0, |d| d.line_opcodes[idx]);
8322 if line_op == u8::from(Instruction::InstrumentedInstruction) {
8323 let inst_op = data.as_ref().map_or(0, |d| d.per_instruction_opcodes[idx]);
8325 (inst_op, true)
8326 } else {
8327 (line_op, false)
8328 }
8329 };
8330 debug_assert!(
8331 real_op_byte != 0,
8332 "INSTRUMENTED_LINE at {idx} without stored opcode"
8333 );
8334
8335 if let Some((loc, _)) = self.code.locations.get(idx) {
8337 let line = loc.line.get() as u32;
8338 if line != self.prev_line.get() && line > 0 {
8339 self.prev_line.set(line);
8340 monitoring::fire_line(vm, self.code, offset, line)?;
8341 }
8342 }
8343
8344 if also_instruction {
8346 monitoring::fire_instruction(vm, self.code, offset)?;
8347 }
8348
8349 let original_op = Instruction::try_from(real_op_byte)
8359 .expect("invalid opcode in side-table chain");
8360 let lasti_before_dispatch = self.lasti();
8361 let result = if original_op.to_base().is_some() {
8362 self.execute_instrumented(original_op, arg, vm)
8363 } else {
8364 let mut do_extend_arg = false;
8365 self.execute_instruction(original_op, arg, &mut do_extend_arg, vm)
8366 };
8367 let orig_caches = original_op.to_base().unwrap_or(original_op).cache_entries();
8368 if orig_caches > 0 && self.lasti() == lasti_before_dispatch {
8369 self.update_lasti(|i| *i += orig_caches as u32);
8370 }
8371 result
8372 }
8373 Instruction::InstrumentedInstruction => {
8374 let idx = self.lasti() as usize - 1;
8375 let offset = idx as u32 * 2;
8376
8377 let original_op_byte = {
8379 let data = self.code.monitoring_data.lock();
8380 data.as_ref().map_or(0, |d| d.per_instruction_opcodes[idx])
8381 };
8382 debug_assert!(
8383 original_op_byte != 0,
8384 "INSTRUMENTED_INSTRUCTION at {idx} without stored opcode"
8385 );
8386
8387 monitoring::fire_instruction(vm, self.code, offset)?;
8389
8390 let original_op = Instruction::try_from(original_op_byte)
8392 .expect("invalid opcode in instruction side-table");
8393 let lasti_before_dispatch = self.lasti();
8394 let result = if original_op.to_base().is_some() {
8395 self.execute_instrumented(original_op, arg, vm)
8396 } else {
8397 let mut do_extend_arg = false;
8398 self.execute_instruction(original_op, arg, &mut do_extend_arg, vm)
8399 };
8400 let orig_caches = original_op.to_base().unwrap_or(original_op).cache_entries();
8401 if orig_caches > 0 && self.lasti() == lasti_before_dispatch {
8402 self.update_lasti(|i| *i += orig_caches as u32);
8403 }
8404 result
8405 }
8406 _ => {
8407 unreachable!("{instruction:?} instruction should not be executed")
8408 }
8409 }
8410 }
8411
8412 #[inline]
8413 fn mapping_get_optional(
8414 &self,
8415 mapping: &PyObject,
8416 name: &Py<PyStr>,
8417 vm: &VirtualMachine,
8418 ) -> PyResult<Option<PyObjectRef>> {
8419 if mapping.class().is(vm.ctx.types.dict_type) {
8420 let dict = mapping
8421 .downcast_ref::<PyDict>()
8422 .expect("exact dict must have a PyDict payload");
8423 dict.get_item_opt(name, vm)
8424 } else {
8425 match mapping.get_item(name, vm) {
8426 Ok(value) => Ok(Some(value)),
8427 Err(err) if err.fast_isinstance(vm.ctx.exceptions.key_error) => Ok(None),
8428 Err(err) => Err(err),
8429 }
8430 }
8431 }
8432
8433 #[inline]
8434 fn load_global_or_builtin(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
8435 if let Some(builtins_dict) = self.builtins_dict {
8436 let globals_exact = unsafe { PyExact::ref_unchecked(self.globals) };
8439 globals_exact
8440 .get_chain_exact(builtins_dict, name, vm)?
8441 .ok_or_else(|| {
8442 vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
8443 })
8444 } else {
8445 if let Some(value) = self.globals.get_item_opt(name, vm)? {
8447 return Ok(value);
8448 }
8449 self.builtins.get_item(name, vm).map_err(|e| {
8450 if e.fast_isinstance(vm.ctx.exceptions.key_error) {
8451 vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
8452 } else {
8453 e
8454 }
8455 })
8456 }
8457 }
8458
8459 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
8460 fn import(&mut self, vm: &VirtualMachine, module_name: Option<&Py<PyStr>>) -> PyResult<()> {
8461 let module_name = module_name.unwrap_or(vm.ctx.empty_str);
8462 let from_list = self.pop_value();
8463 let level = usize::try_from_object(vm, self.pop_value())?;
8464
8465 let module = vm.import_from(module_name, from_list, level)?;
8466
8467 self.push_value(module);
8468 Ok(())
8469 }
8470
8471 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
8472 fn import_from(&mut self, vm: &VirtualMachine, idx: bytecode::NameIdx) -> PyResult {
8473 let module = self.top_value();
8474 let name = self.code.names[idx as usize];
8475
8476 if let Some(obj) = vm.get_attribute_opt(module, name)? {
8478 return Ok(obj);
8479 }
8480 let fallback_module = (|| {
8482 let mod_name = module.get_attr(identifier!(vm, __name__), vm).ok()?;
8483 let mod_name = mod_name.downcast_ref::<PyUtf8Str>()?;
8484 let full_mod_name = vm.ctx.new_utf8_str(format!("{}.{name}", mod_name.as_str()));
8485 let sys_modules = vm.sys_module.get_attr("modules", vm).ok()?;
8486 sys_modules.get_item(&*full_mod_name, vm).ok()
8487 })();
8488
8489 if let Some(sub_module) = fallback_module {
8490 return Ok(sub_module);
8491 }
8492
8493 use crate::import::{
8494 get_spec_file_origin, is_possibly_shadowing_path, is_stdlib_module_name,
8495 };
8496
8497 let mod_name_obj = module.get_attr(identifier!(vm, __name__), vm).ok();
8499 let mod_name = mod_name_obj
8500 .as_ref()
8501 .and_then(|n| n.downcast_ref::<PyUtf8Str>());
8502 let module_name = mod_name.map_or("<unknown module name>", |s| s.as_str());
8503
8504 let spec = module
8505 .get_attr("__spec__", vm)
8506 .ok()
8507 .filter(|s| !vm.is_none(s));
8508
8509 let origin = get_spec_file_origin(spec.as_deref(), vm);
8510
8511 let is_possibly_shadowing = origin
8512 .as_ref()
8513 .is_some_and(|o| is_possibly_shadowing_path(o, vm));
8514 let is_possibly_shadowing_stdlib = if is_possibly_shadowing {
8515 if let Some(ref mod_name) = mod_name_obj {
8516 is_stdlib_module_name(mod_name, vm)?
8517 } else {
8518 false
8519 }
8520 } else {
8521 false
8522 };
8523
8524 let msg = if is_possibly_shadowing_stdlib {
8525 let origin = origin.as_ref().unwrap();
8526 format!(
8527 "cannot import name '{name}' from '{module_name}' \
8528 (consider renaming '{origin}' since it has the same \
8529 name as the standard library module named '{module_name}' \
8530 and prevents importing that standard library module)"
8531 )
8532 } else {
8533 let is_init = is_module_initializing(module, vm);
8534 if is_init {
8535 if is_possibly_shadowing {
8536 let origin = origin.as_ref().unwrap();
8537 format!(
8538 "cannot import name '{name}' from '{module_name}' \
8539 (consider renaming '{origin}' if it has the same name \
8540 as a library you intended to import)"
8541 )
8542 } else if let Some(ref path) = origin {
8543 format!(
8544 "cannot import name '{name}' from partially initialized module \
8545 '{module_name}' (most likely due to a circular import) ({path})"
8546 )
8547 } else {
8548 format!(
8549 "cannot import name '{name}' from partially initialized module \
8550 '{module_name}' (most likely due to a circular import)"
8551 )
8552 }
8553 } else if let Some(ref path) = origin {
8554 format!("cannot import name '{name}' from '{module_name}' ({path})")
8555 } else {
8556 format!("cannot import name '{name}' from '{module_name}' (unknown location)")
8557 }
8558 };
8559 let err = vm.new_import_error(
8560 msg,
8561 match mod_name {
8562 Some(s) => s.to_owned().into_wtf8(),
8563 None => vm.ctx.new_utf8_str("<unknown module name>").into_wtf8(),
8564 },
8565 );
8566
8567 if let Some(ref path) = origin {
8568 let _ignore = err
8569 .as_object()
8570 .set_attr("path", vm.ctx.new_str(path.as_str()), vm);
8571 }
8572
8573 let _ignore = err.as_object().set_attr("name_from", name.to_owned(), vm);
8575
8576 Err(err)
8577 }
8578
8579 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
8580 fn import_star(&mut self, vm: &VirtualMachine) -> PyResult<()> {
8581 let module = self.pop_value();
8582
8583 let Some(dict) = module.dict() else {
8584 return Ok(());
8585 };
8586
8587 let mod_name = module
8588 .get_attr(identifier!(vm, __name__), vm)
8589 .ok()
8590 .and_then(|n| n.downcast::<PyStr>().ok());
8591
8592 let require_str = |obj: PyObjectRef, attr: &str| -> PyResult<PyRef<PyStr>> {
8593 obj.downcast().map_err(|obj: PyObjectRef| {
8594 let source = if let Some(ref mod_name) = mod_name {
8595 format!("{}.{attr}", mod_name.as_wtf8())
8596 } else {
8597 attr.to_owned()
8598 };
8599 let repr = obj.repr(vm).unwrap_or_else(|_| vm.ctx.new_str("?"));
8600 vm.new_type_error(format!(
8601 "{} in {} must be str, not {}",
8602 repr.as_wtf8(),
8603 source,
8604 obj.class().name()
8605 ))
8606 })
8607 };
8608
8609 let locals_map = self.locals.mapping(vm);
8610 if let Ok(all) = dict.get_item(identifier!(vm, __all__), vm) {
8611 let items: Vec<PyObjectRef> = all.try_to_value(vm)?;
8612 for item in items {
8613 let name = require_str(item, "__all__")?;
8614 let value = module.get_attr(&*name, vm)?;
8615 locals_map.ass_subscript(&name, Some(value), vm)?;
8616 }
8617 } else {
8618 for (k, v) in dict {
8619 let k = require_str(k, "__dict__")?;
8620 if !k.as_bytes().starts_with(b"_") {
8621 locals_map.ass_subscript(&k, Some(v), vm)?;
8622 }
8623 }
8624 }
8625 Ok(())
8626 }
8627
8628 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
8633 fn unwind_blocks(&mut self, vm: &VirtualMachine, reason: UnwindReason) -> FrameResult {
8634 match reason {
8636 UnwindReason::Raising { exception, offset } => {
8637 if let Some(entry) =
8640 bytecode::find_exception_handler(&self.code.exceptiontable, offset)
8641 {
8642 if vm.state.monitoring_events.load() & MonitoringEvent::ExceptionHandled.mask()
8646 != 0
8647 {
8648 let byte_offset = offset * 2;
8649 let exc_obj: PyObjectRef = exception.clone().into();
8650 monitoring::fire_exception_handled(vm, self.code, byte_offset, &exc_obj)?;
8651 }
8652
8653 while self.localsplus.stack_len() > entry.depth as usize {
8655 let _ = self.localsplus.stack_pop();
8656 }
8657
8658 if entry.push_lasti {
8661 let lasti = self.lasti().saturating_sub(1);
8662 self.push_value(vm.ctx.new_int(lasti as i32).into());
8663 }
8664
8665 self.push_value(exception.into());
8670
8671 self.jump(bytecode::Label::from_u32(entry.target));
8673
8674 Ok(None)
8675 } else {
8676 Err(exception)
8678 }
8679 }
8680 UnwindReason::Returning { value } => Ok(Some(ExecutionResult::Return(value))),
8681 }
8682 }
8683
8684 fn restore_reraise_lasti(&mut self, oparg: u32) {
8690 if oparg == 0 {
8691 return;
8692 }
8693 let stack_len = self.localsplus.stack_len();
8694 if stack_len < oparg as usize {
8695 return;
8696 }
8697 let Some(lasti_ref) = self.localsplus.stack_index(stack_len - oparg as usize) else {
8698 return;
8699 };
8700 let Some(int) = lasti_ref.as_object().downcast_ref::<PyInt>() else {
8701 return;
8702 };
8703 let Some(idx) = int.as_bigint().to_u32() else {
8704 return;
8705 };
8706 self.lasti.store(idx.saturating_add(1), Relaxed);
8707 }
8708
8709 fn execute_store_subscript(&mut self, vm: &VirtualMachine) -> FrameResult {
8710 let idx = self.pop_stackref();
8711 let obj = self.pop_stackref();
8712 let value = self.pop_value();
8713 obj.set_item(idx.as_object(), value, vm)?;
8714 Ok(None)
8715 }
8716
8717 fn execute_delete_subscript(&mut self, vm: &VirtualMachine) -> FrameResult {
8718 let idx = self.pop_stackref();
8719 let obj = self.pop_stackref();
8720 obj.del_item(idx.as_object(), vm)?;
8721 Ok(None)
8722 }
8723
8724 fn execute_build_map(&mut self, vm: &VirtualMachine, size: u32) -> FrameResult {
8725 let size = size as usize;
8726 let map_obj = vm.ctx.new_dict();
8727 for (key, value) in self.pop_multiple(2 * size).tuples() {
8728 map_obj.set_item(&*key, value, vm)?;
8729 }
8730
8731 self.push_value(map_obj.into());
8732 Ok(None)
8733 }
8734
8735 fn execute_build_slice(
8736 &mut self,
8737 vm: &VirtualMachine,
8738 argc: bytecode::BuildSliceArgCount,
8739 ) -> Option<ExecutionResult> {
8740 let step = match argc {
8741 bytecode::BuildSliceArgCount::Two => None,
8742 bytecode::BuildSliceArgCount::Three => Some(self.pop_value()),
8743 };
8744 let stop = self.pop_value();
8745 let start = self.pop_value();
8746
8747 let obj = PySlice {
8748 start: Some(start),
8749 stop,
8750 step,
8751 }
8752 .into_ref(&vm.ctx);
8753 self.push_value(obj.into());
8754 None
8755 }
8756
8757 fn collect_positional_args(&mut self, nargs: u32) -> FuncArgs {
8758 FuncArgs {
8759 args: self.pop_multiple(nargs as usize).collect(),
8760 ..Default::default()
8761 }
8762 }
8763
8764 fn collect_keyword_args(&mut self, nargs: u32) -> FuncArgs {
8765 let kwarg_names = self
8766 .pop_value()
8767 .downcast::<PyTuple>()
8768 .expect("kwarg names should be tuple of strings");
8769 let args = self.pop_multiple(nargs as usize);
8770
8771 let kwarg_names = kwarg_names.as_slice().iter().map(|pyobj| {
8772 pyobj
8773 .downcast_ref::<PyUtf8Str>()
8774 .unwrap()
8775 .as_str()
8776 .to_owned()
8777 });
8778 FuncArgs::with_kwargs_names(args, kwarg_names)
8779 }
8780
8781 fn collect_ex_args(&mut self, vm: &VirtualMachine) -> PyResult<FuncArgs> {
8782 let kwargs_or_null = self.pop_value_opt();
8783 let mut kwargs = KwArgs::default();
8784 if let Some(kw_obj) = kwargs_or_null {
8785 let callable = self.nth_value(2);
8787 let func_str = Self::object_function_str(callable, vm);
8788
8789 Self::iterate_mapping_keys(vm, &kw_obj, &func_str, |key, value| {
8790 let key_str = key
8793 .downcast_ref::<PyStr>()
8794 .ok_or_else(|| vm.new_type_error("keywords must be strings"))?;
8795 kwargs.insert(key_str.as_wtf8().to_owned(), value);
8796 Ok(())
8797 })?
8798 };
8799
8800 let args_obj = self.pop_value();
8801 let args = if let Some(tuple) = args_obj.downcast_ref::<PyTuple>() {
8802 tuple.as_slice().to_vec()
8803 } else {
8804 let callable = self.nth_value(1);
8807 let func_str = Self::object_function_str(callable, vm);
8808 let not_iterable = args_obj.class().slots().iter.load().is_none()
8809 && args_obj
8810 .get_class_attr(vm.ctx.intern_str("__getitem__"))
8811 .is_none();
8812 args_obj.try_to_value::<Vec<PyObjectRef>>(vm).map_err(|e| {
8813 if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
8814 vm.new_type_error(format!(
8815 "{} argument after * must be an iterable, not {}",
8816 func_str,
8817 args_obj.class().slot_name()
8818 ))
8819 } else {
8820 e
8821 }
8822 })?
8823 };
8824 Ok(FuncArgs { args, kwargs })
8825 }
8826
8827 fn object_function_str(obj: &PyObject, vm: &VirtualMachine) -> Wtf8Buf {
8831 let repr_fallback = || {
8832 obj.repr(vm)
8833 .as_ref()
8834 .map_or_else(|_| "?".as_ref(), |s| s.as_wtf8())
8835 .to_owned()
8836 };
8837 let Ok(qualname) = obj.get_attr(vm.ctx.intern_str("__qualname__"), vm) else {
8838 return repr_fallback();
8839 };
8840 let Some(qualname_str) = qualname.downcast_ref::<PyStr>() else {
8841 return repr_fallback();
8842 };
8843 if let Ok(module) = obj.get_attr(vm.ctx.intern_str("__module__"), vm)
8844 && let Some(module_str) = module.downcast_ref::<PyStr>()
8845 && module_str.as_bytes() != b"builtins"
8846 {
8847 return wtf8_concat!(module_str.as_wtf8(), ".", qualname_str.as_wtf8(), "()");
8848 }
8849 wtf8_concat!(qualname_str.as_wtf8(), "()")
8850 }
8851
8852 fn iterate_mapping_keys<F>(
8855 vm: &VirtualMachine,
8856 mapping: &PyObject,
8857 func_str: &Wtf8,
8858 mut key_handler: F,
8859 ) -> PyResult<()>
8860 where
8861 F: FnMut(PyObjectRef, PyObjectRef) -> PyResult<()>,
8862 {
8863 if mapping.class().is(vm.ctx.types.dict_type) {
8867 let dict = mapping
8868 .downcast_ref::<PyDict>()
8869 .expect("exact dict must have a PyDict payload");
8870 for (key, value) in dict.items_vec() {
8873 key_handler(key, value)?;
8874 }
8875 return Ok(());
8876 }
8877
8878 let Some(keys_method) = vm.get_method(mapping.to_owned(), vm.ctx.intern_str("keys")) else {
8879 return Err(vm.new_type_error(format!(
8880 "{} argument after ** must be a mapping, not {}",
8881 func_str,
8882 mapping.class().name()
8883 )));
8884 };
8885
8886 let keys = PyIter::try_from_object(vm, keys_method?.call((), vm)?)?;
8887 while let PyIterReturn::Return(key) = keys.next(vm)? {
8888 let value = mapping.get_item(&*key, vm)?;
8889 key_handler(key, value)?;
8890 }
8891 Ok(())
8892 }
8893
8894 #[inline]
8897 fn execute_call_vectorcall(&mut self, nargs: u32, vm: &VirtualMachine) -> FrameResult {
8898 let nargs_usize = nargs as usize;
8899 let stack_len = self.localsplus.stack_len();
8900 debug_assert!(
8901 stack_len >= nargs_usize + 2,
8902 "CALL stack underflow: need callable + self_or_null + {nargs_usize} args, have {stack_len}"
8903 );
8904 let callable_idx = stack_len - nargs_usize - 2;
8905 let self_or_null_idx = stack_len - nargs_usize - 1;
8906 let args_start = stack_len - nargs_usize;
8907
8908 let self_or_null = self
8910 .localsplus
8911 .stack_index_mut(self_or_null_idx)
8912 .take()
8913 .map(|sr| sr.to_pyobj());
8914 let has_self = self_or_null.is_some();
8915
8916 let effective_nargs = if has_self {
8917 nargs_usize + 1
8918 } else {
8919 nargs_usize
8920 };
8921 let mut args_vec = Vec::with_capacity(effective_nargs);
8922 if let Some(self_val) = self_or_null {
8923 args_vec.push(self_val);
8924 }
8925 for stack_idx in args_start..stack_len {
8926 let val = self
8927 .localsplus
8928 .stack_index_mut(stack_idx)
8929 .take()
8930 .unwrap()
8931 .to_pyobj();
8932 args_vec.push(val);
8933 }
8934
8935 let callable_obj = self
8936 .localsplus
8937 .stack_index_mut(callable_idx)
8938 .take()
8939 .unwrap()
8940 .to_pyobj();
8941 self.localsplus.stack_truncate(callable_idx);
8942
8943 let result = callable_obj.vectorcall(args_vec, effective_nargs, None, vm)?;
8945 self.push_value(result);
8946 Ok(None)
8947 }
8948
8949 #[inline]
8951 fn execute_call_kw_vectorcall(&mut self, nargs: u32, vm: &VirtualMachine) -> FrameResult {
8952 let nargs_usize = nargs as usize;
8953
8954 let kwarg_names_obj = self.pop_value();
8956 let kwarg_names_tuple = kwarg_names_obj
8957 .downcast_ref::<PyTuple>()
8958 .expect("kwarg names should be tuple");
8959 let kw_count = kwarg_names_tuple.as_slice().len();
8960 debug_assert!(kw_count <= nargs_usize, "CALL_KW kw_count exceeds nargs");
8961
8962 let stack_len = self.localsplus.stack_len();
8963 debug_assert!(
8964 stack_len >= nargs_usize + 2,
8965 "CALL_KW stack underflow: need callable + self_or_null + {nargs_usize} args, have {stack_len}"
8966 );
8967 let callable_idx = stack_len - nargs_usize - 2;
8968 let self_or_null_idx = stack_len - nargs_usize - 1;
8969 let args_start = stack_len - nargs_usize;
8970
8971 let self_or_null = self
8973 .localsplus
8974 .stack_index_mut(self_or_null_idx)
8975 .take()
8976 .map(|sr| sr.to_pyobj());
8977 let has_self = self_or_null.is_some();
8978
8979 let pos_count = nargs_usize
8980 .checked_sub(kw_count)
8981 .expect("CALL_KW: kw_count exceeds nargs");
8982 let effective_nargs = if has_self { pos_count + 1 } else { pos_count };
8983
8984 let total_args = effective_nargs + kw_count;
8986 let mut args_vec = Vec::with_capacity(total_args);
8987 if let Some(self_val) = self_or_null {
8988 args_vec.push(self_val);
8989 }
8990 for stack_idx in args_start..stack_len {
8991 let val = self
8992 .localsplus
8993 .stack_index_mut(stack_idx)
8994 .take()
8995 .unwrap()
8996 .to_pyobj();
8997 args_vec.push(val);
8998 }
8999
9000 let callable_obj = self
9001 .localsplus
9002 .stack_index_mut(callable_idx)
9003 .take()
9004 .unwrap()
9005 .to_pyobj();
9006 self.localsplus.stack_truncate(callable_idx);
9007
9008 let kwnames = kwarg_names_tuple.as_slice();
9010 let result = callable_obj.vectorcall(args_vec, effective_nargs, Some(kwnames), vm)?;
9011 self.push_value(result);
9012 Ok(None)
9013 }
9014
9015 #[inline]
9016 fn execute_call(&mut self, args: FuncArgs, vm: &VirtualMachine) -> FrameResult {
9017 let self_or_null = self.pop_value_opt(); let callable = self.pop_value();
9020
9021 let final_args = if let Some(self_val) = self_or_null {
9022 let mut args = args;
9023 args.prepend_arg(self_val);
9024 args
9025 } else {
9026 args
9027 };
9028
9029 let value = callable.call(final_args, vm)?;
9030 self.push_value(value);
9031 Ok(None)
9032 }
9033
9034 fn execute_call_instrumented(&mut self, args: FuncArgs, vm: &VirtualMachine) -> FrameResult {
9036 let self_or_null = self.pop_value_opt();
9037 let callable = self.pop_value();
9038
9039 let final_args = if let Some(self_val) = self_or_null {
9040 let mut args = args;
9041 args.prepend_arg(self_val);
9042 args
9043 } else {
9044 args
9045 };
9046
9047 let is_python_call = callable.downcast_ref_if_exact::<PyFunction>(vm).is_some();
9048
9049 let call_arg0 = if self.monitoring_mask & MonitoringEvent::Call.mask() != 0 {
9051 let arg0 = final_args
9052 .args
9053 .first()
9054 .cloned()
9055 .unwrap_or_else(|| monitoring::get_missing(vm));
9056 let offset = (self.lasti() - 1) * 2;
9057 monitoring::fire_call(vm, self.code, offset, &callable, arg0.clone())?;
9058 Some(arg0)
9059 } else {
9060 None
9061 };
9062
9063 match callable.call(final_args, vm) {
9064 Ok(value) => {
9065 if let Some(arg0) = call_arg0
9066 && !is_python_call
9067 {
9068 let offset = (self.lasti() - 1) * 2;
9069 monitoring::fire_c_return(vm, self.code, offset, &callable, arg0)?;
9070 }
9071 self.push_value(value);
9072 Ok(None)
9073 }
9074 Err(exc) => {
9075 let exc = if let Some(arg0) = call_arg0
9076 && !is_python_call
9077 {
9078 let offset = (self.lasti() - 1) * 2;
9079 match monitoring::fire_c_raise(vm, self.code, offset, &callable, arg0) {
9080 Ok(()) => exc,
9081 Err(monitor_exc) => monitor_exc,
9082 }
9083 } else {
9084 exc
9085 };
9086 Err(exc)
9087 }
9088 }
9089 }
9090
9091 fn execute_raise(&mut self, vm: &VirtualMachine, kind: bytecode::RaiseKind) -> FrameResult {
9092 let cause = match kind {
9093 bytecode::RaiseKind::RaiseCause => {
9094 let val = self.pop_value();
9095 Some(if vm.is_none(&val) {
9096 None
9098 } else {
9099 let ctor = ExceptionCtor::try_from_object(vm, val).map_err(|_| {
9101 vm.new_type_error("exception causes must derive from BaseException")
9102 })?;
9103 Some(ctor.instantiate(vm)?)
9104 })
9105 }
9106 _ => None,
9108 };
9109 let exception = match kind {
9110 bytecode::RaiseKind::RaiseCause | bytecode::RaiseKind::Raise => {
9111 ExceptionCtor::try_from_object(vm, self.pop_value())?.instantiate(vm)?
9112 }
9113 bytecode::RaiseKind::BareRaise => {
9114 vm.topmost_exception()
9116 .ok_or_else(|| vm.new_runtime_error("No active exception to reraise"))?
9117 }
9118 bytecode::RaiseKind::ReraiseFromStack => {
9119 let exc = self.pop_value();
9122 exc.downcast::<PyBaseException>().map_err(|obj| {
9123 vm.new_type_error(format!(
9124 "exceptions must derive from BaseException, not {}",
9125 obj.class().name()
9126 ))
9127 })?
9128 }
9129 };
9130 #[cfg(debug_assertions)]
9131 debug!("Exception raised: {exception:?} with cause: {cause:?}");
9132 if let Some(cause) = cause {
9133 exception.set_cause(cause);
9134 }
9135 Err(exception)
9136 }
9137
9138 #[inline(never)]
9143 fn send_yield_from_cont(
9144 &self,
9145 receiver: &PyObject,
9146 exit_label: bytecode::Label,
9147 ) -> Option<GenCont> {
9148 let next_idx = self.lasti() as usize + 1;
9149 let unit = self.code.instructions.get(next_idx)?;
9150 if !matches!(unit.op, Instruction::YieldValue { .. }) || u8::from(unit.arg) < 1 {
9151 return None;
9152 }
9153 if !self.builtin_coro(receiver).is_some_and(is_delegating) {
9154 return None;
9155 }
9156 Some(GenCont {
9157 exit: exit_label.as_u32(),
9158 resumed_at: next_idx as u32 + 1,
9159 })
9160 }
9161
9162 fn builtin_coro<'a>(&self, coro: &'a PyObject) -> Option<&'a Coro> {
9163 match_class!(match coro {
9164 ref g @ PyGenerator => Some(g.as_coro()),
9165 ref c @ PyCoroutine => Some(c.as_coro()),
9166 _ => None,
9167 })
9168 }
9169
9170 fn _send(
9171 &self,
9172 jen: &PyObject,
9173 val: PyObjectRef,
9174 vm: &VirtualMachine,
9175 ) -> PyResult<PyIterReturn> {
9176 match self.builtin_coro(jen) {
9177 Some(coro) => coro.send(jen, val, vm),
9178 None if vm.is_none(&val) => PyIter::new(jen).next(vm),
9180 None => {
9181 let meth = jen.get_attr("send", vm)?;
9182 PyIterReturn::from_pyresult(meth.call((val,), vm), vm)
9183 }
9184 }
9185 }
9186
9187 fn execute_unpack_ex(&mut self, vm: &VirtualMachine, before: u8, after: u32) -> FrameResult {
9188 let (before, after) = (before as usize, after as usize);
9189 let value = self.pop_value();
9190 let not_iterable = value.class().slots().iter.load().is_none()
9191 && value
9192 .get_class_attr(vm.ctx.intern_str("__getitem__"))
9193 .is_none();
9194 let elements: Vec<_> = value.try_to_value(vm).map_err(|e| {
9195 if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
9196 vm.new_type_error(format!(
9197 "cannot unpack non-iterable {} object",
9198 value.class().name()
9199 ))
9200 } else {
9201 e
9202 }
9203 })?;
9204 let min_expected = before + after;
9205
9206 let middle = elements.len().checked_sub(min_expected).ok_or_else(|| {
9207 vm.new_value_error(format!(
9208 "not enough values to unpack (expected at least {}, got {})",
9209 min_expected,
9210 elements.len()
9211 ))
9212 })?;
9213
9214 let mut elements = elements;
9215 self.localsplus.stack_extend(
9217 elements
9218 .drain(before + middle..)
9219 .rev()
9220 .map(|e| Some(PyStackRef::new_owned(e))),
9221 );
9222
9223 let middle_elements = elements.drain(before..).collect();
9224 let t = vm.ctx.new_list(middle_elements);
9225 self.push_value(t.into());
9226
9227 self.localsplus.stack_extend(
9229 elements
9230 .into_iter()
9231 .rev()
9232 .map(|e| Some(PyStackRef::new_owned(e))),
9233 );
9234
9235 Ok(None)
9236 }
9237
9238 #[inline]
9239 fn jump(&mut self, label: bytecode::Label) {
9240 let target_pc = label.as_u32();
9241 vm_trace!("jump from {:?} to {:?}", self.lasti(), target_pc);
9242 self.update_lasti(|i| *i = target_pc);
9243 }
9244
9245 #[inline]
9251 fn jump_relative_forward(&mut self, delta: u32, caches: u32) {
9252 let target = self.lasti() + caches + delta;
9253 self.update_lasti(|i| *i = target);
9254 }
9255
9256 #[inline]
9261 fn jump_relative_backward(&mut self, delta: u32, caches: u32) {
9262 let target = self.lasti() + caches - delta;
9263 self.update_lasti(|i| *i = target);
9264 }
9265
9266 fn jump_relative_backward_and_trace_line(
9269 &mut self,
9270 delta: u32,
9271 caches: u32,
9272 vm: &VirtualMachine,
9273 ) -> PyResult<()> {
9274 let from_idx = self.lasti().saturating_sub(1) as usize;
9275 self.jump_relative_backward(delta, caches);
9276 self.trace_backward_same_line(from_idx, vm)
9277 }
9278
9279 fn trace_backward_same_line(&mut self, from_idx: usize, vm: &VirtualMachine) -> PyResult<()> {
9282 if !(vm.use_tracing.get() && self.trace_is_set(vm) && self.trace_lines_is_set()) {
9283 return Ok(());
9284 }
9285 let to_idx = self.lasti() as usize;
9286 let from_line = self.code.addr2line(from_idx as i32 * 2);
9287 let to_line = self.code.addr2line(to_idx as i32 * 2);
9288 if to_line >= 0 && to_line == from_line {
9289 self.prev_line.set(to_line as u32);
9290 let dest = self.lasti();
9294 self.update_lasti(|i| *i = dest + 1);
9295 let result = vm.trace_event(crate::protocol::TraceEvent::Line, None);
9296 self.update_lasti(|i| *i = dest);
9297 result?;
9298 }
9299 Ok(())
9300 }
9301
9302 #[inline]
9314 fn skip_fallthrough_not_taken(&mut self, vm: &VirtualMachine, caches: u32) {
9315 if self.specialization_eval_frame_active(vm) {
9316 return;
9317 }
9318 let next = self.lasti() + caches;
9319 if (next as usize) < self.code.instructions.len()
9320 && matches!(
9321 self.code.instructions.read_op(next as usize),
9322 Instruction::NotTaken
9323 )
9324 {
9325 self.update_lasti(|i| *i = next + 1);
9326 }
9327 }
9328
9329 #[inline]
9330 fn pop_jump_if_relative(
9331 &mut self,
9332 vm: &VirtualMachine,
9333 arg: bytecode::OpArg,
9334 caches: u32,
9335 flag: bool,
9336 ) -> FrameResult {
9337 let obj = self.pop_stackref();
9338 let value = obj.try_to_bool(vm)?;
9339 if value == flag {
9340 self.jump_relative_forward(u32::from(arg), caches);
9341 } else {
9342 self.skip_fallthrough_not_taken(vm, caches);
9343 }
9344 Ok(None)
9345 }
9346
9347 fn monitor_for_iter_stop(
9352 &self,
9353 value: Option<PyObjectRef>,
9354 vm: &VirtualMachine,
9355 ) -> PyResult<()> {
9356 let iter = self.top_value();
9357 if iter.downcast_ref_if_exact::<PyListIterator>(vm).is_some()
9358 || iter.downcast_ref_if_exact::<PyRangeIterator>(vm).is_some()
9359 || iter.downcast_ref_if_exact::<PyTupleIterator>(vm).is_some()
9360 {
9361 return Ok(());
9362 }
9363 let need_raise = vm.state.monitoring_events.load() & MonitoringEvent::Raise.mask() != 0;
9364 let need_trace = vm.use_tracing.get() && self.trace_is_set(vm);
9365 if !need_raise && !need_trace {
9366 return Ok(());
9367 }
9368 let stop_exc = vm.new_stop_iteration(value);
9369 if need_raise {
9370 let offset = (self.lasti() - 1) * 2;
9371 let exc_obj: PyObjectRef = stop_exc.clone().into();
9372 monitoring::fire_raise(vm, self.code, offset, &exc_obj)?;
9373 }
9374 if need_trace {
9375 self.fire_exception_trace(&stop_exc, vm)?;
9376 }
9377 Ok(())
9378 }
9379
9380 fn execute_for_iter(
9383 &mut self,
9384 vm: &VirtualMachine,
9385 target: bytecode::Label,
9386 ) -> Result<bool, PyBaseExceptionRef> {
9387 let top = self.top_value();
9388
9389 if let Some(range_iter) = top.downcast_ref_if_exact::<PyRangeIterator>(vm) {
9391 if let Some(value) = range_iter.fast_next() {
9392 self.push_value(vm.ctx.new_int(value).into());
9393 return Ok(true);
9394 }
9395 if vm.use_tracing.get() && self.trace_is_set(vm) {
9396 let stop_exc = vm.new_stop_iteration(None);
9397 self.fire_exception_trace(&stop_exc, vm)?;
9398 }
9399 self.jump(self.for_iter_jump_target(target));
9400 return Ok(false);
9401 }
9402
9403 let top_of_stack = PyIter::new(top);
9404 let next_obj = top_of_stack.next(vm);
9405
9406 match next_obj {
9407 Ok(PyIterReturn::Return(value)) => {
9408 self.push_value(value);
9409 Ok(true)
9410 }
9411 Ok(PyIterReturn::StopIteration(value)) => {
9412 self.monitor_for_iter_stop(value, vm)?;
9415 self.jump(self.for_iter_jump_target(target));
9416 Ok(false)
9417 }
9418 Err(next_error) => {
9419 self.pop_stackref();
9420 Err(next_error)
9421 }
9422 }
9423 }
9424
9425 fn for_iter_jump_target(&self, target: bytecode::Label) -> bytecode::Label {
9427 let target_idx = target.as_usize();
9428 if let Some(unit) = self.code.instructions.get(target_idx)
9429 && matches!(
9430 unit.op,
9431 bytecode::Instruction::EndFor | bytecode::Instruction::InstrumentedEndFor
9432 )
9433 {
9434 return bytecode::Label::from_u32(target.as_u32() + 1);
9435 }
9436 target
9437 }
9438 fn execute_make_function(&mut self, vm: &VirtualMachine) -> FrameResult {
9439 let code_obj: PyRef<PyCode> = self
9441 .pop_value()
9442 .downcast()
9443 .expect("Stack value should be code object");
9444
9445 let func_obj = PyFunction::new(code_obj, self.globals.to_owned(), vm)?.into_pyobject(vm);
9447
9448 self.push_value(func_obj);
9449 Ok(None)
9450 }
9451
9452 fn execute_set_function_attribute(
9453 &mut self,
9454 vm: &VirtualMachine,
9455 attr: bytecode::MakeFunctionFlag,
9456 ) -> FrameResult {
9457 let func = self.pop_value_opt();
9462 let attr_value = expect_unchecked(self.replace_top(func), "attr_value must not be null");
9463
9464 let func = self.top_value();
9465 let func_ref = func
9467 .downcast_ref_if_exact::<PyFunction>(vm)
9468 .expect("SET_FUNCTION_ATTRIBUTE expects function on stack");
9469
9470 let payload: &PyFunction = func_ref.payload();
9471 unsafe {
9474 let payload_ptr = payload as *const PyFunction as *mut PyFunction;
9475 (*payload_ptr).set_function_attribute(attr, attr_value, vm)?;
9476 };
9477
9478 Ok(None)
9479 }
9480
9481 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
9482 fn execute_bin_op(&mut self, vm: &VirtualMachine, op: bytecode::BinaryOperator) -> FrameResult {
9483 let b = self.pop_stackref();
9484 let a = self.pop_stackref();
9485 let (a_ref, b_ref) = (a.as_object(), b.as_object());
9486 let value = match op {
9487 bytecode::BinaryOperator::Add | bytecode::BinaryOperator::InplaceAdd => {
9491 if let (Some(a), Some(b)) = (
9492 a_ref.downcast_ref_if_exact::<PyInt>(vm),
9493 b_ref.downcast_ref_if_exact::<PyInt>(vm),
9494 ) {
9495 Ok(Self::int_add(a, b, vm))
9496 } else if matches!(op, bytecode::BinaryOperator::Add) {
9497 vm._add(a_ref, b_ref)
9498 } else {
9499 vm._iadd(a_ref, b_ref)
9500 }
9501 }
9502 bytecode::BinaryOperator::Subtract | bytecode::BinaryOperator::InplaceSubtract => {
9503 if let (Some(a), Some(b)) = (
9504 a_ref.downcast_ref_if_exact::<PyInt>(vm),
9505 b_ref.downcast_ref_if_exact::<PyInt>(vm),
9506 ) {
9507 Ok(Self::int_sub(a, b, vm))
9508 } else if matches!(op, bytecode::BinaryOperator::Subtract) {
9509 vm._sub(a_ref, b_ref)
9510 } else {
9511 vm._isub(a_ref, b_ref)
9512 }
9513 }
9514 bytecode::BinaryOperator::Multiply | bytecode::BinaryOperator::InplaceMultiply => {
9515 if let (Some(a), Some(b)) = (
9516 a_ref.downcast_ref_if_exact::<PyInt>(vm),
9517 b_ref.downcast_ref_if_exact::<PyInt>(vm),
9518 ) {
9519 Ok(Self::int_mul(a, b, vm))
9520 } else if matches!(op, bytecode::BinaryOperator::Multiply) {
9521 vm._mul(a_ref, b_ref)
9522 } else {
9523 vm._imul(a_ref, b_ref)
9524 }
9525 }
9526 bytecode::BinaryOperator::MatrixMultiply => vm._matmul(a_ref, b_ref),
9527 bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref, vm.ctx.none.as_object()),
9528 bytecode::BinaryOperator::TrueDivide => vm._truediv(a_ref, b_ref),
9529 bytecode::BinaryOperator::FloorDivide
9530 | bytecode::BinaryOperator::InplaceFloorDivide => {
9531 if let (Some(a), Some(b)) = (
9532 a_ref.downcast_ref_if_exact::<PyInt>(vm),
9533 b_ref.downcast_ref_if_exact::<PyInt>(vm),
9534 ) && let Some(result) = Self::int_floordiv(a.as_bigint(), b.as_bigint(), vm)
9535 {
9536 Ok(result)
9537 } else if matches!(op, bytecode::BinaryOperator::FloorDivide) {
9538 vm._floordiv(a_ref, b_ref)
9539 } else {
9540 vm._ifloordiv(a_ref, b_ref)
9541 }
9542 }
9543 bytecode::BinaryOperator::Remainder | bytecode::BinaryOperator::InplaceRemainder => {
9544 if let (Some(a), Some(b)) = (
9545 a_ref.downcast_ref_if_exact::<PyInt>(vm),
9546 b_ref.downcast_ref_if_exact::<PyInt>(vm),
9547 ) && let Some(result) = Self::int_mod(a.as_bigint(), b.as_bigint(), vm)
9548 {
9549 Ok(result)
9550 } else if matches!(op, bytecode::BinaryOperator::Remainder) {
9551 vm._mod(a_ref, b_ref)
9552 } else {
9553 vm._imod(a_ref, b_ref)
9554 }
9555 }
9556 bytecode::BinaryOperator::Lshift => vm._lshift(a_ref, b_ref),
9557 bytecode::BinaryOperator::Rshift => vm._rshift(a_ref, b_ref),
9558 bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref),
9559 bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref),
9560 bytecode::BinaryOperator::And => vm._and(a_ref, b_ref),
9561 bytecode::BinaryOperator::InplaceMatrixMultiply => vm._imatmul(a_ref, b_ref),
9562 bytecode::BinaryOperator::InplacePower => {
9563 vm._ipow(a_ref, b_ref, vm.ctx.none.as_object())
9564 }
9565 bytecode::BinaryOperator::InplaceTrueDivide => vm._itruediv(a_ref, b_ref),
9566 bytecode::BinaryOperator::InplaceLshift => vm._ilshift(a_ref, b_ref),
9567 bytecode::BinaryOperator::InplaceRshift => vm._irshift(a_ref, b_ref),
9568 bytecode::BinaryOperator::InplaceXor => vm._ixor(a_ref, b_ref),
9569 bytecode::BinaryOperator::InplaceOr => vm._ior(a_ref, b_ref),
9570 bytecode::BinaryOperator::InplaceAnd => vm._iand(a_ref, b_ref),
9571 bytecode::BinaryOperator::Subscr => a_ref.get_item(b_ref.as_object(), vm),
9572 }?;
9573
9574 self.push_value(value);
9575 Ok(None)
9576 }
9577
9578 #[inline]
9584 fn int_fast_op(
9585 a: &Py<PyInt>,
9586 b: &Py<PyInt>,
9587 vm: &VirtualMachine,
9588 checked: fn(i64, i64) -> Option<i64>,
9589 fallback: impl FnOnce(&BigInt, &BigInt) -> BigInt,
9590 ) -> PyObjectRef {
9591 if let (Some(av), Some(bv)) = (a.try_to_i64_fast(), b.try_to_i64_fast())
9592 && let Some(result) = checked(av, bv)
9593 {
9594 return vm.ctx.new_int(result).into();
9595 }
9596 vm.ctx
9597 .new_int(fallback(a.as_bigint(), b.as_bigint()))
9598 .into()
9599 }
9600
9601 #[inline]
9603 fn int_add(a: &Py<PyInt>, b: &Py<PyInt>, vm: &VirtualMachine) -> PyObjectRef {
9604 Self::int_fast_op(a, b, vm, i64::checked_add, |a, b| a + b)
9605 }
9606
9607 #[inline]
9609 fn int_sub(a: &Py<PyInt>, b: &Py<PyInt>, vm: &VirtualMachine) -> PyObjectRef {
9610 Self::int_fast_op(a, b, vm, i64::checked_sub, |a, b| a - b)
9611 }
9612
9613 #[inline]
9615 fn int_mul(a: &Py<PyInt>, b: &Py<PyInt>, vm: &VirtualMachine) -> PyObjectRef {
9616 Self::int_fast_op(a, b, vm, i64::checked_mul, |a, b| a * b)
9617 }
9618
9619 #[inline]
9625 fn int_div_fast_op(
9626 a: &BigInt,
9627 b: &BigInt,
9628 vm: &VirtualMachine,
9629 compute: fn(i64, i64) -> Option<i64>,
9630 ) -> Option<PyObjectRef> {
9631 use num_traits::ToPrimitive;
9632 let (av, bv) = (a.to_i64()?, b.to_i64()?);
9633 compute(av, bv).map(|r| vm.ctx.new_int(r).into())
9634 }
9635
9636 #[inline]
9640 fn floordiv_i64(a: i64, b: i64) -> Option<i64> {
9641 if b == 0 {
9642 return None;
9643 }
9644 let q = a.checked_div(b)?;
9645 let r = a % b;
9646 Some(if r != 0 && (r < 0) != (b < 0) {
9647 q - 1
9648 } else {
9649 q
9650 })
9651 }
9652
9653 #[inline]
9656 fn mod_i64(a: i64, b: i64) -> Option<i64> {
9657 if b == 0 {
9658 return None;
9659 }
9660 let r = a.checked_rem(b)?;
9661 Some(if r != 0 && (r < 0) != (b < 0) {
9662 r + b
9663 } else {
9664 r
9665 })
9666 }
9667
9668 #[inline]
9671 fn int_floordiv(a: &BigInt, b: &BigInt, vm: &VirtualMachine) -> Option<PyObjectRef> {
9672 Self::int_div_fast_op(a, b, vm, Self::floordiv_i64)
9673 }
9674
9675 #[inline]
9678 fn int_mod(a: &BigInt, b: &BigInt, vm: &VirtualMachine) -> Option<PyObjectRef> {
9679 Self::int_div_fast_op(a, b, vm, Self::mod_i64)
9680 }
9681
9682 #[cold]
9683 fn setup_annotations(&mut self, vm: &VirtualMachine) -> FrameResult {
9684 let __annotations__ = identifier!(vm, __annotations__);
9685 let locals_obj = self.locals.as_object(vm);
9686 let has_annotations = if let Some(d) = locals_obj.downcast_ref_if_exact::<PyDict>(vm) {
9688 d.contains_key(__annotations__, vm)
9689 } else {
9690 self._in(vm, __annotations__.as_object(), locals_obj)?
9691 };
9692 if !has_annotations {
9693 locals_obj.set_item(__annotations__, vm.ctx.new_dict().into(), vm)?;
9694 }
9695 Ok(None)
9696 }
9697
9698 fn unpack_sequence(&mut self, size: u32, vm: &VirtualMachine) -> FrameResult {
9700 let value = self.pop_stackref();
9701 let size = size as usize;
9702
9703 let cls = value.class();
9707 if cls.is(vm.ctx.types.tuple_type) {
9708 let tuple = value.downcast_ref::<PyTuple>().unwrap();
9709 return self.unpack_fast(tuple.as_slice(), size, vm);
9710 }
9711 if cls.is(vm.ctx.types.list_type) {
9712 let list = value.downcast_ref::<PyList>().unwrap();
9713 let borrowed = list.borrow_vec();
9714 return self.unpack_fast(&borrowed, size, vm);
9715 }
9716
9717 let not_iterable = value.class().slots().iter.load().is_none()
9720 && value
9721 .get_class_attr(vm.ctx.intern_str("__getitem__"))
9722 .is_none();
9723 let iter = PyIter::try_from_object(vm, value.as_object().to_owned()).map_err(|e| {
9724 if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
9725 vm.new_type_error(format!(
9726 "cannot unpack non-iterable {} object",
9727 value.class().name()
9728 ))
9729 } else {
9730 e
9731 }
9732 })?;
9733
9734 let mut elements = Vec::with_capacity(size);
9735 for _ in 0..size {
9736 match iter.next(vm)? {
9737 PyIterReturn::Return(item) => elements.push(item),
9738 PyIterReturn::StopIteration(_) => {
9739 return Err(vm.new_value_error(format!(
9740 "not enough values to unpack (expected {size}, got {})",
9741 elements.len()
9742 )));
9743 }
9744 }
9745 }
9746
9747 match iter.next(vm)? {
9749 PyIterReturn::Return(_) => {
9750 let msg = if value.class().is(vm.ctx.types.dict_type) {
9754 if let Ok(got) = value.length(vm) {
9755 if got > size {
9756 format!("too many values to unpack (expected {size}, got {got})")
9757 } else {
9758 format!("too many values to unpack (expected {size})")
9759 }
9760 } else {
9761 format!("too many values to unpack (expected {size})")
9762 }
9763 } else {
9764 format!("too many values to unpack (expected {size})")
9765 };
9766 Err(vm.new_value_error(msg))
9767 }
9768 PyIterReturn::StopIteration(_) => {
9769 self.localsplus.stack_extend(
9770 elements
9771 .into_iter()
9772 .rev()
9773 .map(|e| Some(PyStackRef::new_owned(e))),
9774 );
9775 Ok(None)
9776 }
9777 }
9778 }
9779
9780 fn unpack_fast(
9781 &mut self,
9782 elements: &[PyObjectRef],
9783 size: usize,
9784 vm: &VirtualMachine,
9785 ) -> FrameResult {
9786 match elements.len().cmp(&size) {
9787 core::cmp::Ordering::Equal => {
9788 for elem in elements.iter().rev() {
9789 self.push_value(elem.clone());
9790 }
9791 Ok(None)
9792 }
9793 core::cmp::Ordering::Greater => Err(vm.new_value_error(format!(
9794 "too many values to unpack (expected {size}, got {})",
9795 elements.len()
9796 ))),
9797 core::cmp::Ordering::Less => Err(vm.new_value_error(format!(
9798 "not enough values to unpack (expected {size}, got {})",
9799 elements.len()
9800 ))),
9801 }
9802 }
9803
9804 fn convert_value(
9805 &mut self,
9806 conversion: bytecode::ConvertValueOparg,
9807 vm: &VirtualMachine,
9808 ) -> FrameResult {
9809 use bytecode::ConvertValueOparg;
9810 let value = self.pop_value();
9811 let value = match conversion {
9812 ConvertValueOparg::Str => value.str(vm)?.into(),
9813 ConvertValueOparg::Repr => value.repr(vm)?.into(),
9814 ConvertValueOparg::Ascii => builtins::ascii(value, vm)?.into(),
9815 ConvertValueOparg::None => value,
9816 };
9817
9818 self.push_value(value);
9819 Ok(None)
9820 }
9821
9822 fn _in(&self, vm: &VirtualMachine, needle: &PyObject, haystack: &PyObject) -> PyResult<bool> {
9823 let found = vm._contains(haystack, needle)?;
9824 Ok(found)
9825 }
9826
9827 #[inline(always)]
9828 fn _not_in(
9829 &self,
9830 vm: &VirtualMachine,
9831 needle: &PyObject,
9832 haystack: &PyObject,
9833 ) -> PyResult<bool> {
9834 Ok(!self._in(vm, needle, haystack)?)
9835 }
9836
9837 #[cfg_attr(feature = "flame-it", flame("FrameObject"))]
9838 fn execute_compare(&mut self, vm: &VirtualMachine, arg: bytecode::OpArg) -> FrameResult {
9839 let op = bytecode::ComparisonOperator::try_from(u32::from(arg))
9840 .unwrap_or(bytecode::ComparisonOperator::Equal);
9841 let b = self.pop_stackref();
9842 let a = self.pop_stackref();
9843 let cmp_op: PyComparisonOp = op.into();
9844 let force_bool = u32::from(arg) & bytecode::oparg::COMPARE_OP_BOOL_MASK != 0;
9845
9846 if let (Some(a_int), Some(b_int)) = (
9848 a.downcast_ref_if_exact::<PyInt>(vm),
9849 b.downcast_ref_if_exact::<PyInt>(vm),
9850 ) {
9851 let result = cmp_op.eval_ord(a_int.as_bigint().cmp(b_int.as_bigint()));
9852 self.push_value(vm.ctx.new_bool(result).into());
9853 return Ok(None);
9854 }
9855 if let (Some(a_f), Some(b_f)) = (
9858 a.downcast_ref_if_exact::<PyFloat>(vm),
9859 b.downcast_ref_if_exact::<PyFloat>(vm),
9860 ) && let Some(ord) = a_f.to_f64().partial_cmp(&b_f.to_f64())
9861 {
9862 let result = cmp_op.eval_ord(ord);
9863 self.push_value(vm.ctx.new_bool(result).into());
9864 return Ok(None);
9865 }
9866
9867 let value = a.rich_compare(b.as_object(), cmp_op, vm)?;
9868 let value = if force_bool {
9869 let bool_val = value.try_to_bool(vm)?;
9870 vm.ctx.new_bool(bool_val).into()
9871 } else {
9872 value
9873 };
9874 self.push_value(value);
9875 Ok(None)
9876 }
9877
9878 fn store_attr_dict_hinted(
9881 &mut self,
9882 dict: &Py<PyDict>,
9883 attr_name: &'static PyStrInterned,
9884 value: PyObjectRef,
9885 cache_base: usize,
9886 vm: &VirtualMachine,
9887 ) -> PyResult<()> {
9888 let hint = self.code.instructions.read_cache_u16(cache_base + 3);
9889 if let Some(new_hint) = dict.set_item_with_hint(attr_name, hint, value, vm)? {
9890 unsafe {
9891 self.code
9892 .instructions
9893 .write_cache_u16(cache_base + 3, new_hint);
9894 }
9895 }
9896 Ok(())
9897 }
9898
9899 fn shadowing_instance_attr(
9908 &self,
9909 cache_base: usize,
9910 attr_name: &'static PyStrInterned,
9911 vm: &VirtualMachine,
9912 ) -> PyResult<Option<PyObjectRef>> {
9913 let stamp = self.code.instructions.read_cache_ptr(cache_base + 3);
9914 let stamped = self.top_value().with_instance_dict(|dict| {
9918 dict.is_some_and(|d| stamp != 0 && stamp == d.keys_version() as usize)
9919 });
9920 if stamped {
9921 return Ok(None);
9922 }
9923 let Some(dict) = self.top_value().dict() else {
9924 return Ok(None);
9925 };
9926 let stamp = dict.assign_keys_version(vm);
9928 if let Some(value) = dict.get_item_opt(attr_name, vm)? {
9929 return Ok(Some(value));
9930 }
9931 unsafe {
9932 self.code
9933 .instructions
9934 .write_cache_ptr(cache_base + 3, stamp as usize);
9935 }
9936 Ok(None)
9937 }
9938
9939 #[inline]
9948 fn try_read_cached_descriptor(
9949 &self,
9950 cache_base: usize,
9951 expected_type_version: u32,
9952 ) -> Option<PyObjectRef> {
9953 let descr_ptr = self.code.instructions.read_cache_ptr(cache_base + 5);
9954 if descr_ptr == 0 {
9955 return None;
9956 }
9957 let cloned = unsafe { PyObject::try_to_owned_from_ptr(descr_ptr as *mut PyObject) }?;
9962 if self.code.instructions.read_cache_u32(cache_base + 1) == expected_type_version
9964 && self.code.instructions.read_cache_ptr(cache_base + 5) == descr_ptr
9965 {
9966 Some(cloned)
9967 } else {
9968 drop(cloned);
9969 None
9970 }
9971 }
9972
9973 #[inline]
9974 unsafe fn write_cached_descriptor(
9975 &self,
9976 cache_base: usize,
9977 type_version: u32,
9978 descr_ptr: usize,
9979 ) {
9980 unsafe {
9984 self.code.instructions.write_cache_u32(cache_base + 1, 0);
9985 self.code
9986 .instructions
9987 .write_cache_ptr(cache_base + 5, descr_ptr);
9988 self.code
9989 .instructions
9990 .write_cache_u32(cache_base + 1, type_version);
9991 }
9992 }
9993
9994 #[inline]
9995 unsafe fn write_cached_descriptor_with_metaclass(
9996 &self,
9997 cache_base: usize,
9998 type_version: u32,
9999 metaclass_version: u32,
10000 descr_ptr: usize,
10001 ) {
10002 unsafe {
10003 self.code.instructions.write_cache_u32(cache_base + 1, 0);
10004 self.code
10005 .instructions
10006 .write_cache_u32(cache_base + 3, metaclass_version);
10007 self.code
10008 .instructions
10009 .write_cache_ptr(cache_base + 5, descr_ptr);
10010 self.code
10011 .instructions
10012 .write_cache_u32(cache_base + 1, type_version);
10013 }
10014 }
10015
10016 #[inline]
10017 unsafe fn write_cached_binary_op_extend_descr(
10018 &self,
10019 cache_base: usize,
10020 descr: Option<&'static BinaryOpExtendSpecializationDescr>,
10021 ) {
10022 let ptr = descr.map_or(0, |d| {
10023 d as *const BinaryOpExtendSpecializationDescr as usize
10024 });
10025 unsafe {
10026 self.code
10027 .instructions
10028 .write_cache_ptr(cache_base + BINARY_OP_EXTEND_EXTERNAL_CACHE_OFFSET, ptr);
10029 }
10030 }
10031
10032 #[inline]
10033 fn read_cached_binary_op_extend_descr(
10034 &self,
10035 cache_base: usize,
10036 ) -> Option<&'static BinaryOpExtendSpecializationDescr> {
10037 let ptr = self
10038 .code
10039 .instructions
10040 .read_cache_ptr(cache_base + BINARY_OP_EXTEND_EXTERNAL_CACHE_OFFSET);
10041 if ptr == 0 {
10042 return None;
10043 }
10044 Some(unsafe { &*(ptr as *const BinaryOpExtendSpecializationDescr) })
10046 }
10047
10048 #[inline]
10049 fn binary_op_extended_specialization(
10050 &self,
10051 op: bytecode::BinaryOperator,
10052 lhs: &PyObject,
10053 rhs: &PyObject,
10054 vm: &VirtualMachine,
10055 ) -> Option<&'static BinaryOpExtendSpecializationDescr> {
10056 BINARY_OP_EXTEND_DESCRIPTORS
10057 .iter()
10058 .find(|d| d.oparg == op && (d.guard)(lhs, rhs, vm))
10059 }
10060
10061 fn load_attr(&mut self, vm: &VirtualMachine, oparg: LoadAttr) -> FrameResult {
10062 self.adaptive(|s, ii, cb| s.specialize_load_attr(vm, oparg, ii, cb));
10063 self.load_attr_slow(vm, oparg)
10064 }
10065
10066 fn specialize_load_attr(
10067 &mut self,
10068 _vm: &VirtualMachine,
10069 oparg: LoadAttr,
10070 instr_idx: usize,
10071 cache_base: usize,
10072 ) {
10073 if !matches!(
10075 self.code.instructions.read_op(instr_idx),
10076 Instruction::LoadAttr { .. }
10077 ) {
10078 return;
10079 }
10080 let obj = self.top_value();
10081 let cls = obj.class();
10082
10083 if obj.downcast_ref::<PyType>().is_some() {
10085 self.specialize_class_load_attr(_vm, oparg, instr_idx, cache_base);
10086 return;
10087 }
10088
10089 let type_version = cls.version_for_specialization(_vm);
10093 if type_version == 0 {
10094 unsafe {
10095 self.code.instructions.write_adaptive_counter(
10096 cache_base,
10097 bytecode::adaptive_counter_backoff(
10098 self.code.instructions.read_adaptive_counter(cache_base),
10099 ),
10100 );
10101 }
10102 return;
10103 }
10104
10105 let attr_name = self.code.names[oparg.name_idx() as usize];
10106
10107 if let Some(module) = obj.downcast_ref_if_exact::<PyModule>(_vm) {
10113 let module_dict = module.dict();
10114 if cls.get_attr(attr_name).is_none()
10115 && let Some((keys_version, index)) = module_dict.module_attr_cache(attr_name, _vm)
10116 {
10117 unsafe {
10120 self.code
10121 .instructions
10122 .write_cache_ptr(cache_base + 6, type_version as usize);
10123 self.code
10124 .instructions
10125 .write_cache_ptr(cache_base + 7, keys_version as usize);
10126 self.code
10127 .instructions
10128 .write_cache_ptr(cache_base + 8, usize::from(index));
10129 }
10130 self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrModule);
10131 } else {
10132 self.cooldown_adaptive_at(cache_base);
10133 }
10134 return;
10135 }
10136
10137 let is_default_getattro = cls.slots().getattro.load().is_some_and(|f| {
10139 crate::types::fn_addr(f)
10140 == crate::types::fn_addr(PyBaseObject::getattro as crate::types::GetattroFunc)
10141 });
10142 if !is_default_getattro {
10143 let getattribute = cls.get_attr(identifier!(_vm, __getattribute__));
10144 if !oparg.is_method()
10145 && !self.specialization_eval_frame_active(_vm)
10146 && cls.get_attr(identifier!(_vm, __getattr__)).is_none()
10147 && let Some(getattribute) = getattribute
10148 && let Some(func) = getattribute.downcast_ref_if_exact::<PyFunction>(_vm)
10149 && func.can_specialize_call(2)
10150 {
10151 let func_version = func.get_version_for_current_state();
10152 if func_version != 0 {
10153 let func_ptr = &*getattribute as *const PyObject as usize;
10154 unsafe {
10155 self.code
10156 .instructions
10157 .write_cache_u32(cache_base + 3, func_version);
10158 self.write_cached_descriptor(cache_base, type_version, func_ptr);
10159 }
10160 self.specialize_at(
10161 instr_idx,
10162 cache_base,
10163 Instruction::LoadAttrGetattributeOverridden,
10164 );
10165 return;
10166 }
10167 }
10168 unsafe {
10169 self.code.instructions.write_adaptive_counter(
10170 cache_base,
10171 bytecode::adaptive_counter_backoff(
10172 self.code.instructions.read_adaptive_counter(cache_base),
10173 ),
10174 );
10175 }
10176 return;
10177 }
10178
10179 let cls_attr = cls.get_attr(attr_name);
10180 let class_has_dict = cls.slots().flags.has_feature(PyTypeFlags::HAS_DICT);
10181
10182 if oparg.is_method() {
10183 if let Some(ref descr) = cls_attr
10185 && descr
10186 .class()
10187 .slots
10188 .flags
10189 .has_feature(PyTypeFlags::METHOD_DESCRIPTOR)
10190 {
10191 let descr_ptr = &**descr as *const PyObject as usize;
10192 unsafe {
10193 self.write_cached_descriptor(cache_base, type_version, descr_ptr);
10194 }
10195
10196 let new_op = if !class_has_dict {
10197 Instruction::LoadAttrMethodNoDict
10198 } else if obj.dict().is_none() {
10199 Instruction::LoadAttrMethodLazyDict
10200 } else {
10201 Instruction::LoadAttrMethodWithValues
10202 };
10203 self.specialize_at(instr_idx, cache_base, new_op);
10204 return;
10205 }
10206 unsafe {
10208 self.code.instructions.write_adaptive_counter(
10209 cache_base,
10210 bytecode::adaptive_counter_backoff(
10211 self.code.instructions.read_adaptive_counter(cache_base),
10212 ),
10213 );
10214 }
10215 } else {
10216 let has_data_descr = cls_attr.as_ref().is_some_and(|descr| {
10218 let descr_cls = descr.class();
10219 descr_cls.slots().descr_get.load().is_some()
10220 && descr_cls.slots().descr_set.load().is_some()
10221 });
10222 let has_descr_get = cls_attr
10223 .as_ref()
10224 .is_some_and(|descr| descr.class().slots().descr_get.load().is_some());
10225
10226 if has_data_descr {
10227 if let Some(ref descr) = cls_attr
10233 && let Some(member_descr) = descr.downcast_ref::<PyMemberDescriptor>()
10234 && let Some(offset) = member_descr.slot_offset()
10235 && !member_descr.member.audit_read()
10236 && cls.fast_issubclass(&member_descr.common.typ)
10237 {
10238 unsafe {
10239 self.code
10240 .instructions
10241 .write_cache_u32(cache_base + 1, type_version);
10242 self.code
10243 .instructions
10244 .write_cache_u32(cache_base + 3, offset as u32);
10245 }
10246 self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrSlot);
10247 } else if let Some(ref descr) = cls_attr
10248 && let Some(prop) = descr.downcast_ref::<PyProperty>()
10249 && let Some(fget) = prop.get_fget()
10250 && let Some(func) = fget.downcast_ref_if_exact::<PyFunction>(_vm)
10251 && func.can_specialize_call(1)
10252 && !self.specialization_eval_frame_active(_vm)
10253 {
10254 let fget_ptr = &*fget as *const PyObject as usize;
10256 unsafe {
10257 self.write_cached_descriptor(cache_base, type_version, fget_ptr);
10258 }
10259 self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrProperty);
10260 } else {
10261 unsafe {
10262 self.code.instructions.write_adaptive_counter(
10263 cache_base,
10264 bytecode::adaptive_counter_backoff(
10265 self.code.instructions.read_adaptive_counter(cache_base),
10266 ),
10267 );
10268 }
10269 }
10270 } else if has_descr_get {
10271 unsafe {
10273 self.code.instructions.write_adaptive_counter(
10274 cache_base,
10275 bytecode::adaptive_counter_backoff(
10276 self.code.instructions.read_adaptive_counter(cache_base),
10277 ),
10278 );
10279 }
10280 } else if class_has_dict {
10281 if let Some(ref descr) = cls_attr {
10282 let descr_ptr = &**descr as *const PyObject as usize;
10284 unsafe {
10285 self.write_cached_descriptor(cache_base, type_version, descr_ptr);
10286 }
10287 self.specialize_at(
10288 instr_idx,
10289 cache_base,
10290 Instruction::LoadAttrNondescriptorWithValues,
10291 );
10292 } else {
10293 let instance_attr_hint = if let Some(dict) = obj.dict() {
10301 match dict.get_item_opt_refresh_hint(attr_name, 0, _vm) {
10302 Ok(present) => present.map(|(_, refreshed)| refreshed.unwrap_or(0)),
10303 Err(_) => {
10304 unsafe {
10305 self.code.instructions.write_adaptive_counter(
10306 cache_base,
10307 bytecode::adaptive_counter_backoff(
10308 self.code
10309 .instructions
10310 .read_adaptive_counter(cache_base),
10311 ),
10312 );
10313 }
10314 return;
10315 }
10316 }
10317 } else {
10318 None
10319 };
10320 if let Some(hint) = instance_attr_hint {
10321 unsafe {
10322 self.code
10323 .instructions
10324 .write_cache_u32(cache_base + 1, type_version);
10325 self.code.instructions.write_cache_u16(cache_base + 3, hint);
10326 }
10327 self.specialize_at(instr_idx, cache_base, Instruction::LoadAttrWithHint);
10328 } else {
10329 self.cooldown_adaptive_at(cache_base);
10330 }
10331 }
10332 } else if let Some(ref descr) = cls_attr {
10333 let descr_ptr = &**descr as *const PyObject as usize;
10335 unsafe {
10336 self.write_cached_descriptor(cache_base, type_version, descr_ptr);
10337 }
10338 self.specialize_at(
10339 instr_idx,
10340 cache_base,
10341 Instruction::LoadAttrNondescriptorNoDict,
10342 );
10343 } else {
10344 self.cooldown_adaptive_at(cache_base);
10346 }
10347 }
10348 }
10349
10350 fn specialize_class_load_attr(
10351 &mut self,
10352 _vm: &VirtualMachine,
10353 oparg: LoadAttr,
10354 instr_idx: usize,
10355 cache_base: usize,
10356 ) {
10357 let obj = self.top_value();
10358 let owner_type = obj.downcast_ref::<PyType>().unwrap();
10359 let attr_name = self.code.names[oparg.name_idx() as usize];
10360
10361 let mcl = obj.class();
10363 let (mcl_attr, mut metaclass_version) = mcl.lookup_ref_and_version_interned(attr_name, _vm);
10364 if let Some(ref attr) = mcl_attr {
10365 let attr_class = attr.class();
10366 if attr_class.slots().descr_set.load().is_some() {
10367 unsafe {
10369 self.code.instructions.write_adaptive_counter(
10370 cache_base,
10371 bytecode::adaptive_counter_backoff(
10372 self.code.instructions.read_adaptive_counter(cache_base),
10373 ),
10374 );
10375 }
10376 return;
10377 }
10378 }
10379 if !mcl.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
10380 if metaclass_version == 0 {
10381 unsafe {
10382 self.code.instructions.write_adaptive_counter(
10383 cache_base,
10384 bytecode::adaptive_counter_backoff(
10385 self.code.instructions.read_adaptive_counter(cache_base),
10386 ),
10387 );
10388 }
10389 return;
10390 }
10391 } else {
10392 metaclass_version = 0;
10393 }
10394
10395 let (cls_attr, type_version) = owner_type.lookup_ref_and_version_interned(attr_name, _vm);
10396 if type_version == 0 {
10397 unsafe {
10398 self.code.instructions.write_adaptive_counter(
10399 cache_base,
10400 bytecode::adaptive_counter_backoff(
10401 self.code.instructions.read_adaptive_counter(cache_base),
10402 ),
10403 );
10404 }
10405 return;
10406 }
10407 if let Some(ref descr) = cls_attr {
10408 let descr_class = descr.class();
10409 let has_descr_get = descr_class.slots.descr_get.load().is_some();
10410 if !has_descr_get {
10411 let descr_ptr = &**descr as *const PyObject as usize;
10413 let new_op = if metaclass_version == 0 {
10414 Instruction::LoadAttrClass
10415 } else {
10416 Instruction::LoadAttrClassWithMetaclassCheck
10417 };
10418 unsafe {
10419 if metaclass_version == 0 {
10420 self.write_cached_descriptor(cache_base, type_version, descr_ptr);
10421 } else {
10422 self.write_cached_descriptor_with_metaclass(
10423 cache_base,
10424 type_version,
10425 metaclass_version,
10426 descr_ptr,
10427 );
10428 }
10429 }
10430 self.specialize_at(instr_idx, cache_base, new_op);
10431 return;
10432 }
10433 }
10434
10435 unsafe {
10437 self.code.instructions.write_adaptive_counter(
10438 cache_base,
10439 bytecode::adaptive_counter_backoff(
10440 self.code.instructions.read_adaptive_counter(cache_base),
10441 ),
10442 );
10443 }
10444 }
10445
10446 fn load_attr_slow(&mut self, vm: &VirtualMachine, oparg: LoadAttr) -> FrameResult {
10447 let attr_name = self.code.names[oparg.name_idx() as usize];
10448
10449 if !oparg.is_method() {
10450 let parent = self.pop_stackref();
10453 let obj = parent.get_attr(attr_name, vm)?;
10454 self.push_value(obj);
10455 return Ok(None);
10456 }
10457
10458 let parent = self.pop_value();
10461 match PyMethod::get(parent.clone(), attr_name, vm)? {
10462 PyMethod::Function { target: _, func } => {
10463 self.push_value(func);
10464 self.push_value(parent);
10465 }
10466 PyMethod::Attribute(val) => {
10467 self.push_value(val);
10468 self.push_null();
10469 }
10470 }
10471 Ok(None)
10472 }
10473
10474 fn specialize_binary_op(
10475 &mut self,
10476 vm: &VirtualMachine,
10477 op: bytecode::BinaryOperator,
10478 instr_idx: usize,
10479 cache_base: usize,
10480 ) {
10481 if !matches!(
10482 self.code.instructions.read_op(instr_idx),
10483 Instruction::BinaryOp { .. }
10484 ) {
10485 return;
10486 }
10487 let b = self.top_value();
10488 let a = self.nth_value(1);
10489 unsafe {
10491 self.write_cached_binary_op_extend_descr(cache_base, None);
10492 }
10493 let mut cached_extend_descr = None;
10494
10495 let new_op = match op {
10496 bytecode::BinaryOperator::Add => {
10497 if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10498 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10499 {
10500 Some(Instruction::BinaryOpAddInt)
10501 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10502 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10503 {
10504 Some(Instruction::BinaryOpAddFloat)
10505 } else if a.downcast_ref_if_exact::<PyStr>(vm).is_some()
10506 && b.downcast_ref_if_exact::<PyStr>(vm).is_some()
10507 {
10508 if self
10509 .binary_op_inplace_unicode_target_local(cache_base, a)
10510 .is_some()
10511 {
10512 Some(Instruction::BinaryOpInplaceAddUnicode)
10513 } else {
10514 Some(Instruction::BinaryOpAddUnicode)
10515 }
10516 } else if let Some(descr) = self.binary_op_extended_specialization(op, a, b, vm) {
10517 cached_extend_descr = Some(descr);
10518 Some(Instruction::BinaryOpExtend)
10519 } else {
10520 None
10521 }
10522 }
10523 bytecode::BinaryOperator::Subtract => {
10524 if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10525 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10526 {
10527 Some(Instruction::BinaryOpSubtractInt)
10528 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10529 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10530 {
10531 Some(Instruction::BinaryOpSubtractFloat)
10532 } else if let Some(descr) = self.binary_op_extended_specialization(op, a, b, vm) {
10533 cached_extend_descr = Some(descr);
10534 Some(Instruction::BinaryOpExtend)
10535 } else {
10536 None
10537 }
10538 }
10539 bytecode::BinaryOperator::Multiply => {
10540 if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10541 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10542 {
10543 Some(Instruction::BinaryOpMultiplyInt)
10544 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10545 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10546 {
10547 Some(Instruction::BinaryOpMultiplyFloat)
10548 } else if let Some(descr) = self.binary_op_extended_specialization(op, a, b, vm) {
10549 cached_extend_descr = Some(descr);
10550 Some(Instruction::BinaryOpExtend)
10551 } else {
10552 None
10553 }
10554 }
10555 bytecode::BinaryOperator::TrueDivide => {
10556 if let Some(descr) = self.binary_op_extended_specialization(op, a, b, vm) {
10557 cached_extend_descr = Some(descr);
10558 Some(Instruction::BinaryOpExtend)
10559 } else {
10560 None
10561 }
10562 }
10563 bytecode::BinaryOperator::Subscr => {
10564 let b_is_nonnegative_int = b
10565 .downcast_ref_if_exact::<PyInt>(vm)
10566 .is_some_and(|i| specialization_nonnegative_compact_index(i, vm).is_some());
10567 if a.downcast_ref_if_exact::<PyList>(vm).is_some() && b_is_nonnegative_int {
10568 Some(Instruction::BinaryOpSubscrListInt)
10569 } else if a.downcast_ref_if_exact::<PyTuple>(vm).is_some() && b_is_nonnegative_int {
10570 Some(Instruction::BinaryOpSubscrTupleInt)
10571 } else if a.downcast_ref_if_exact::<PyDict>(vm).is_some() {
10572 Some(Instruction::BinaryOpSubscrDict)
10573 } else if a.downcast_ref_if_exact::<PyStr>(vm).is_some() && b_is_nonnegative_int {
10574 Some(Instruction::BinaryOpSubscrStrInt)
10575 } else if a.downcast_ref_if_exact::<PyList>(vm).is_some()
10576 && b.downcast_ref::<PySlice>().is_some()
10577 {
10578 Some(Instruction::BinaryOpSubscrListSlice)
10579 } else {
10580 let cls = a.class();
10581 if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE)
10584 && !self.specialization_eval_frame_active(vm)
10585 {
10586 let (getitem, type_version) =
10587 cls.lookup_ref_and_version_interned(identifier!(vm, __getitem__), vm);
10588 if type_version != 0
10589 && let Some(getitem) = getitem
10590 && let Some(func) = getitem.downcast_ref_if_exact::<PyFunction>(vm)
10591 && func.can_specialize_call(2)
10592 && cls.cache_getitem_for_specialization(
10593 func.to_owned(),
10594 type_version,
10595 vm,
10596 )
10597 {
10598 unsafe {
10601 self.code
10602 .instructions
10603 .write_cache_u32(cache_base + 1, type_version);
10604 }
10605 Some(Instruction::BinaryOpSubscrGetitem)
10606 } else {
10607 None
10608 }
10609 } else {
10610 None
10611 }
10612 }
10613 }
10614 bytecode::BinaryOperator::InplaceAdd => {
10615 if a.downcast_ref_if_exact::<PyStr>(vm).is_some()
10616 && b.downcast_ref_if_exact::<PyStr>(vm).is_some()
10617 {
10618 if self
10619 .binary_op_inplace_unicode_target_local(cache_base, a)
10620 .is_some()
10621 {
10622 Some(Instruction::BinaryOpInplaceAddUnicode)
10623 } else {
10624 Some(Instruction::BinaryOpAddUnicode)
10625 }
10626 } else if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10627 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10628 {
10629 Some(Instruction::BinaryOpAddInt)
10630 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10631 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10632 {
10633 Some(Instruction::BinaryOpAddFloat)
10634 } else {
10635 None
10636 }
10637 }
10638 bytecode::BinaryOperator::InplaceSubtract => {
10639 if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10640 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10641 {
10642 Some(Instruction::BinaryOpSubtractInt)
10643 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10644 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10645 {
10646 Some(Instruction::BinaryOpSubtractFloat)
10647 } else {
10648 None
10649 }
10650 }
10651 bytecode::BinaryOperator::InplaceMultiply => {
10652 if a.downcast_ref_if_exact::<PyInt>(vm).is_some()
10653 && b.downcast_ref_if_exact::<PyInt>(vm).is_some()
10654 {
10655 Some(Instruction::BinaryOpMultiplyInt)
10656 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10657 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
10658 {
10659 Some(Instruction::BinaryOpMultiplyFloat)
10660 } else {
10661 None
10662 }
10663 }
10664 bytecode::BinaryOperator::And
10665 | bytecode::BinaryOperator::Or
10666 | bytecode::BinaryOperator::Xor
10667 | bytecode::BinaryOperator::InplaceAnd
10668 | bytecode::BinaryOperator::InplaceOr
10669 | bytecode::BinaryOperator::InplaceXor => {
10670 if let Some(descr) = self.binary_op_extended_specialization(op, a, b, vm) {
10671 cached_extend_descr = Some(descr);
10672 Some(Instruction::BinaryOpExtend)
10673 } else {
10674 None
10675 }
10676 }
10677 _ => None,
10678 };
10679
10680 if matches!(new_op, Some(Instruction::BinaryOpExtend)) {
10681 unsafe {
10682 self.write_cached_binary_op_extend_descr(cache_base, cached_extend_descr);
10683 }
10684 }
10685 self.commit_specialization(instr_idx, cache_base, new_op);
10686 }
10687
10688 #[inline]
10689 fn binary_op_inplace_unicode_target_local(
10690 &self,
10691 cache_base: usize,
10692 left: &PyObject,
10693 ) -> Option<usize> {
10694 let next_idx = cache_base + Instruction::from(Opcode::BinaryOp).cache_entries();
10695 let unit = self.code.instructions.get(next_idx)?;
10696 let next_op = unit.op.to_base().unwrap_or(unit.op);
10697 if !matches!(next_op, Instruction::StoreFast { .. }) {
10698 return None;
10699 }
10700 let local_idx = usize::from(u8::from(unit.arg));
10701 self.localsplus
10702 .fastlocals()
10703 .get(local_idx)
10704 .and_then(|slot| slot.as_ref())
10705 .filter(|local| local.is(left))
10706 .map(|_| local_idx)
10707 }
10708
10709 #[inline]
10711 fn adaptive(&mut self, specialize: impl FnOnce(&mut Self, usize, usize)) {
10712 let instr_idx = self.lasti() as usize - 1;
10713 let cache_base = instr_idx + 1;
10714 let counter = self.code.instructions.read_adaptive_counter(cache_base);
10715 if bytecode::adaptive_counter_triggers(counter) {
10716 specialize(self, instr_idx, cache_base);
10717 } else {
10718 unsafe {
10719 self.code.instructions.write_adaptive_counter(
10720 cache_base,
10721 bytecode::advance_adaptive_counter(counter),
10722 );
10723 }
10724 }
10725 }
10726
10727 #[inline]
10729 fn specialize_at(&mut self, instr_idx: usize, cache_base: usize, new_op: Instruction) {
10730 unsafe {
10731 self.code
10732 .instructions
10733 .write_adaptive_counter(cache_base, ADAPTIVE_COOLDOWN_VALUE);
10734 self.code.instructions.replace_op(instr_idx, new_op);
10735 }
10736 }
10737
10738 #[inline]
10739 fn cooldown_adaptive_at(&mut self, cache_base: usize) {
10740 unsafe {
10741 self.code
10742 .instructions
10743 .write_adaptive_counter(cache_base, ADAPTIVE_COOLDOWN_VALUE);
10744 }
10745 }
10746
10747 #[inline]
10749 fn commit_specialization(
10750 &mut self,
10751 instr_idx: usize,
10752 cache_base: usize,
10753 new_op: Option<Instruction>,
10754 ) {
10755 if let Some(new_op) = new_op {
10756 self.specialize_at(instr_idx, cache_base, new_op);
10757 } else {
10758 unsafe {
10759 self.code.instructions.write_adaptive_counter(
10760 cache_base,
10761 bytecode::adaptive_counter_backoff(
10762 self.code.instructions.read_adaptive_counter(cache_base),
10763 ),
10764 );
10765 }
10766 }
10767 }
10768
10769 #[inline]
10772 fn execute_binary_op_int(
10773 &mut self,
10774 vm: &VirtualMachine,
10775 op: impl FnOnce(&Py<PyInt>, &Py<PyInt>, &VirtualMachine) -> PyObjectRef,
10776 deopt_op: bytecode::BinaryOperator,
10777 ) -> FrameResult {
10778 let b = self.top_value();
10779 let a = self.nth_value(1);
10780 if let (Some(a_int), Some(b_int)) = (
10781 a.downcast_ref_if_exact::<PyInt>(vm),
10782 b.downcast_ref_if_exact::<PyInt>(vm),
10783 ) {
10784 let result = op(a_int, b_int, vm);
10785 self.pop_stackref();
10786 self.pop_stackref();
10787 self.push_value(result);
10788 Ok(None)
10789 } else {
10790 self.execute_bin_op(vm, deopt_op)
10791 }
10792 }
10793
10794 #[inline]
10797 fn execute_binary_op_float(
10798 &mut self,
10799 vm: &VirtualMachine,
10800 op: impl FnOnce(f64, f64) -> f64,
10801 deopt_op: bytecode::BinaryOperator,
10802 ) -> FrameResult {
10803 let b = self.top_value();
10804 let a = self.nth_value(1);
10805 if let (Some(a_f), Some(b_f)) = (
10806 a.downcast_ref_if_exact::<PyFloat>(vm),
10807 b.downcast_ref_if_exact::<PyFloat>(vm),
10808 ) {
10809 let result = op(a_f.to_f64(), b_f.to_f64());
10810 self.pop_stackref();
10811 self.pop_stackref();
10812 self.push_value(vm.ctx.new_float(result).into());
10813 Ok(None)
10814 } else {
10815 self.execute_bin_op(vm, deopt_op)
10816 }
10817 }
10818
10819 fn specialize_call(
10820 &mut self,
10821 vm: &VirtualMachine,
10822 nargs: u32,
10823 instr_idx: usize,
10824 cache_base: usize,
10825 ) {
10826 if !matches!(
10827 self.code.instructions.read_op(instr_idx),
10828 Instruction::Call { .. }
10829 ) {
10830 return;
10831 }
10832 let stack_len = self.localsplus.stack_len();
10836 let self_or_null_is_some = self
10837 .localsplus
10838 .stack_index(stack_len - nargs as usize - 1)
10839 .is_some();
10840 let callable = self.nth_value(nargs + 1);
10841
10842 if let Some(func) = callable.downcast_ref_if_exact::<PyFunction>(vm) {
10843 if self.specialization_eval_frame_active(vm) || func.is_jitted() {
10844 unsafe {
10845 self.code.instructions.write_adaptive_counter(
10846 cache_base,
10847 bytecode::adaptive_counter_backoff(
10848 self.code.instructions.read_adaptive_counter(cache_base),
10849 ),
10850 );
10851 }
10852 return;
10853 }
10854 if !func.is_optimized_for_call_specialization() {
10855 unsafe {
10856 self.code.instructions.write_adaptive_counter(
10857 cache_base,
10858 bytecode::adaptive_counter_backoff(
10859 self.code.instructions.read_adaptive_counter(cache_base),
10860 ),
10861 );
10862 }
10863 return;
10864 }
10865 let version = func.get_version_for_current_state();
10866 if version == 0 {
10867 unsafe {
10868 self.code.instructions.write_adaptive_counter(
10869 cache_base,
10870 bytecode::adaptive_counter_backoff(
10871 self.code.instructions.read_adaptive_counter(cache_base),
10872 ),
10873 );
10874 }
10875 return;
10876 }
10877
10878 let effective_nargs = if self_or_null_is_some {
10879 nargs + 1
10880 } else {
10881 nargs
10882 };
10883
10884 let new_op = if func.can_specialize_call(effective_nargs) {
10885 Instruction::CallPyExactArgs
10886 } else {
10887 Instruction::CallPyGeneral
10888 };
10889 unsafe {
10890 self.code
10891 .instructions
10892 .write_cache_u32(cache_base + 1, version);
10893 }
10894 self.specialize_at(instr_idx, cache_base, new_op);
10895 return;
10896 }
10897
10898 if !self_or_null_is_some
10900 && let Some(bound_method) = callable.downcast_ref_if_exact::<PyBoundMethod>(vm)
10901 {
10902 if let Some(func) = bound_method
10903 .function_obj()
10904 .downcast_ref_if_exact::<PyFunction>(vm)
10905 {
10906 if self.specialization_eval_frame_active(vm) || func.is_jitted() {
10907 unsafe {
10908 self.code.instructions.write_adaptive_counter(
10909 cache_base,
10910 bytecode::adaptive_counter_backoff(
10911 self.code.instructions.read_adaptive_counter(cache_base),
10912 ),
10913 );
10914 }
10915 return;
10916 }
10917 if !func.is_optimized_for_call_specialization() {
10918 unsafe {
10919 self.code.instructions.write_adaptive_counter(
10920 cache_base,
10921 bytecode::adaptive_counter_backoff(
10922 self.code.instructions.read_adaptive_counter(cache_base),
10923 ),
10924 );
10925 }
10926 return;
10927 }
10928 let version = func.get_version_for_current_state();
10929 if version == 0 {
10930 unsafe {
10931 self.code.instructions.write_adaptive_counter(
10932 cache_base,
10933 bytecode::adaptive_counter_backoff(
10934 self.code.instructions.read_adaptive_counter(cache_base),
10935 ),
10936 );
10937 }
10938 return;
10939 }
10940
10941 let new_op = if func.can_specialize_call(nargs + 1) {
10942 Instruction::CallBoundMethodExactArgs
10943 } else {
10944 Instruction::CallBoundMethodGeneral
10945 };
10946 unsafe {
10947 self.code
10948 .instructions
10949 .write_cache_u32(cache_base + 1, version);
10950 }
10951 self.specialize_at(instr_idx, cache_base, new_op);
10952 } else {
10953 unsafe {
10956 self.code.instructions.write_adaptive_counter(
10957 cache_base,
10958 bytecode::adaptive_counter_backoff(
10959 self.code.instructions.read_adaptive_counter(cache_base),
10960 ),
10961 );
10962 }
10963 }
10964 return;
10965 }
10966
10967 if let Some(descr) = callable.downcast_ref_if_exact::<PyMethodDescriptor>(vm) {
10969 let call_cache_entries = Instruction::CallListAppend.cache_entries();
10970 let next_idx = cache_base + call_cache_entries;
10971 let next_is_pop_top = if next_idx < self.code.instructions.len() {
10972 let next_op = self.code.instructions.read_op(next_idx);
10973 matches!(next_op.to_base().unwrap_or(next_op), Instruction::PopTop)
10974 } else {
10975 false
10976 };
10977
10978 let call_conv = descr.method.flags
10979 & (PyMethodFlags::VARARGS
10980 | PyMethodFlags::FASTCALL
10981 | PyMethodFlags::NOARGS
10982 | PyMethodFlags::O
10983 | PyMethodFlags::KEYWORDS);
10984 let total_nargs = nargs + u32::from(self_or_null_is_some);
10985
10986 let new_op = if call_conv == PyMethodFlags::NOARGS {
10987 if total_nargs != 1 {
10988 unsafe {
10989 self.code.instructions.write_adaptive_counter(
10990 cache_base,
10991 bytecode::adaptive_counter_backoff(
10992 self.code.instructions.read_adaptive_counter(cache_base),
10993 ),
10994 );
10995 }
10996 return;
10997 }
10998 Instruction::CallMethodDescriptorNoargs
10999 } else if call_conv == PyMethodFlags::O {
11000 if total_nargs != 2 {
11001 unsafe {
11002 self.code.instructions.write_adaptive_counter(
11003 cache_base,
11004 bytecode::adaptive_counter_backoff(
11005 self.code.instructions.read_adaptive_counter(cache_base),
11006 ),
11007 );
11008 }
11009 return;
11010 }
11011 if self_or_null_is_some
11012 && nargs == 1
11013 && next_is_pop_top
11014 && vm
11015 .callable_cache
11016 .list_append
11017 .as_ref()
11018 .is_some_and(|list_append| callable.is(list_append))
11019 {
11020 Instruction::CallListAppend
11021 } else {
11022 Instruction::CallMethodDescriptorO
11023 }
11024 } else if call_conv == PyMethodFlags::FASTCALL {
11025 Instruction::CallMethodDescriptorFast
11026 } else if call_conv == (PyMethodFlags::FASTCALL | PyMethodFlags::KEYWORDS) {
11027 Instruction::CallMethodDescriptorFastWithKeywords
11028 } else {
11029 Instruction::CallNonPyGeneral
11030 };
11031 self.specialize_at(instr_idx, cache_base, new_op);
11032 return;
11033 }
11034
11035 if let Some(native) = callable.downcast_ref_if_exact::<PyNativeFunction>(vm) {
11037 let effective_nargs = nargs + u32::from(self_or_null_is_some);
11038 let call_conv = native.value.flags
11039 & (PyMethodFlags::VARARGS
11040 | PyMethodFlags::FASTCALL
11041 | PyMethodFlags::NOARGS
11042 | PyMethodFlags::O
11043 | PyMethodFlags::KEYWORDS);
11044 let new_op = if call_conv == PyMethodFlags::O {
11045 if effective_nargs != 1 {
11046 unsafe {
11047 self.code.instructions.write_adaptive_counter(
11048 cache_base,
11049 bytecode::adaptive_counter_backoff(
11050 self.code.instructions.read_adaptive_counter(cache_base),
11051 ),
11052 );
11053 }
11054 return;
11055 }
11056 if native.zelf.is_none()
11057 && nargs == 1
11058 && vm
11059 .callable_cache
11060 .len
11061 .as_ref()
11062 .is_some_and(|len_callable| callable.is(len_callable))
11063 {
11064 Instruction::CallLen
11065 } else {
11066 Instruction::CallBuiltinO
11067 }
11068 } else if call_conv == PyMethodFlags::FASTCALL {
11069 if native.zelf.is_none()
11070 && effective_nargs == 2
11071 && vm
11072 .callable_cache
11073 .isinstance
11074 .as_ref()
11075 .is_some_and(|isinstance_callable| callable.is(isinstance_callable))
11076 {
11077 Instruction::CallIsinstance
11078 } else {
11079 Instruction::CallBuiltinFast
11080 }
11081 } else if call_conv == (PyMethodFlags::FASTCALL | PyMethodFlags::KEYWORDS) {
11082 Instruction::CallBuiltinFastWithKeywords
11083 } else {
11084 Instruction::CallNonPyGeneral
11085 };
11086 self.specialize_at(instr_idx, cache_base, new_op);
11087 return;
11088 }
11089
11090 if let Some(cls) = callable.downcast_ref::<PyType>() {
11092 if cls.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
11093 if !self_or_null_is_some && nargs == 1 {
11094 let new_op = if callable.is(&vm.ctx.types.type_type.as_object()) {
11095 Some(Instruction::CallType1)
11096 } else if callable.is(&vm.ctx.types.str_type.as_object()) {
11097 Some(Instruction::CallStr1)
11098 } else if callable.is(&vm.ctx.types.tuple_type.as_object()) {
11099 Some(Instruction::CallTuple1)
11100 } else {
11101 None
11102 };
11103 if let Some(new_op) = new_op {
11104 self.specialize_at(instr_idx, cache_base, new_op);
11105 return;
11106 }
11107 }
11108 if cls.slots().vectorcall.load().is_some() {
11109 self.specialize_at(instr_idx, cache_base, Instruction::CallBuiltinClass);
11110 return;
11111 }
11112 self.specialize_at(instr_idx, cache_base, Instruction::CallNonPyGeneral);
11113 return;
11114 }
11115
11116 if !callable.class().is(vm.ctx.types.type_type) {
11119 self.specialize_at(instr_idx, cache_base, Instruction::CallNonPyGeneral);
11120 return;
11121 }
11122
11123 if !self_or_null_is_some && cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) {
11125 let type_version = cls.version_for_specialization(vm);
11129 let object_new = vm.ctx.types.object_type.slots().new.load();
11130 let cls_new = cls.slots().new.load();
11131 let object_alloc = vm.ctx.types.object_type.slots().alloc.load();
11132 let cls_alloc = cls.slots().alloc.load();
11133 if let (Some(cls_new_fn), Some(obj_new_fn), Some(cls_alloc_fn), Some(obj_alloc_fn)) =
11134 (cls_new, object_new, cls_alloc, object_alloc)
11135 && crate::types::fn_addr(cls_new_fn) == crate::types::fn_addr(obj_new_fn)
11136 && crate::types::fn_addr(cls_alloc_fn) == crate::types::fn_addr(obj_alloc_fn)
11137 {
11138 if type_version == 0 {
11139 unsafe {
11140 self.code.instructions.write_adaptive_counter(
11141 cache_base,
11142 bytecode::adaptive_counter_backoff(
11143 self.code.instructions.read_adaptive_counter(cache_base),
11144 ),
11145 );
11146 }
11147 return;
11148 }
11149 let init = cls.get_attr(identifier!(vm, __init__));
11150 if let Some(init) = init
11151 && let Some(init_func) = init.downcast_ref_if_exact::<PyFunction>(vm)
11152 && init_func.can_specialize_call(nargs + 1)
11153 && !init_func.is_generator_like()
11154 && cls.cache_init_for_specialization(init_func.to_owned(), type_version, vm)
11155 {
11156 unsafe {
11157 self.code
11158 .instructions
11159 .write_cache_u32(cache_base + 1, type_version);
11160 }
11161 self.specialize_at(
11162 instr_idx,
11163 cache_base,
11164 Instruction::CallAllocAndEnterInit,
11165 );
11166 return;
11167 }
11168 }
11169 }
11170 self.specialize_at(instr_idx, cache_base, Instruction::CallNonPyGeneral);
11171 return;
11172 }
11173
11174 self.specialize_at(instr_idx, cache_base, Instruction::CallNonPyGeneral);
11176 }
11177
11178 fn specialize_call_kw(
11179 &mut self,
11180 vm: &VirtualMachine,
11181 nargs: u32,
11182 instr_idx: usize,
11183 cache_base: usize,
11184 ) {
11185 if !matches!(
11186 self.code.instructions.read_op(instr_idx),
11187 Instruction::CallKw { .. }
11188 ) {
11189 return;
11190 }
11191 let stack_len = self.localsplus.stack_len();
11194 let self_or_null_is_some = self
11195 .localsplus
11196 .stack_index(stack_len - nargs as usize - 2)
11197 .is_some();
11198 let callable = self.nth_value(nargs + 2);
11199
11200 if let Some(func) = callable.downcast_ref_if_exact::<PyFunction>(vm) {
11201 if self.specialization_eval_frame_active(vm) || func.is_jitted() {
11202 unsafe {
11203 self.code.instructions.write_adaptive_counter(
11204 cache_base,
11205 bytecode::adaptive_counter_backoff(
11206 self.code.instructions.read_adaptive_counter(cache_base),
11207 ),
11208 );
11209 }
11210 return;
11211 }
11212 if !func.is_optimized_for_call_specialization() {
11213 unsafe {
11214 self.code.instructions.write_adaptive_counter(
11215 cache_base,
11216 bytecode::adaptive_counter_backoff(
11217 self.code.instructions.read_adaptive_counter(cache_base),
11218 ),
11219 );
11220 }
11221 return;
11222 }
11223 let version = func.get_version_for_current_state();
11224 if version == 0 {
11225 unsafe {
11226 self.code.instructions.write_adaptive_counter(
11227 cache_base,
11228 bytecode::adaptive_counter_backoff(
11229 self.code.instructions.read_adaptive_counter(cache_base),
11230 ),
11231 );
11232 }
11233 return;
11234 }
11235
11236 unsafe {
11237 self.code
11238 .instructions
11239 .write_cache_u32(cache_base + 1, version);
11240 }
11241 self.specialize_at(instr_idx, cache_base, Instruction::CallKwPy);
11242 return;
11243 }
11244
11245 if !self_or_null_is_some
11246 && let Some(bound_method) = callable.downcast_ref_if_exact::<PyBoundMethod>(vm)
11247 {
11248 if let Some(func) = bound_method
11249 .function_obj()
11250 .downcast_ref_if_exact::<PyFunction>(vm)
11251 {
11252 if self.specialization_eval_frame_active(vm) || func.is_jitted() {
11253 unsafe {
11254 self.code.instructions.write_adaptive_counter(
11255 cache_base,
11256 bytecode::adaptive_counter_backoff(
11257 self.code.instructions.read_adaptive_counter(cache_base),
11258 ),
11259 );
11260 }
11261 return;
11262 }
11263 if !func.is_optimized_for_call_specialization() {
11264 unsafe {
11265 self.code.instructions.write_adaptive_counter(
11266 cache_base,
11267 bytecode::adaptive_counter_backoff(
11268 self.code.instructions.read_adaptive_counter(cache_base),
11269 ),
11270 );
11271 }
11272 return;
11273 }
11274 let version = func.get_version_for_current_state();
11275 if version == 0 {
11276 unsafe {
11277 self.code.instructions.write_adaptive_counter(
11278 cache_base,
11279 bytecode::adaptive_counter_backoff(
11280 self.code.instructions.read_adaptive_counter(cache_base),
11281 ),
11282 );
11283 }
11284 return;
11285 }
11286 unsafe {
11287 self.code
11288 .instructions
11289 .write_cache_u32(cache_base + 1, version);
11290 }
11291 self.specialize_at(instr_idx, cache_base, Instruction::CallKwBoundMethod);
11292 } else {
11293 unsafe {
11296 self.code.instructions.write_adaptive_counter(
11297 cache_base,
11298 bytecode::adaptive_counter_backoff(
11299 self.code.instructions.read_adaptive_counter(cache_base),
11300 ),
11301 );
11302 }
11303 }
11304 return;
11305 }
11306
11307 self.specialize_at(instr_idx, cache_base, Instruction::CallKwNonPy);
11309 }
11310
11311 fn specialize_send(&mut self, vm: &VirtualMachine, instr_idx: usize, cache_base: usize) {
11312 if !matches!(
11313 self.code.instructions.read_op(instr_idx),
11314 Instruction::Send { .. }
11315 ) {
11316 return;
11317 }
11318 let receiver = self.nth_value(1);
11320 let is_exact_gen_or_coro = receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
11321 || receiver.downcast_ref_if_exact::<PyCoroutine>(vm).is_some();
11322 if is_exact_gen_or_coro && !self.specialization_eval_frame_active(vm) {
11323 self.specialize_at(instr_idx, cache_base, Instruction::SendGen);
11324 } else {
11325 unsafe {
11326 self.code.instructions.write_adaptive_counter(
11327 cache_base,
11328 bytecode::adaptive_counter_backoff(
11329 self.code.instructions.read_adaptive_counter(cache_base),
11330 ),
11331 );
11332 }
11333 }
11334 }
11335
11336 fn specialize_load_super_attr(
11337 &mut self,
11338 vm: &VirtualMachine,
11339 oparg: LoadSuperAttr,
11340 instr_idx: usize,
11341 cache_base: usize,
11342 ) {
11343 if !matches!(
11344 self.code.instructions.read_op(instr_idx),
11345 Instruction::LoadSuperAttr { .. }
11346 ) {
11347 return;
11348 }
11349 let global_super = self.nth_value(2);
11351 let class = self.nth_value(1);
11352
11353 if !global_super.is(&vm.ctx.types.super_type.as_object())
11354 || class.downcast_ref::<PyType>().is_none()
11355 {
11356 unsafe {
11357 self.code.instructions.write_adaptive_counter(
11358 cache_base,
11359 bytecode::adaptive_counter_backoff(
11360 self.code.instructions.read_adaptive_counter(cache_base),
11361 ),
11362 );
11363 }
11364 return;
11365 }
11366
11367 let new_op = if oparg.is_load_method() {
11368 Instruction::LoadSuperAttrMethod
11369 } else {
11370 Instruction::LoadSuperAttrAttr
11371 };
11372 self.specialize_at(instr_idx, cache_base, new_op);
11373 }
11374
11375 fn specialize_compare_op(
11376 &mut self,
11377 vm: &VirtualMachine,
11378 op: bytecode::ComparisonOperator,
11379 instr_idx: usize,
11380 cache_base: usize,
11381 ) {
11382 if !matches!(
11383 self.code.instructions.read_op(instr_idx),
11384 Instruction::CompareOp { .. }
11385 ) {
11386 return;
11387 }
11388 let b = self.top_value();
11389 let a = self.nth_value(1);
11390
11391 let new_op = if let (Some(a_int), Some(b_int)) = (
11392 a.downcast_ref_if_exact::<PyInt>(vm),
11393 b.downcast_ref_if_exact::<PyInt>(vm),
11394 ) {
11395 if specialization_compact_int_value(a_int).is_some()
11396 && specialization_compact_int_value(b_int).is_some()
11397 {
11398 Some(Instruction::CompareOpInt)
11399 } else {
11400 None
11401 }
11402 } else if a.downcast_ref_if_exact::<PyFloat>(vm).is_some()
11403 && b.downcast_ref_if_exact::<PyFloat>(vm).is_some()
11404 {
11405 Some(Instruction::CompareOpFloat)
11406 } else if a.downcast_ref_if_exact::<PyStr>(vm).is_some()
11407 && b.downcast_ref_if_exact::<PyStr>(vm).is_some()
11408 && (op == bytecode::ComparisonOperator::Equal
11409 || op == bytecode::ComparisonOperator::NotEqual)
11410 {
11411 Some(Instruction::CompareOpStr)
11412 } else {
11413 None
11414 };
11415
11416 self.commit_specialization(instr_idx, cache_base, new_op);
11417 }
11418
11419 fn compare_op_from_arg(&self, arg: bytecode::OpArg) -> PyComparisonOp {
11422 bytecode::ComparisonOperator::try_from(u32::from(arg))
11423 .unwrap_or(bytecode::ComparisonOperator::Equal)
11424 .into()
11425 }
11426
11427 #[inline]
11431 fn fused_bool_jump(&self, caches: usize, vm: &VirtualMachine) -> Option<FusedBoolJump> {
11440 if self.specialization_eval_frame_active(vm) {
11441 return None;
11442 }
11443
11444 let jump_idx = self.lasti() as usize + caches;
11445 if jump_idx >= self.code.instructions.len() {
11446 return None;
11447 }
11448
11449 let jump = self.code.instructions.read_unit(jump_idx);
11453 let jump_on = match jump.op {
11454 Instruction::PopJumpIfFalse { .. } => false,
11455 Instruction::PopJumpIfTrue { .. } => true,
11456 _ => return None,
11457 };
11458 debug_assert_eq!(jump.op.cache_entries(), 1);
11459 let fallthrough = jump_idx as u32 + 2;
11460 Some(FusedBoolJump {
11461 jump_on,
11462 taken: fallthrough + jump.arg.as_u32(),
11463 fallthrough,
11464 })
11465 }
11466
11467 #[inline]
11471 fn take_fused_bool_jump(&mut self, jump: FusedBoolJump, result: bool) {
11472 let target = if result == jump.jump_on {
11473 jump.taken
11474 } else {
11475 self.not_taken_skipped(jump.fallthrough)
11476 };
11477 self.lasti.store(target, Relaxed);
11478 }
11479
11480 #[inline]
11483 fn push_bool_or_fused_jump(&mut self, caches: usize, result: bool, vm: &VirtualMachine) {
11484 match self.fused_bool_jump(caches, vm) {
11485 Some(jump) => self.take_fused_bool_jump(jump, result),
11486 None => self.push_value(vm.ctx.new_bool(result).into()),
11487 }
11488 }
11489
11490 #[inline]
11497 fn not_taken_skipped(&self, next: u32) -> u32 {
11498 if (next as usize) < self.code.instructions.len()
11499 && matches!(
11500 self.code.instructions.read_op(next as usize),
11501 Instruction::NotTaken
11502 )
11503 {
11504 next + 1
11505 } else {
11506 next
11507 }
11508 }
11509
11510 fn binary_op_from_arg(&self, arg: bytecode::OpArg) -> bytecode::BinaryOperator {
11513 bytecode::BinaryOperator::try_from(u32::from(arg)).unwrap_or(bytecode::BinaryOperator::Add)
11514 }
11515
11516 fn specialize_to_bool(&mut self, vm: &VirtualMachine, instr_idx: usize, cache_base: usize) {
11517 if !matches!(
11518 self.code.instructions.read_op(instr_idx),
11519 Instruction::ToBool
11520 ) {
11521 return;
11522 }
11523 let obj = self.top_value();
11524 let cls = obj.class();
11525
11526 let new_op = if cls.is(vm.ctx.types.bool_type) {
11527 Some(Instruction::ToBoolBool)
11528 } else if cls.is(PyInt::class(&vm.ctx)) {
11529 Some(Instruction::ToBoolInt)
11530 } else if cls.is(vm.ctx.types.none_type) {
11531 Some(Instruction::ToBoolNone)
11532 } else if cls.is(PyList::class(&vm.ctx)) {
11533 Some(Instruction::ToBoolList)
11534 } else if cls.is(PyStr::class(&vm.ctx)) {
11535 Some(Instruction::ToBoolStr)
11536 } else if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) {
11537 let type_version = cls.version_for_specialization(vm);
11541 let has_bool_or_len = cls.slots().as_number.boolean.load().is_some()
11542 || cls.slots().as_mapping.length.load().is_some()
11543 || cls.slots().as_sequence.length.load().is_some();
11544 if !has_bool_or_len {
11545 if type_version != 0 {
11546 unsafe {
11547 self.code
11548 .instructions
11549 .write_cache_u32(cache_base + 1, type_version);
11550 }
11551 self.specialize_at(instr_idx, cache_base, Instruction::ToBoolAlwaysTrue);
11552 } else {
11553 unsafe {
11554 self.code.instructions.write_adaptive_counter(
11555 cache_base,
11556 bytecode::adaptive_counter_backoff(
11557 self.code.instructions.read_adaptive_counter(cache_base),
11558 ),
11559 );
11560 }
11561 }
11562 return;
11563 }
11564 None
11565 } else {
11566 None
11567 };
11568
11569 self.commit_specialization(instr_idx, cache_base, new_op);
11570 }
11571
11572 fn specialize_for_iter(
11573 &mut self,
11574 vm: &VirtualMachine,
11575 jump_delta: u32,
11576 instr_idx: usize,
11577 cache_base: usize,
11578 ) {
11579 if !matches!(
11580 self.code.instructions.read_op(instr_idx),
11581 Instruction::ForIter { .. }
11582 ) {
11583 return;
11584 }
11585 let iter = self.top_value();
11586
11587 let new_op = if iter.downcast_ref_if_exact::<PyRangeIterator>(vm).is_some() {
11588 Some(Instruction::ForIterRange)
11589 } else if iter.downcast_ref_if_exact::<PyListIterator>(vm).is_some() {
11590 Some(Instruction::ForIterList)
11591 } else if iter.downcast_ref_if_exact::<PyTupleIterator>(vm).is_some() {
11592 Some(Instruction::ForIterTuple)
11593 } else if iter.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
11594 && i16::try_from(jump_delta).is_ok()
11595 && self.for_iter_has_end_for_shape(instr_idx, jump_delta)
11596 && !self.specialization_eval_frame_active(vm)
11597 {
11598 Some(Instruction::ForIterGen)
11599 } else {
11600 None
11601 };
11602
11603 self.commit_specialization(instr_idx, cache_base, new_op);
11604 }
11605
11606 #[inline]
11607 fn specialization_eval_frame_active(&self, vm: &VirtualMachine) -> bool {
11608 vm.use_tracing.get()
11609 }
11610
11611 #[inline]
11612 fn specialization_has_datastack_space_for_func(
11613 &self,
11614 vm: &VirtualMachine,
11615 func: &Py<PyFunction>,
11616 ) -> bool {
11617 self.specialization_has_datastack_space_for_func_with_extra(vm, func, 0)
11618 }
11619
11620 #[inline]
11621 fn specialization_has_datastack_space_for_func_with_extra(
11622 &self,
11623 vm: &VirtualMachine,
11624 func: &Py<PyFunction>,
11625 extra_bytes: usize,
11626 ) -> bool {
11627 match func.datastack_frame_size_bytes() {
11628 Some(frame_size) => frame_size
11629 .checked_add(extra_bytes)
11630 .is_some_and(|size| vm.datastack_has_space(size)),
11631 None => extra_bytes == 0 || vm.datastack_has_space(extra_bytes),
11632 }
11633 }
11634
11635 #[inline]
11636 fn specialization_call_recursion_guard(&self, vm: &VirtualMachine) -> bool {
11637 self.specialization_call_recursion_guard_with_extra_frames(vm, 0)
11638 }
11639
11640 #[inline]
11641 fn specialization_call_recursion_guard_with_extra_frames(
11642 &self,
11643 vm: &VirtualMachine,
11644 extra_frames: usize,
11645 ) -> bool {
11646 vm.current_recursion_depth()
11647 .saturating_add(1)
11648 .saturating_add(extra_frames)
11649 >= vm.recursion_limit.get()
11650 }
11651
11652 fn tailcall_prepare_frame(
11659 &mut self,
11660 nargs: u32,
11661 self_or_null_is_some: bool,
11662 vm: &VirtualMachine,
11663 ) {
11664 let base = usize::from(self_or_null_is_some);
11665 let effective_nargs = nargs as usize + base;
11666
11667 let callable = self.nth_value(nargs + 1);
11671 let func = callable.downcast_ref_if_exact::<PyFunction>(vm).unwrap();
11672
11673 let code: &Py<PyCode> = &func.code;
11674
11675 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
11676 FrameLocals::lazy()
11677 } else {
11678 FrameLocals::with_locals(crate::function::ArgMapping::from_dict_exact(
11679 func.globals.clone(),
11680 ))
11681 };
11682
11683 let callee_iframe = unsafe {
11684 InterpreterFrame::new_on_datastack(
11688 code,
11689 &func.globals,
11690 &func.builtins,
11691 Some(func.as_object()),
11692 locals,
11693 func.closure.as_ref().map_or(&[], |c| c.as_slice()),
11694 vm,
11695 )
11696 };
11697
11698 {
11701 let fastlocals = callee_iframe.localsplus.fastlocals_mut();
11702 for (dst, arg) in fastlocals[base..effective_nargs]
11703 .iter_mut()
11704 .zip(self.pop_multiple(nargs as usize))
11705 {
11706 *dst = Some(arg);
11707 }
11708 let self_or_null = self.pop_value_opt();
11709 debug_assert_eq!(self_or_null.is_some(), self_or_null_is_some);
11710 if self_or_null.is_some() {
11711 fastlocals[0] = self_or_null;
11712 }
11713 }
11714
11715 let callable = self.pop_value();
11718 vm.set_pending_tailcall_owner(callable);
11719
11720 vm.set_pending_tailcall(callee_iframe);
11721 }
11722
11723 fn tailcall_prepare_bound_method_frame(
11728 &mut self,
11729 nargs: u32,
11730 bound_function: PyObjectRef,
11731 bound_self: PyObjectRef,
11732 vm: &VirtualMachine,
11733 ) {
11734 let effective_nargs = nargs as usize + 1; let func = bound_function
11737 .downcast_ref_if_exact::<PyFunction>(vm)
11738 .unwrap();
11739 let code: &Py<PyCode> = &func.code;
11740
11741 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
11742 FrameLocals::lazy()
11743 } else {
11744 FrameLocals::with_locals(crate::function::ArgMapping::from_dict_exact(
11745 func.globals.clone(),
11746 ))
11747 };
11748
11749 let callee_iframe = unsafe {
11750 InterpreterFrame::new_on_datastack(
11754 code,
11755 &func.globals,
11756 &func.builtins,
11757 Some(func.as_object()),
11758 locals,
11759 func.closure.as_ref().map_or(&[], |c| c.as_slice()),
11760 vm,
11761 )
11762 };
11763
11764 let fastlocals = callee_iframe.localsplus.fastlocals_mut();
11766 for (dst, arg) in fastlocals[1..effective_nargs]
11767 .iter_mut()
11768 .zip(self.pop_multiple(nargs as usize))
11769 {
11770 *dst = Some(arg);
11771 }
11772 self.pop_stackref_opt(); self.pop_stackref(); fastlocals[0] = Some(bound_self);
11775
11776 vm.set_pending_tailcall_owner(bound_function);
11780
11781 vm.set_pending_tailcall(callee_iframe);
11782 }
11783
11784 #[inline]
11785 fn for_iter_has_end_for_shape(&self, instr_idx: usize, jump_delta: u32) -> bool {
11786 let target_idx = instr_idx
11787 + 1
11788 + Instruction::from(Opcode::ForIter).cache_entries()
11789 + jump_delta as usize;
11790 self.code.instructions.get(target_idx).is_some_and(|unit| {
11791 matches!(
11792 unit.op,
11793 Instruction::EndFor | Instruction::InstrumentedEndFor
11794 )
11795 })
11796 }
11797
11798 fn for_iter_jump_on_exhausted(&mut self, target: bytecode::Label) {
11801 let target_idx = target.as_usize();
11802 let jump_target = if let Some(unit) = self.code.instructions.get(target_idx) {
11803 if matches!(
11804 unit.op,
11805 bytecode::Instruction::EndFor | bytecode::Instruction::InstrumentedEndFor
11806 ) {
11807 bytecode::Label::from_u32(target.as_u32() + 1)
11808 } else {
11809 target
11810 }
11811 } else {
11812 target
11813 };
11814 self.jump(jump_target);
11815 }
11816
11817 fn specialize_load_global(
11818 &mut self,
11819 vm: &VirtualMachine,
11820 oparg: u32,
11821 instr_idx: usize,
11822 cache_base: usize,
11823 ) {
11824 if !matches!(
11825 self.code.instructions.read_op(instr_idx),
11826 Instruction::LoadGlobal { .. }
11827 ) {
11828 return;
11829 }
11830 let name = self.code.names[(oparg >> 1) as usize];
11831 let Ok(globals_version @ 1..) = u16::try_from(self.globals.assign_keys_version(vm)) else {
11832 unsafe {
11833 self.code.instructions.write_adaptive_counter(
11834 cache_base,
11835 bytecode::adaptive_counter_backoff(
11836 self.code.instructions.read_adaptive_counter(cache_base),
11837 ),
11838 );
11839 }
11840 return;
11841 };
11842
11843 if let Ok(Some(globals_hint)) = self.globals.hint_for_key(name, vm) {
11844 unsafe {
11845 self.code
11846 .instructions
11847 .write_cache_u16(cache_base + 1, globals_version);
11848 self.code.instructions.write_cache_u16(cache_base + 2, 0);
11849 self.code
11850 .instructions
11851 .write_cache_u16(cache_base + 3, globals_hint);
11852 }
11853 self.specialize_at(instr_idx, cache_base, Instruction::LoadGlobalModule);
11854 return;
11855 }
11856
11857 if let Some(builtins_dict) = self.builtins.downcast_ref_if_exact::<PyDict>(vm)
11858 && let Ok(Some(builtins_hint)) = builtins_dict.hint_for_key(name, vm)
11859 && let Ok(builtins_version @ 1..) = u16::try_from(builtins_dict.assign_keys_version(vm))
11860 {
11861 unsafe {
11862 self.code
11863 .instructions
11864 .write_cache_u16(cache_base + 1, globals_version);
11865 self.code
11866 .instructions
11867 .write_cache_u16(cache_base + 2, builtins_version);
11868 self.code
11869 .instructions
11870 .write_cache_u16(cache_base + 3, builtins_hint);
11871 }
11872 self.specialize_at(instr_idx, cache_base, Instruction::LoadGlobalBuiltin);
11873 return;
11874 }
11875
11876 unsafe {
11877 self.code.instructions.write_adaptive_counter(
11878 cache_base,
11879 bytecode::adaptive_counter_backoff(
11880 self.code.instructions.read_adaptive_counter(cache_base),
11881 ),
11882 );
11883 }
11884 }
11885
11886 fn specialize_store_subscr(
11887 &mut self,
11888 vm: &VirtualMachine,
11889 instr_idx: usize,
11890 cache_base: usize,
11891 ) {
11892 if !matches!(
11893 self.code.instructions.read_op(instr_idx),
11894 Instruction::StoreSubscr
11895 ) {
11896 return;
11897 }
11898 let obj = self.nth_value(1);
11900 let idx = self.top_value();
11901
11902 let new_op = if let (Some(list), Some(int_idx)) = (
11903 obj.downcast_ref_if_exact::<PyList>(vm),
11904 idx.downcast_ref_if_exact::<PyInt>(vm),
11905 ) {
11906 let list_len = list.borrow_vec().len();
11907 if specialization_nonnegative_compact_index(int_idx, vm).is_some_and(|i| i < list_len) {
11908 Some(Instruction::StoreSubscrListInt)
11909 } else {
11910 None
11911 }
11912 } else if obj.downcast_ref_if_exact::<PyDict>(vm).is_some() {
11913 Some(Instruction::StoreSubscrDict)
11914 } else {
11915 None
11916 };
11917
11918 self.commit_specialization(instr_idx, cache_base, new_op);
11919 }
11920
11921 fn specialize_contains_op(&mut self, vm: &VirtualMachine, instr_idx: usize, cache_base: usize) {
11922 if !matches!(
11923 self.code.instructions.read_op(instr_idx),
11924 Instruction::ContainsOp { .. }
11925 ) {
11926 return;
11927 }
11928 let haystack = self.top_value(); let new_op = if haystack.downcast_ref_if_exact::<PyDict>(vm).is_some() {
11930 Some(Instruction::ContainsOpDict)
11931 } else if haystack.downcast_ref_if_exact::<PySet>(vm).is_some()
11932 || haystack.downcast_ref_if_exact::<PyFrozenSet>(vm).is_some()
11933 {
11934 Some(Instruction::ContainsOpSet)
11935 } else {
11936 None
11937 };
11938
11939 self.commit_specialization(instr_idx, cache_base, new_op);
11940 }
11941
11942 fn specialize_unpack_sequence(
11943 &mut self,
11944 vm: &VirtualMachine,
11945 expected_count: u32,
11946 instr_idx: usize,
11947 cache_base: usize,
11948 ) {
11949 if !matches!(
11950 self.code.instructions.read_op(instr_idx),
11951 Instruction::UnpackSequence { .. }
11952 ) {
11953 return;
11954 }
11955 let obj = self.top_value();
11956 let new_op = if let Some(tuple) = obj.downcast_ref_if_exact::<PyTuple>(vm) {
11957 if tuple.as_slice().len() != expected_count as usize {
11958 None
11959 } else if expected_count == 2 {
11960 Some(Instruction::UnpackSequenceTwoTuple)
11961 } else {
11962 Some(Instruction::UnpackSequenceTuple)
11963 }
11964 } else if let Some(list) = obj.downcast_ref_if_exact::<PyList>(vm) {
11965 if list.borrow_vec().len() == expected_count as usize {
11966 Some(Instruction::UnpackSequenceList)
11967 } else {
11968 None
11969 }
11970 } else {
11971 None
11972 };
11973
11974 self.commit_specialization(instr_idx, cache_base, new_op);
11975 }
11976
11977 fn specialize_store_attr(
11978 &mut self,
11979 vm: &VirtualMachine,
11980 attr_idx: bytecode::NameIdx,
11981 instr_idx: usize,
11982 cache_base: usize,
11983 ) {
11984 if !matches!(
11985 self.code.instructions.read_op(instr_idx),
11986 Instruction::StoreAttr { .. }
11987 ) {
11988 return;
11989 }
11990 let owner = self.top_value();
11992 let cls = owner.class();
11993
11994 let type_version = cls.version_for_specialization(vm);
11998 if type_version == 0 {
11999 unsafe {
12000 self.code.instructions.write_adaptive_counter(
12001 cache_base,
12002 bytecode::adaptive_counter_backoff(
12003 self.code.instructions.read_adaptive_counter(cache_base),
12004 ),
12005 );
12006 }
12007 return;
12008 }
12009
12010 let is_default_setattr = cls.slots().setattro.load().is_some_and(|f| {
12012 crate::types::fn_addr(f)
12013 == crate::types::fn_addr(PyBaseObject::slot_setattro as crate::types::SetattroFunc)
12014 });
12015 if !is_default_setattr {
12016 unsafe {
12017 self.code.instructions.write_adaptive_counter(
12018 cache_base,
12019 bytecode::adaptive_counter_backoff(
12020 self.code.instructions.read_adaptive_counter(cache_base),
12021 ),
12022 );
12023 }
12024 return;
12025 }
12026
12027 let attr_name = self.code.names[attr_idx as usize];
12028 let cls_attr = cls.get_attr(attr_name);
12029 let has_data_descr = cls_attr.as_ref().is_some_and(|descr| {
12030 let descr_cls = descr.class();
12031 descr_cls.slots().descr_get.load().is_some()
12032 && descr_cls.slots().descr_set.load().is_some()
12033 });
12034
12035 if has_data_descr {
12036 if let Some(ref descr) = cls_attr
12040 && let Some(member_descr) = descr.downcast_ref::<PyMemberDescriptor>()
12041 && let Some(offset) = member_descr
12042 .slot_offset()
12043 .filter(|_| !member_descr.member.readonly())
12044 && cls.fast_issubclass(&member_descr.common.typ)
12045 {
12046 unsafe {
12047 self.code
12048 .instructions
12049 .write_cache_u32(cache_base + 1, type_version);
12050 self.code
12051 .instructions
12052 .write_cache_u16(cache_base + 3, offset as u16);
12053 }
12054 self.specialize_at(instr_idx, cache_base, Instruction::StoreAttrSlot);
12055 } else {
12056 unsafe {
12057 self.code.instructions.write_adaptive_counter(
12058 cache_base,
12059 bytecode::adaptive_counter_backoff(
12060 self.code.instructions.read_adaptive_counter(cache_base),
12061 ),
12062 );
12063 }
12064 }
12065 } else if let Some(dict) = owner.dict() {
12066 let hint = match dict.hint_for_key(attr_name, vm) {
12067 Ok(hint) => hint,
12068 Err(_) => {
12069 unsafe {
12070 self.code.instructions.write_adaptive_counter(
12071 cache_base,
12072 bytecode::adaptive_counter_backoff(
12073 self.code.instructions.read_adaptive_counter(cache_base),
12074 ),
12075 );
12076 }
12077 return;
12078 }
12079 };
12080 unsafe {
12081 self.code
12082 .instructions
12083 .write_cache_u32(cache_base + 1, type_version);
12084 self.code
12085 .instructions
12086 .write_cache_u16(cache_base + 3, hint.unwrap_or(0));
12087 }
12088 self.specialize_at(
12089 instr_idx,
12090 cache_base,
12091 if hint.is_some() {
12092 Instruction::StoreAttrWithHint
12093 } else {
12094 Instruction::StoreAttrInstanceValue
12095 },
12096 );
12097 } else {
12098 unsafe {
12099 self.code.instructions.write_adaptive_counter(
12100 cache_base,
12101 bytecode::adaptive_counter_backoff(
12102 self.code.instructions.read_adaptive_counter(cache_base),
12103 ),
12104 );
12105 }
12106 }
12107 }
12108
12109 fn load_super_attr(&mut self, vm: &VirtualMachine, oparg: LoadSuperAttr) -> FrameResult {
12110 let attr_name = self.code.names[oparg.name_idx() as usize];
12111
12112 let self_obj = self.pop_value();
12115 let class = self.pop_value();
12116 let global_super = self.pop_value();
12117
12118 let super_obj = if oparg.has_class() {
12121 global_super.call((class, self_obj.clone()), vm)?
12122 } else {
12123 global_super.call((), vm)?
12124 };
12125
12126 if oparg.is_load_method() {
12127 let method = PyMethod::get(super_obj, attr_name, vm)?;
12129 match method {
12130 PyMethod::Function { target: _, func } => {
12131 self.push_value(func);
12132 self.push_value(self_obj);
12133 }
12134 PyMethod::Attribute(val) => {
12135 self.push_value(val);
12136 self.push_null();
12137 }
12138 }
12139 } else {
12140 let obj = super_obj.get_attr(attr_name, vm)?;
12142 self.push_value(obj);
12143 }
12144 Ok(None)
12145 }
12146
12147 fn store_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
12148 let attr_name = self.code.names[attr as usize];
12149 let parent = self.pop_stackref();
12150 let value = self.pop_value();
12151 parent.set_attr(attr_name, value, vm)?;
12152 Ok(None)
12153 }
12154
12155 fn delete_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
12156 let attr_name = self.code.names[attr as usize];
12157 let parent = self.pop_stackref();
12158 parent.del_attr(attr_name, vm)?;
12159 Ok(None)
12160 }
12161
12162 #[inline(always)]
12165 #[track_caller]
12166 fn push_stackref_opt(&mut self, obj: Option<PyStackRef>) {
12167 match self.localsplus.stack_try_push(obj) {
12168 Ok(()) => {}
12169 Err(_e) => self.fatal("tried to push value onto stack but overflowed max_stackdepth"),
12170 }
12171 }
12172
12173 #[inline(always)]
12174 #[track_caller] fn push_value_opt(&mut self, obj: Option<PyObjectRef>) {
12176 self.push_stackref_opt(obj.map(PyStackRef::new_owned));
12177 }
12178
12179 #[inline(always)]
12180 #[track_caller]
12181 fn push_value(&mut self, obj: PyObjectRef) {
12182 self.push_stackref_opt(Some(PyStackRef::new_owned(obj)));
12183 }
12184
12185 #[inline]
12191 #[track_caller]
12192 unsafe fn push_borrowed(&mut self, obj: &PyObject) {
12193 self.push_stackref_opt(Some(unsafe { PyStackRef::new_borrowed(obj) }));
12194 }
12195
12196 #[inline(always)]
12204 fn push_local(&mut self, idx: usize, vm: &VirtualMachine) -> PyResult<()> {
12205 #[cold]
12206 #[inline(never)]
12207 fn unbound(varname: &'static PyStrInterned, vm: &VirtualMachine) -> PyBaseExceptionRef {
12208 vm.new_unbound_local_error(format!(
12209 "local variable '{varname}' referenced before assignment"
12210 ))
12211 }
12212 if BORROW_LOCAL_LOADS {
12213 let obj: *const PyObject = match self.localsplus.fastlocals()[idx].as_ref() {
12216 Some(obj) => obj.as_object(),
12217 None => return Err(unbound(self.code.varnames[idx], vm)),
12218 };
12219 unsafe { self.push_borrowed(&*obj) };
12223 } else {
12224 let obj = match self.localsplus.fastlocals()[idx].clone() {
12225 Some(obj) => obj,
12226 None => return Err(unbound(self.code.varnames[idx], vm)),
12227 };
12228 self.push_value(obj);
12229 }
12230 Ok(())
12231 }
12232
12233 #[inline(always)]
12234 fn push_null(&mut self) {
12235 self.push_stackref_opt(None);
12236 }
12237
12238 #[inline(always)]
12240 fn pop_stackref_opt(&mut self) -> Option<PyStackRef> {
12241 if self.localsplus.stack_is_empty() {
12242 self.fatal("tried to pop from empty stack");
12243 }
12244 self.localsplus.stack_pop()
12245 }
12246
12247 #[inline(always)]
12249 #[track_caller]
12250 fn pop_stackref(&mut self) -> PyStackRef {
12251 expect_unchecked(
12252 self.pop_stackref_opt(),
12253 "pop stackref but null found. This is a compiler bug.",
12254 )
12255 }
12256
12257 #[inline(always)]
12260 fn pop_value_opt(&mut self) -> Option<PyObjectRef> {
12261 self.pop_stackref_opt().map(|sr| sr.to_pyobj())
12262 }
12263
12264 #[inline(always)]
12265 #[track_caller]
12266 fn pop_value(&mut self) -> PyObjectRef {
12267 self.pop_stackref().to_pyobj()
12268 }
12269
12270 fn call_intrinsic_1(
12271 &mut self,
12272 func: bytecode::IntrinsicFunction1,
12273 arg: PyObjectRef,
12274 vm: &VirtualMachine,
12275 ) -> PyResult {
12276 match func {
12277 bytecode::IntrinsicFunction1::Invalid => {
12278 unreachable!("This is a bug in RustPython compiler")
12279 }
12280 bytecode::IntrinsicFunction1::Print => {
12281 let displayhook = vm
12282 .sys_module
12283 .get_attr("displayhook", vm)
12284 .map_err(|_| vm.new_runtime_error("lost sys.displayhook"))?;
12285 displayhook.call((arg,), vm)
12286 }
12287 bytecode::IntrinsicFunction1::ImportStar => {
12288 self.push_value(arg); self.import_star(vm)?;
12291 Ok(vm.ctx.none())
12292 }
12293 bytecode::IntrinsicFunction1::UnaryPositive => vm._pos(&arg),
12294 bytecode::IntrinsicFunction1::SubscriptGeneric => {
12295 crate::builtins::genericalias::subscript_generic(arg, vm)
12297 }
12298 bytecode::IntrinsicFunction1::TypeVar => {
12299 let type_var: PyObjectRef =
12300 _typing::TypeVar::new(vm, arg, vm.ctx.none(), vm.ctx.none())
12301 .into_ref(&vm.ctx)
12302 .into();
12303 Ok(type_var)
12304 }
12305 bytecode::IntrinsicFunction1::ParamSpec => {
12306 let param_spec: PyObjectRef =
12307 _typing::ParamSpec::new(arg, vm).into_ref(&vm.ctx).into();
12308 Ok(param_spec)
12309 }
12310 bytecode::IntrinsicFunction1::TypeVarTuple => {
12311 let type_var_tuple: PyObjectRef =
12312 _typing::TypeVarTuple::new(arg, vm).into_ref(&vm.ctx).into();
12313 Ok(type_var_tuple)
12314 }
12315 bytecode::IntrinsicFunction1::TypeAlias => {
12316 let tuple: PyTupleRef = arg
12318 .downcast()
12319 .map_err(|_| vm.new_type_error("TypeAlias expects a tuple argument"))?;
12320
12321 if tuple.as_slice().len() != 3 {
12322 return Err(vm.new_type_error(format!(
12323 "TypeAlias expects exactly 3 arguments, got {}",
12324 tuple.as_slice().len()
12325 )));
12326 }
12327
12328 let name = tuple.as_slice()[0].clone();
12329 let type_params_obj = tuple.as_slice()[1].clone();
12330 let compute_value = tuple.as_slice()[2].clone();
12331
12332 let type_params: PyTupleRef = if vm.is_none(&type_params_obj) {
12333 vm.ctx.empty_tuple.clone()
12334 } else {
12335 type_params_obj
12336 .downcast()
12337 .map_err(|_| vm.new_type_error("Type params must be a tuple."))?
12338 };
12339
12340 let name = name
12341 .downcast::<crate::builtins::PyStr>()
12342 .map_err(|_| vm.new_type_error("TypeAliasType name must be a string"))?;
12343 let type_alias = _typing::TypeAliasType::new(name, type_params, compute_value);
12344 Ok(type_alias.into_ref(&vm.ctx).into())
12345 }
12346 bytecode::IntrinsicFunction1::ListToTuple => {
12347 let list = arg
12349 .downcast::<PyList>()
12350 .map_err(|_| vm.new_type_error("LIST_TO_TUPLE expects a list"))?;
12351 Ok(vm.ctx.new_tuple(list.borrow_vec().to_vec()).into())
12352 }
12353 bytecode::IntrinsicFunction1::StopIterationError => {
12354 if arg.fast_isinstance(vm.ctx.exceptions.stop_iteration) {
12357 let flags = &self.code.flags;
12358 let msg = if flags.contains(bytecode::CodeFlags::ASYNC_GENERATOR) {
12359 "async generator raised StopIteration"
12360 } else if flags.contains(bytecode::CodeFlags::COROUTINE) {
12361 "coroutine raised StopIteration"
12362 } else {
12363 "generator raised StopIteration"
12364 };
12365 let err = vm.new_runtime_error(msg);
12366 let cause: Option<PyBaseExceptionRef> = arg.downcast().ok();
12371 err.set_context(cause.clone());
12372 err.set_cause(cause);
12373 Ok(err.into())
12374 } else {
12375 Ok(arg)
12377 }
12378 }
12379 bytecode::IntrinsicFunction1::AsyncGenWrap => {
12380 Ok(crate::builtins::asyncgenerator::PyAsyncGenWrappedValue(arg)
12383 .into_ref(&vm.ctx)
12384 .into())
12385 }
12386 }
12387 }
12388
12389 fn call_intrinsic_2(
12390 &mut self,
12391 func: bytecode::IntrinsicFunction2,
12392 arg1: PyObjectRef,
12393 arg2: PyObjectRef,
12394 vm: &VirtualMachine,
12395 ) -> PyResult {
12396 match func {
12397 bytecode::IntrinsicFunction2::Invalid => {
12398 unreachable!("This is a bug in RustPython compiler")
12399 }
12400 bytecode::IntrinsicFunction2::SetTypeparamDefault => {
12401 crate::stdlib::_typing::set_typeparam_default(arg1, arg2, vm)
12402 }
12403 bytecode::IntrinsicFunction2::SetFunctionTypeParams => {
12404 arg1.set_attr("__type_params__", arg2, vm)?;
12407 Ok(arg1)
12408 }
12409 bytecode::IntrinsicFunction2::TypeVarWithBound => {
12410 let type_var: PyObjectRef = _typing::TypeVar::new(vm, arg1, arg2, vm.ctx.none())
12411 .into_ref(&vm.ctx)
12412 .into();
12413 Ok(type_var)
12414 }
12415 bytecode::IntrinsicFunction2::TypeVarWithConstraint => {
12416 let type_var: PyObjectRef = _typing::TypeVar::new(vm, arg1, vm.ctx.none(), arg2)
12417 .into_ref(&vm.ctx)
12418 .into();
12419 Ok(type_var)
12420 }
12421 bytecode::IntrinsicFunction2::PrepReraiseStar => {
12422 crate::exceptions::prep_reraise_star(&arg1, &arg2, vm)
12426 }
12427 }
12428 }
12429
12430 fn take_call_args(&mut self, nargs: usize) -> (PyObjectRef, Vec<PyObjectRef>) {
12438 let stack_len = self.localsplus.stack_len();
12439 debug_assert!(
12440 stack_len >= nargs + 2,
12441 "CALL stack underflow: need callable + self_or_null + {nargs} args, have {stack_len}"
12442 );
12443 let callable_idx = stack_len - nargs - 2;
12444 let self_or_null_idx = callable_idx + 1;
12445
12446 let self_or_null = self
12447 .localsplus
12448 .stack_index_mut(self_or_null_idx)
12449 .take()
12450 .map(|sr| sr.to_pyobj());
12451 let mut args = Vec::with_capacity(nargs + usize::from(self_or_null.is_some()));
12452 args.extend(self_or_null);
12453 for stack_idx in self_or_null_idx + 1..stack_len {
12454 let val = self
12455 .localsplus
12456 .stack_index_mut(stack_idx)
12457 .take()
12458 .unwrap()
12459 .to_pyobj();
12460 args.push(val);
12461 }
12462
12463 let callable = self
12464 .localsplus
12465 .stack_index_mut(callable_idx)
12466 .take()
12467 .unwrap()
12468 .to_pyobj();
12469 self.localsplus.stack_truncate(callable_idx);
12470 (callable, args)
12471 }
12472
12473 fn pop_multiple(&mut self, count: usize) -> impl ExactSizeIterator<Item = PyObjectRef> + '_ {
12475 let stack_len = self.localsplus.stack_len();
12476 if count > stack_len {
12477 let instr = self.code.instructions.get(self.lasti() as usize);
12478 let op_name = instr.map_or_else(|| "None".to_string(), |i| format!("{:?}", i.op));
12479 panic!(
12480 "Stack underflow in pop_multiple: trying to pop {} elements from stack with {} elements. lasti={}, code={}, op={}, source_path={}",
12481 count,
12482 stack_len,
12483 self.lasti(),
12484 self.code.obj_name,
12485 op_name,
12486 self.code.source_path()
12487 );
12488 }
12489 self.localsplus.stack_drain(stack_len - count).map(|obj| {
12490 expect_unchecked(obj, "pop_multiple but null found. This is a compiler bug.").to_pyobj()
12491 })
12492 }
12493
12494 #[inline]
12495 fn replace_top(&mut self, top: Option<PyObjectRef>) -> Option<PyObjectRef> {
12496 let mut slot = top.map(PyStackRef::new_owned);
12497 let last = self.localsplus.stack_last_mut().unwrap();
12498 core::mem::swap(last, &mut slot);
12499 slot.map(|sr| sr.to_pyobj())
12500 }
12501
12502 #[inline(always)]
12503 #[track_caller]
12504 fn top_value(&self) -> &PyObject {
12505 match self.localsplus.stack_last() {
12506 Some(Some(last)) => last.as_object(),
12507 Some(None) => self.fatal("tried to get top of stack but got NULL"),
12508 None => self.fatal("tried to get top of stack but stack is empty"),
12509 }
12510 }
12511
12512 #[inline(always)]
12513 #[track_caller]
12514 fn nth_value(&self, depth: u32) -> &PyObject {
12515 let idx = self.localsplus.stack_len() - depth as usize - 1;
12516 match self.localsplus.stack_index(idx) {
12517 Some(obj) => obj.as_object(),
12518 None => unsafe { core::hint::unreachable_unchecked() },
12519 }
12520 }
12521
12522 #[cold]
12523 #[inline(never)]
12524 #[track_caller]
12525 fn fatal(&self, msg: &'static str) -> ! {
12526 dbg!(self);
12527 panic!("{msg}")
12528 }
12529}
12530
12531impl fmt::Debug for FrameObject {
12532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12533 let Some(iframe) = (unsafe { &*self.iframe.get() }) else {
12536 return f.write_str("FrameObject Object { cleared }");
12537 };
12538 let stack_str =
12539 iframe
12540 .localsplus
12541 .stack_as_slice()
12542 .iter()
12543 .fold(String::new(), |mut s, slot| {
12544 match slot {
12545 Some(elem) if elem.downcastable::<Self>() => {
12546 s.push_str("\n > {frame}");
12547 }
12548 Some(elem) => {
12549 core::fmt::write(&mut s, format_args!("\n > {elem:?}")).unwrap();
12550 }
12551 None => {
12552 s.push_str("\n > NULL");
12553 }
12554 }
12555 s
12556 });
12557 write!(
12559 f,
12560 "FrameObject Object {{ \n Stack:{}\n Locals initialized:{}\n}}",
12561 stack_str,
12562 self.iframe().locals.get().is_some()
12563 )
12564 }
12565}
12566
12567fn special_method_can_suggest(
12569 obj: &PyObject,
12570 oparg: SpecialMethod,
12571 vm: &VirtualMachine,
12572) -> PyResult<bool> {
12573 Ok(match oparg {
12574 SpecialMethod::Enter | SpecialMethod::Exit => {
12575 vm.get_special_method(obj, get_special_method_name(SpecialMethod::AEnter, vm))?
12576 .is_some()
12577 && vm
12578 .get_special_method(obj, get_special_method_name(SpecialMethod::AExit, vm))?
12579 .is_some()
12580 }
12581 SpecialMethod::AEnter | SpecialMethod::AExit => {
12582 vm.get_special_method(obj, get_special_method_name(SpecialMethod::Enter, vm))?
12583 .is_some()
12584 && vm
12585 .get_special_method(obj, get_special_method_name(SpecialMethod::Exit, vm))?
12586 .is_some()
12587 }
12588 })
12589}
12590
12591fn get_special_method_name(oparg: SpecialMethod, vm: &VirtualMachine) -> &'static PyStrInterned {
12592 match oparg {
12593 SpecialMethod::Enter => identifier!(vm, __enter__),
12594 SpecialMethod::Exit => identifier!(vm, __exit__),
12595 SpecialMethod::AEnter => identifier!(vm, __aenter__),
12596 SpecialMethod::AExit => identifier!(vm, __aexit__),
12597 }
12598}
12599
12600fn get_special_method_error_msg(
12602 oparg: SpecialMethod,
12603 class_name: &str,
12604 can_suggest: bool,
12605) -> String {
12606 if can_suggest {
12607 match oparg {
12608 SpecialMethod::Enter => format!(
12609 "'{class_name}' object does not support the context manager protocol (missed __enter__ method) but it supports the asynchronous context manager protocol. Did you mean to use 'async with'?"
12610 ),
12611 SpecialMethod::Exit => format!(
12612 "'{class_name}' object does not support the context manager protocol (missed __exit__ method) but it supports the asynchronous context manager protocol. Did you mean to use 'async with'?"
12613 ),
12614 SpecialMethod::AEnter => format!(
12615 "'{class_name}' object does not support the asynchronous context manager protocol (missed __aenter__ method) but it supports the context manager protocol. Did you mean to use 'with'?"
12616 ),
12617 SpecialMethod::AExit => format!(
12618 "'{class_name}' object does not support the asynchronous context manager protocol (missed __aexit__ method) but it supports the context manager protocol. Did you mean to use 'with'?"
12619 ),
12620 }
12621 } else {
12622 match oparg {
12623 SpecialMethod::Enter => format!(
12624 "'{class_name}' object does not support the context manager protocol (missed __enter__ method)"
12625 ),
12626 SpecialMethod::Exit => format!(
12627 "'{class_name}' object does not support the context manager protocol (missed __exit__ method)"
12628 ),
12629 SpecialMethod::AEnter => format!(
12630 "'{class_name}' object does not support the asynchronous context manager protocol (missed __aenter__ method)"
12631 ),
12632 SpecialMethod::AExit => format!(
12633 "'{class_name}' object does not support the asynchronous context manager protocol (missed __aexit__ method)"
12634 ),
12635 }
12636 }
12637}
12638
12639fn is_module_initializing(module: &PyObject, vm: &VirtualMachine) -> bool {
12640 let Ok(spec) = module.get_attr(&vm.ctx.new_str("__spec__"), vm) else {
12641 return false;
12642 };
12643 if vm.is_none(&spec) {
12644 return false;
12645 }
12646 let Ok(initializing_attr) = spec.get_attr(&vm.ctx.new_str("_initializing"), vm) else {
12647 return false;
12648 };
12649 initializing_attr.try_to_bool(vm).unwrap_or(false)
12650}
12651
12652fn expect_unchecked<T: fmt::Debug>(optional: Option<T>, err_msg: &'static str) -> T {
12653 if cfg!(debug_assertions) {
12654 optional.expect(err_msg)
12655 } else {
12656 unsafe { optional.unwrap_unchecked() }
12657 }
12658}