1#[cfg(feature = "rustpython-compiler")]
7mod compile;
8pub(crate) mod compile_mode;
9#[cfg(feature = "rustpython-compiler")]
10pub use compile::VmCompileError;
11mod context;
12pub mod crossinterp;
13mod interpreter;
14mod method;
15#[cfg(feature = "rustpython-compiler")]
16mod python_run;
17pub mod runtime;
18mod setting;
19pub mod thread;
20mod vm_new;
21mod vm_object;
22mod vm_ops;
23
24use crate::{
25 AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
26 builtins::{
27 self, PyBaseExceptionRef, PyBaseObject, PyDict, PyDictRef, PyFrozenSet, PyInt, PyList,
28 PyModule, PySet, PyStr, PyStrInterned, PyStrRef, PyTypeRef, PyUtf8Str, PyUtf8StrInterned,
29 PyWeak,
30 code::PyCode,
31 dict::{PyDictItems, PyDictKeys, PyDictValues},
32 pystr::AsPyStr,
33 tuple::PyTuple,
34 },
35 codecs::CodecsRegistry,
36 common::{hash::HashSecret, lock::PyMutex, rc::PyRc},
37 convert::ToPyObject,
38 exceptions::types::{PyBaseException, PyMemoryError},
39 frame::{ExecutionResult, FrameObject, FrameObjectRef},
40 frozen::FrozenModule,
41 function::{ArgMapping, FuncArgs, PySetterValue},
42 import,
43 protocol::{PyIterIter, PyIterReturn},
44 scope::Scope,
45 signal::{self, SignalHandlers},
46 stdlib,
47 types::{GetattroFunc, fn_addr},
48 warn::WarningsState,
49};
50use alloc::{borrow::Cow, collections::BTreeMap};
51#[cfg(all(not(unix), feature = "threading"))]
52use core::ptr::NonNull;
53use core::{
54 cell::{Cell, OnceCell, RefCell},
55 sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering},
56};
57use crossbeam_utils::atomic::AtomicCell;
58use std::{
59 collections::{HashMap, HashSet},
60 ffi::{OsStr, OsString},
61};
62
63pub use context::Context;
64pub use interpreter::{Interpreter, InterpreterBuilder};
65pub(crate) use method::PyMethod;
66pub use runtime::{
67 InterpFeatureFlags, InterpreterConfig, InterpreterGil, InterpreterInfo, InterpreterWhence,
68 MAIN_INTERPRETER_ID,
69};
70pub use setting::{CheckHashPycsMode, Paths, PyConfig, Settings};
71
72pub const MAX_MEMORY_SIZE: usize = isize::MAX as usize;
73
74pub struct VirtualMachine {
86 pub builtins: PyRef<PyModule>,
87 pub sys_module: PyRef<PyModule>,
88 pub ctx: PyRc<Context>,
89 datastack: core::cell::UnsafeCell<crate::datastack::DataStack>,
92 pub wasm_id: Option<String>,
93 exceptions: RefCell<ExceptionStack>,
94 pub import_func: PyObjectRef,
95 pub(crate) importlib: PyObjectRef,
96 pub profile_func: RefCell<PyObjectRef>,
97 pub trace_func: RefCell<PyObjectRef>,
98 pub use_tracing: Cell<bool>,
99 pub(crate) what_event: Cell<Option<crate::stdlib::sys::monitoring::MonitoringEvent>>,
102 tracing_depth: Cell<usize>,
103 pub recursion_limit: Cell<usize>,
104 pub(crate) signal_handlers: OnceCell<SignalHandlers>,
105 pub(crate) signal_rx: Option<signal::UserSignalReceiver>,
106 pub repr_guards: RefCell<HashSet<usize>>,
107 pub state: PyRc<PyGlobalState>,
108 pub initialized: bool,
109 recursion_depth: Cell<usize>,
110 #[cfg(any(miri, target_env = "musl"))]
114 native_recursion_depth: Cell<usize>,
115 #[cfg_attr(any(miri, target_env = "musl"), allow(dead_code))]
117 c_stack_soft_limit: Cell<usize>,
118 pub async_gen_firstiter: RefCell<Option<PyObjectRef>>,
120 pub async_gen_finalizer: RefCell<Option<PyObjectRef>>,
122 pub asyncio_running_loop: RefCell<Option<PyObjectRef>>,
124 pub asyncio_running_task: RefCell<Option<PyObjectRef>>,
126 pub context_stack: RefCell<Vec<PyObjectRef>>,
128 pub(crate) callable_cache: CallableCache,
129 pending_tailcall_frame: Cell<Option<PendingFrame>>,
133 pending_tailcall_owner: core::cell::UnsafeCell<Option<PyObjectRef>>,
138 pending_gen_resume: core::cell::UnsafeCell<Option<PendingGenResume>>,
143 trampoline_stack: core::cell::UnsafeCell<Vec<SuspendedFrame>>,
151}
152
153#[cfg(all(not(unix), feature = "threading"))]
158#[derive(Copy, Clone)]
159pub struct FramePtr(NonNull<Py<FrameObject>>);
160
161#[cfg(all(not(unix), feature = "threading"))]
162impl FramePtr {
163 #[must_use]
166 pub unsafe fn as_ref(&self) -> &Py<FrameObject> {
167 unsafe { self.0.as_ref() }
168 }
169}
170
171#[cfg(all(not(unix), feature = "threading"))]
175unsafe impl Send for FramePtr {}
176
177#[derive(Debug)]
178struct ExceptionStack {
179 stack: Vec<Option<PyBaseExceptionRef>>,
183}
184
185impl Default for ExceptionStack {
186 fn default() -> Self {
187 Self { stack: vec![None] }
189 }
190}
191
192#[cfg(feature = "threading")]
195pub struct StopTheWorldState {
196 pub(crate) requested: AtomicBool,
198 world_stopped: AtomicBool,
200 requester: AtomicU64,
202 exclusion: AtomicBool,
207 notify_mutex: std::sync::Mutex<()>,
209 notify_cv: std::sync::Condvar,
210 thread_countdown: AtomicI64,
212 stats_stop_calls: AtomicU64,
214 stats_last_wait_ns: AtomicU64,
216 stats_total_wait_ns: AtomicU64,
218 stats_max_wait_ns: AtomicU64,
220 stats_poll_loops: AtomicU64,
222 stats_attached_seen: AtomicU64,
224 stats_forced_parks: AtomicU64,
226 stats_suspend_notifications: AtomicU64,
228 stats_attach_wait_yields: AtomicU64,
230 stats_suspend_wait_yields: AtomicU64,
232}
233
234#[cfg(feature = "threading")]
235#[derive(Debug, Clone, Copy)]
236pub struct StopTheWorldStats {
237 pub stop_calls: u64,
238 pub last_wait_ns: u64,
239 pub total_wait_ns: u64,
240 pub max_wait_ns: u64,
241 pub poll_loops: u64,
242 pub attached_seen: u64,
243 pub forced_parks: u64,
244 pub suspend_notifications: u64,
245 pub attach_wait_yields: u64,
246 pub suspend_wait_yields: u64,
247 pub world_stopped: bool,
248}
249
250#[cfg(feature = "threading")]
251impl Default for StopTheWorldState {
252 fn default() -> Self {
253 Self::new()
254 }
255}
256
257#[cfg(feature = "threading")]
258impl StopTheWorldState {
259 #[must_use]
260 pub const fn new() -> Self {
261 Self {
262 requested: AtomicBool::new(false),
263 world_stopped: AtomicBool::new(false),
264 requester: AtomicU64::new(0),
265 exclusion: AtomicBool::new(false),
266 notify_mutex: std::sync::Mutex::new(()),
267 notify_cv: std::sync::Condvar::new(),
268 thread_countdown: AtomicI64::new(0),
269 stats_stop_calls: AtomicU64::new(0),
270 stats_last_wait_ns: AtomicU64::new(0),
271 stats_total_wait_ns: AtomicU64::new(0),
272 stats_max_wait_ns: AtomicU64::new(0),
273 stats_poll_loops: AtomicU64::new(0),
274 stats_attached_seen: AtomicU64::new(0),
275 stats_forced_parks: AtomicU64::new(0),
276 stats_suspend_notifications: AtomicU64::new(0),
277 stats_attach_wait_yields: AtomicU64::new(0),
278 stats_suspend_wait_yields: AtomicU64::new(0),
279 }
280 }
281
282 pub(crate) fn notify_suspended(&self) {
284 self.stats_suspend_notifications
285 .fetch_add(1, Ordering::Relaxed);
286 let _guard = self.notify_mutex.lock().unwrap();
288 self.decrement_thread_countdown(1);
289 self.notify_cv.notify_one();
290 }
291
292 #[inline]
293 fn init_thread_countdown(&self, state: &PyGlobalState) -> i64 {
294 let requester = self.requester.load(Ordering::Relaxed);
295 let registry = state.thread_frames.lock();
296 self.requested.store(true, Ordering::Release);
300 let count = registry
301 .iter()
302 .filter(|(thread_id, slot)| {
303 **thread_id != requester
304 && slot.state.load(Ordering::Relaxed)
305 != thread::ThreadState::ShuttingDown as i32
306 })
307 .count();
308 let count = (count.min(i64::MAX as usize)) as i64;
309 self.thread_countdown.store(count, Ordering::Release);
310 count
311 }
312
313 #[inline]
314 fn decrement_thread_countdown(&self, n: u64) {
315 if n == 0 {
316 return;
317 }
318 let n = (n.min(i64::MAX as u64)) as i64;
319 let prev = self.thread_countdown.fetch_sub(n, Ordering::AcqRel);
320 if prev <= n {
321 self.thread_countdown.store(0, Ordering::Release);
323 }
324 }
325
326 fn park_detached_threads(&self, state: &PyGlobalState) -> bool {
329 use thread::ThreadState;
330 let requester = self.requester.load(Ordering::Relaxed);
331 let registry = state.thread_frames.lock();
332 let mut attached_seen = 0u64;
333 let mut forced_parks = 0u64;
334
335 #[expect(
336 clippy::iter_over_hash_type,
337 reason = "Iteration order doesn't matter here"
338 )]
339 for (&id, slot) in registry.iter() {
340 if id == requester {
341 continue;
342 }
343
344 let state = slot.state.load(Ordering::Relaxed);
345 if state == ThreadState::Detached as i32 {
346 match slot.state.compare_exchange(
348 ThreadState::Detached as i32,
349 ThreadState::Suspended as i32,
350 Ordering::AcqRel,
351 Ordering::Relaxed,
352 ) {
353 Ok(_) => {
354 slot.stop_requested.store(false, Ordering::Release);
355 forced_parks = forced_parks.saturating_add(1);
356 }
357 Err(actual) => match ThreadState::from_i32(actual) {
358 Some(ThreadState::Attached) => {
359 slot.stop_requested.store(true, Ordering::Release);
361 crate::signal::set_stop_bit();
362 attached_seen = attached_seen.saturating_add(1);
364 }
365 Some(ThreadState::Detached) => {
366 }
368 Some(ThreadState::Suspended) => {
369 slot.stop_requested.store(false, Ordering::Release);
370 }
372 Some(ThreadState::ShuttingDown) => {
373 slot.stop_requested.store(false, Ordering::Release);
374 }
375 None => {
376 debug_assert!(
377 false,
378 "unexpected thread state in park_detached_threads: {actual}"
379 );
380 }
381 },
382 }
383 } else if state == ThreadState::Attached as i32 {
384 slot.stop_requested.store(true, Ordering::Release);
386 crate::signal::set_stop_bit();
387 attached_seen = attached_seen.saturating_add(1);
389 }
390 }
392 if attached_seen != 0 {
393 self.stats_attached_seen
394 .fetch_add(attached_seen, Ordering::Relaxed);
395 }
396 if forced_parks != 0 {
397 self.decrement_thread_countdown(forced_parks);
398 self.stats_forced_parks
399 .fetch_add(forced_parks, Ordering::Relaxed);
400 }
401 forced_parks != 0 && self.thread_countdown.load(Ordering::Acquire) == 0
402 }
403
404 fn acquire_exclusion(&self, state: &PyGlobalState) {
425 if self
426 .exclusion
427 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
428 .is_ok()
429 {
430 return;
431 }
432 loop {
433 crate::vm::thread::suspend_if_needed(state);
434 std::thread::yield_now();
435 if self
436 .exclusion
437 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
438 .is_ok()
439 {
440 return;
441 }
442 }
443 }
444
445 fn release_exclusion(&self) {
447 self.exclusion.store(false, Ordering::Release);
448 }
449
450 pub fn stop_the_world(&self, state: &PyGlobalState) {
461 self.acquire_exclusion(state);
462 let start = std::time::Instant::now();
463 let requester_ident = crate::stdlib::_thread::get_ident();
464 self.requester.store(requester_ident, Ordering::Relaxed);
465 self.stats_stop_calls.fetch_add(1, Ordering::Relaxed);
466 let initial_countdown = self.init_thread_countdown(state);
467 stw_trace(format_args!("stop begin requester={requester_ident}"));
468 self.park_detached_threads(state);
473 if initial_countdown == 0 || self.all_non_requester_suspended(state) {
474 self.world_stopped.store(true, Ordering::Release);
475 crate::common::lock::set_world_stopped(true);
476 #[cfg(debug_assertions)]
477 self.debug_assert_all_non_requester_suspended(state);
478 stw_trace(format_args!(
479 "stop end requester={requester_ident} wait_ns=0 polls=0"
480 ));
481 return;
482 }
483
484 let mut polls = 0u64;
485 loop {
486 self.park_detached_threads(state);
487 if self.all_non_requester_suspended(state) {
488 break;
489 }
490 polls = polls.saturating_add(1);
491 let guard = self.notify_mutex.lock().unwrap();
495 if self.all_non_requester_suspended(state) {
496 drop(guard);
497 break;
498 }
499 let _ = self
500 .notify_cv
501 .wait_timeout(guard, core::time::Duration::from_millis(1));
502 }
503 if polls != 0 {
504 self.stats_poll_loops.fetch_add(polls, Ordering::Relaxed);
505 }
506 let wait_ns = start.elapsed().as_nanos().min(u128::from(u64::MAX)) as u64;
507 self.stats_last_wait_ns.store(wait_ns, Ordering::Relaxed);
508 self.stats_total_wait_ns
509 .fetch_add(wait_ns, Ordering::Relaxed);
510 let mut prev_max = self.stats_max_wait_ns.load(Ordering::Relaxed);
511 while wait_ns > prev_max {
512 match self.stats_max_wait_ns.compare_exchange_weak(
513 prev_max,
514 wait_ns,
515 Ordering::Relaxed,
516 Ordering::Relaxed,
517 ) {
518 Ok(_) => break,
519 Err(observed) => prev_max = observed,
520 }
521 }
522 self.world_stopped.store(true, Ordering::Release);
523 crate::common::lock::set_world_stopped(true);
524 #[cfg(debug_assertions)]
525 self.debug_assert_all_non_requester_suspended(state);
526 stw_trace(format_args!(
527 "stop end requester={requester_ident} wait_ns={wait_ns} polls={polls}"
528 ));
529 }
530
531 pub fn start_the_world(&self, state: &PyGlobalState) {
533 use thread::ThreadState;
534 let requester = self.requester.load(Ordering::Relaxed);
535 stw_trace(format_args!("start begin requester={requester}"));
536 let registry = state.thread_frames.lock();
537 self.requested.store(false, Ordering::Release);
543 self.world_stopped.store(false, Ordering::Release);
544 crate::common::lock::set_world_stopped(false);
545
546 #[expect(
547 clippy::iter_over_hash_type,
548 reason = "Iteration order doesn't matter here"
549 )]
550 for (&id, slot) in registry.iter() {
551 if id == requester {
552 continue;
553 }
554
555 slot.stop_requested.store(false, Ordering::Release);
556 let state = slot.state.load(Ordering::Relaxed);
557 if state == ThreadState::ShuttingDown as i32 {
558 continue;
561 }
562 debug_assert!(
563 state == ThreadState::Suspended as i32,
564 "non-requester thread not suspended at start-the-world: id={id} state={state}"
565 );
566 if state == ThreadState::Suspended as i32 {
567 slot.state
568 .store(ThreadState::Detached as i32, Ordering::Release);
569 slot.thread.unpark();
570 }
571 }
572
573 drop(registry);
574 self.thread_countdown.store(0, Ordering::Release);
575 self.requester.store(0, Ordering::Relaxed);
576 crate::signal::clear_stop_bit();
580 #[cfg(debug_assertions)]
581 self.debug_assert_all_non_requester_detached(state);
582 self.release_exclusion();
585 stw_trace(format_args!("start end requester={requester}"));
586 }
587
588 pub fn reset_after_fork(&self) {
590 self.requested.store(false, Ordering::Relaxed);
591 self.world_stopped.store(false, Ordering::Relaxed);
592 crate::common::lock::set_world_stopped(false);
593 self.requester.store(0, Ordering::Relaxed);
594 self.thread_countdown.store(0, Ordering::Relaxed);
595 crate::signal::clear_stop_bit();
598 self.release_exclusion();
601 stw_trace(format_args!("reset-after-fork"));
602 }
603
604 #[inline]
605 pub(crate) fn requester_ident(&self) -> u64 {
606 self.requester.load(Ordering::Relaxed)
607 }
608
609 #[inline]
610 pub(crate) fn notify_thread_gone(&self) {
611 let _guard = self.notify_mutex.lock().unwrap();
612 self.decrement_thread_countdown(1);
613 self.notify_cv.notify_one();
614 }
615
616 pub fn stats_snapshot(&self) -> StopTheWorldStats {
617 StopTheWorldStats {
618 stop_calls: self.stats_stop_calls.load(Ordering::Relaxed),
619 last_wait_ns: self.stats_last_wait_ns.load(Ordering::Relaxed),
620 total_wait_ns: self.stats_total_wait_ns.load(Ordering::Relaxed),
621 max_wait_ns: self.stats_max_wait_ns.load(Ordering::Relaxed),
622 poll_loops: self.stats_poll_loops.load(Ordering::Relaxed),
623 attached_seen: self.stats_attached_seen.load(Ordering::Relaxed),
624 forced_parks: self.stats_forced_parks.load(Ordering::Relaxed),
625 suspend_notifications: self.stats_suspend_notifications.load(Ordering::Relaxed),
626 attach_wait_yields: self.stats_attach_wait_yields.load(Ordering::Relaxed),
627 suspend_wait_yields: self.stats_suspend_wait_yields.load(Ordering::Relaxed),
628 world_stopped: self.world_stopped.load(Ordering::Relaxed),
629 }
630 }
631
632 pub fn reset_stats(&self) {
633 self.stats_stop_calls.store(0, Ordering::Relaxed);
634 self.stats_last_wait_ns.store(0, Ordering::Relaxed);
635 self.stats_total_wait_ns.store(0, Ordering::Relaxed);
636 self.stats_max_wait_ns.store(0, Ordering::Relaxed);
637 self.stats_poll_loops.store(0, Ordering::Relaxed);
638 self.stats_attached_seen.store(0, Ordering::Relaxed);
639 self.stats_forced_parks.store(0, Ordering::Relaxed);
640 self.stats_suspend_notifications.store(0, Ordering::Relaxed);
641 self.stats_attach_wait_yields.store(0, Ordering::Relaxed);
642 self.stats_suspend_wait_yields.store(0, Ordering::Relaxed);
643 }
644
645 #[inline]
646 pub(crate) fn add_attach_wait_yields(&self, n: u64) {
647 if n != 0 {
648 self.stats_attach_wait_yields
649 .fetch_add(n, Ordering::Relaxed);
650 }
651 }
652
653 #[inline]
654 pub(crate) fn add_suspend_wait_yields(&self, n: u64) {
655 if n != 0 {
656 self.stats_suspend_wait_yields
657 .fetch_add(n, Ordering::Relaxed);
658 }
659 }
660
661 fn all_non_requester_suspended(&self, state: &PyGlobalState) -> bool {
669 use thread::ThreadState;
670 let requester = self.requester.load(Ordering::Relaxed);
671 let registry = state.thread_frames.lock();
672
673 #[expect(
674 clippy::iter_over_hash_type,
675 reason = "Iteration order doesn't matter here"
676 )]
677 for (&id, slot) in registry.iter() {
678 if id == requester {
679 continue;
680 }
681 let slot_state = slot.state.load(Ordering::Acquire);
682 if slot_state != ThreadState::Suspended as i32
683 && slot_state != ThreadState::ShuttingDown as i32
684 {
685 return false;
686 }
687 }
688 true
689 }
690
691 #[cfg(debug_assertions)]
692 fn debug_assert_all_non_requester_suspended(&self, state: &PyGlobalState) {
693 use thread::ThreadState;
694 let requester = self.requester.load(Ordering::Relaxed);
695 let registry = state.thread_frames.lock();
696
697 #[expect(
698 clippy::iter_over_hash_type,
699 reason = "Iteration order doesn't matter here"
700 )]
701 for (&id, slot) in registry.iter() {
702 if id == requester {
703 continue;
704 }
705
706 let state = slot.state.load(Ordering::Relaxed);
707 debug_assert!(
708 state == ThreadState::Suspended as i32 || state == ThreadState::ShuttingDown as i32,
709 "non-requester thread not suspended during stop-the-world: id={id} state={state}"
710 );
711 }
712 }
713
714 #[cfg(debug_assertions)]
715 fn debug_assert_all_non_requester_detached(&self, state: &PyGlobalState) {
716 use thread::ThreadState;
717 let requester = self.requester.load(Ordering::Relaxed);
718 let registry = state.thread_frames.lock();
719
720 #[expect(
721 clippy::iter_over_hash_type,
722 reason = "Iteration order doesn't matter here"
723 )]
724 for (&id, slot) in registry.iter() {
725 if id == requester {
726 continue;
727 }
728
729 let state = slot.state.load(Ordering::Relaxed);
730 debug_assert!(
731 state != ThreadState::Suspended as i32,
732 "non-requester thread still suspended after start-the-world: id={id} state={state}"
733 );
734 }
735 }
736}
737
738#[cfg(feature = "threading")]
739pub(super) fn stw_trace_enabled() -> bool {
740 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
741 *ENABLED.get_or_init(|| crate::host_env::os::var_os("RUSTPYTHON_STW_TRACE").is_some())
742}
743
744#[cfg(feature = "threading")]
745pub(super) fn stw_trace(msg: core::fmt::Arguments<'_>) {
746 if stw_trace_enabled() {
747 use core::fmt::Write as _;
748
749 struct FixedBuf {
752 buf: [u8; 512],
753 len: usize,
754 }
755
756 impl core::fmt::Write for FixedBuf {
757 fn write_str(&mut self, s: &str) -> core::fmt::Result {
758 if self.len >= self.buf.len() {
759 return Ok(());
760 }
761 let remain = self.buf.len() - self.len;
762 let src = s.as_bytes();
763 let n = src.len().min(remain);
764 self.buf[self.len..self.len + n].copy_from_slice(&src[..n]);
765 self.len += n;
766 Ok(())
767 }
768 }
769
770 let mut out = FixedBuf {
771 buf: [0u8; 512],
772 len: 0,
773 };
774 let _ = writeln!(
775 &mut out,
776 "[rp-stw tid={}] {}",
777 crate::stdlib::_thread::get_ident(),
778 msg
779 );
780 #[cfg(unix)]
781 crate::host_env::io::write_stderr_raw(&out.buf[..out.len]);
782 #[cfg(not(unix))]
783 {
784 use std::io::Write as _;
785 let _ = std::io::stderr().write_all(&out.buf[..out.len]);
786 }
787 }
788}
789
790#[derive(Clone, Debug, Default)]
791pub(crate) struct CallableCache {
792 pub len: Option<PyObjectRef>,
793 pub isinstance: Option<PyObjectRef>,
794 pub list_append: Option<PyObjectRef>,
795 pub builtin_all: Option<PyObjectRef>,
796 pub builtin_any: Option<PyObjectRef>,
797}
798
799pub struct PyGlobalState {
805 pub interpreter_id: i64,
807 pub runtime_root_id: i64,
809 pub whence: runtime::InterpreterWhence,
811 pub is_main: bool,
815 pub config: PyConfig,
816 pub module_defs: BTreeMap<&'static str, &'static builtins::PyModuleDef>,
817 pub frozen: HashMap<&'static str, FrozenModule, rapidhash::quality::RandomState>,
818 pub stacksize: AtomicCell<usize>,
819 pub thread_count: AtomicCell<usize>,
820 pub atexit_funcs: PyMutex<Vec<PyRc<(PyObjectRef, FuncArgs)>>>,
824 pub(crate) audit_hooks: PyMutex<Vec<PyObjectRef>>,
826 pub codec_registry: CodecsRegistry,
827 pub struct_format_cache: crate::buffer::FormatSpecCache,
828 pub finalizing: AtomicBool,
829 #[cfg(feature = "threading")]
831 pub(crate) finalizing_thread_ident: AtomicCell<u64>,
832 pub warnings: WarningsState,
833 pub override_frozen_modules: AtomicCell<isize>,
834 pub before_forkers: PyMutex<Vec<PyObjectRef>>,
835 pub after_forkers_child: PyMutex<Vec<PyObjectRef>>,
836 pub after_forkers_parent: PyMutex<Vec<PyObjectRef>>,
837 pub int_max_str_digits: AtomicCell<usize>,
838 pub switch_interval: AtomicCell<f64>,
839 pub global_trace_func: PyMutex<Option<PyObjectRef>>,
841 pub global_profile_func: PyMutex<Option<PyObjectRef>>,
843 pub type_mutex: PyMutex<()>,
845 #[cfg(feature = "threading")]
847 pub main_thread_ident: AtomicCell<u64>,
848 #[cfg(feature = "threading")]
850 pub thread_frames: parking_lot::Mutex<HashMap<u64, stdlib::_thread::CurrentFrameSlot>>,
851 #[cfg(feature = "threading")]
853 pub thread_handles: parking_lot::Mutex<Vec<stdlib::_thread::HandleEntry>>,
854 #[cfg(feature = "threading")]
856 pub shutdown_handles: parking_lot::Mutex<Vec<stdlib::_thread::ShutdownEntry>>,
857 pub monitoring: PyMutex<stdlib::sys::monitoring::MonitoringState>,
859 pub monitoring_events: stdlib::sys::monitoring::MonitoringEventsMask,
861 pub instrumentation_version: AtomicU64,
864 #[cfg(feature = "threading")]
866 pub stop_the_world: StopTheWorldState,
867 pub gc: crate::gc_state::GcInterpreterState,
869 pub feature_flags: runtime::InterpFeatureFlags,
871 pub own_gil: bool,
873 pub running_main: AtomicBool,
875 pub ready: AtomicBool,
877 pub id_refcount: AtomicI64,
879 pub require_idref: AtomicBool,
881}
882
883impl PyGlobalState {
884 #[inline]
885 #[must_use]
886 pub fn is_main_interpreter(&self) -> bool {
887 self.is_main
888 }
889
890 #[inline]
891 #[must_use]
892 pub fn allow_fork(&self) -> bool {
893 self.feature_flags.allow_fork
894 }
895
896 #[inline]
897 #[must_use]
898 pub fn allow_exec(&self) -> bool {
899 self.feature_flags.allow_exec
900 }
901
902 #[inline]
903 #[must_use]
904 pub fn allow_threads(&self) -> bool {
905 self.feature_flags.allow_threads
906 }
907
908 #[inline]
909 #[must_use]
910 pub fn allow_daemon_threads(&self) -> bool {
911 self.feature_flags.allow_daemon_threads
912 }
913
914 #[must_use]
917 pub fn config(&self) -> runtime::InterpreterConfig {
918 runtime::InterpreterConfig::from_state(self.feature_flags, self.own_gil)
919 }
920}
921
922static HASH_SECRET: std::sync::OnceLock<HashSecret> = std::sync::OnceLock::new();
925
926pub(crate) fn init_hash_secret(hash_seed: Option<u32>) {
931 let _ = HASH_SECRET.get_or_init(|| {
932 let seed = hash_seed.unwrap_or_else(|| {
933 u32::from_ne_bytes(rustpython_common::rand::os_random())
935 });
936 HashSecret::new(seed)
937 });
938}
939
940#[inline]
942#[must_use]
943pub(crate) fn hash_secret() -> &'static HashSecret {
944 HASH_SECRET
945 .get()
946 .expect("hash secret is set by the first top-level interpreter")
947}
948
949#[repr(transparent)]
963struct PendingFrame(core::ptr::NonNull<crate::frame::InterpreterFrame>);
964
965impl Copy for PendingFrame {}
966impl Clone for PendingFrame {
967 fn clone(&self) -> Self {
968 *self
969 }
970}
971
972unsafe impl Send for PendingFrame {}
976unsafe impl Sync for PendingFrame {}
977
978pub(crate) struct GenFrameLink {
981 old_chain: *const crate::frame::InterpreterFrame,
982 old_owner: i8,
983}
984
985pub(crate) struct IframeEntryState {
988 pub(crate) iframe_ptr: *const crate::frame::InterpreterFrame,
989 pub(crate) old_chain: *const crate::frame::InterpreterFrame,
990 pub(crate) saved_exc: Option<PyBaseExceptionRef>,
991 pub(crate) save_exc: bool,
992}
993
994struct PendingGenResume {
996 jen: PyObjectRef,
999 value: PyObjectRef,
1001 cont: crate::frame::GenCont,
1003}
1004
1005enum FrameKind {
1011 Entry(IframeEntryState),
1015 GenEntry,
1020 Callee(IframeEntryState),
1022 Gen(crate::coroutine::FlatResume),
1025}
1026
1027impl FrameKind {
1028 #[inline]
1030 const fn flatten(&self) -> crate::frame::Flatten {
1031 match self {
1032 Self::Entry(_) | Self::Callee(_) => crate::frame::Flatten::CallAndGenResume,
1033 Self::GenEntry | Self::Gen(_) => crate::frame::Flatten::GenResume,
1034 }
1035 }
1036}
1037
1038struct TrampolineIFrame {
1046 ptr: *mut crate::frame::InterpreterFrame,
1047}
1048
1049impl TrampolineIFrame {
1050 fn from_mut(iframe: &mut crate::frame::InterpreterFrame) -> Self {
1051 Self { ptr: iframe }
1052 }
1053
1054 unsafe fn from_ptr(ptr: *mut crate::frame::InterpreterFrame) -> Self {
1058 Self { ptr }
1059 }
1060
1061 fn as_mut(&mut self) -> &mut crate::frame::InterpreterFrame {
1062 unsafe { &mut *self.ptr }
1064 }
1065}
1066
1067struct SuspendedFrame {
1069 iframe: TrampolineIFrame,
1070 kind: FrameKind,
1071 callee_owner: Option<PyObjectRef>,
1077 cont: crate::frame::GenCont,
1081}
1082
1083unsafe impl Send for SuspendedFrame {}
1089unsafe impl Sync for SuspendedFrame {}
1091
1092enum Outcome {
1094 Value(PyObjectRef),
1096 GenStop(Option<PyObjectRef>),
1099 Raise(PyBaseExceptionRef),
1101}
1102
1103enum TrampolineStart {
1105 Ran(PyResult<crate::frame::ExecutionResult>),
1107 Delegating {
1110 delegate: PyObjectRef,
1111 value: PyObjectRef,
1112 cont: crate::frame::GenCont,
1113 },
1114}
1115
1116enum GenEntered {
1118 Ran {
1121 iframe: TrampolineIFrame,
1122 state: crate::coroutine::FlatResume,
1123 result: PyResult<crate::frame::ExecutionResult>,
1124 },
1125 Failed(Outcome),
1128}
1129
1130#[derive(Clone, Copy)]
1134enum LengthHint<'a> {
1135 Unasked,
1138 Iterable(&'a dyn Fn() -> usize),
1141}
1142
1143impl VirtualMachine {
1144 fn init_callable_cache(&mut self) -> PyResult<()> {
1145 self.callable_cache.len = Some(self.builtins.get_attr("len", self)?);
1146 self.callable_cache.isinstance = Some(self.builtins.get_attr("isinstance", self)?);
1147 let list_append = self
1148 .ctx
1149 .types
1150 .list_type
1151 .get_attr(self.ctx.intern_str("append"))
1152 .ok_or_else(|| self.new_runtime_error("failed to cache list.append"))?;
1153 self.callable_cache.list_append = Some(list_append);
1154 self.callable_cache.builtin_all = Some(self.builtins.get_attr("all", self)?);
1155 self.callable_cache.builtin_any = Some(self.builtins.get_attr("any", self)?);
1156 Ok(())
1157 }
1158
1159 #[inline(always)]
1164 pub(crate) fn datastack_push(&self, size: usize) -> *mut u8 {
1165 unsafe { (*self.datastack.get()).push(size) }
1166 }
1167
1168 #[inline(always)]
1171 pub(crate) fn datastack_push_frame(&self, size: usize) -> (*mut u8, bool) {
1172 unsafe { (*self.datastack.get()).push_frame(size) }
1173 }
1174
1175 #[inline(always)]
1177 pub(crate) fn datastack_has_space(&self, size: usize) -> bool {
1178 unsafe { (*self.datastack.get()).has_space(size) }
1179 }
1180
1181 #[inline(always)]
1187 pub(crate) unsafe fn datastack_pop(&self, base: *mut u8) {
1188 unsafe { (*self.datastack.get()).pop(base) }
1189 }
1190
1191 #[inline(always)]
1193 pub(crate) unsafe fn datastack_pop_frame(&self, base: *mut u8, size: usize) {
1194 unsafe { (*self.datastack.get()).pop_frame(base, size) }
1195 }
1196
1197 #[inline]
1203 pub fn allow_threads<R>(&self, f: impl FnOnce() -> R) -> R {
1204 thread::allow_threads(self, f)
1205 }
1206
1207 #[inline]
1214 pub fn attach_for_callback<R>(&self, f: impl FnOnce() -> R) -> R {
1215 thread::attach_for_callback(self, f)
1216 }
1217
1218 #[allow(dead_code)]
1221 pub(crate) fn is_main_thread(&self) -> bool {
1222 cfg_select! {
1223 feature = "threading" => {
1224 crate::stdlib::_thread::get_ident() == self.state.main_thread_ident.load()
1225 }
1226 _ => true,
1227 }
1228 }
1229
1230 pub(crate) fn new(ctx: PyRc<Context>, state: PyRc<PyGlobalState>) -> Self {
1232 flame_guard!("new VirtualMachine");
1233
1234 let new_module = |def| {
1237 PyRef::new_ref(
1238 PyModule::from_def(def),
1239 ctx.types.module_type.to_owned(),
1240 Some(ctx.new_dict()),
1241 )
1242 };
1243
1244 let builtins = new_module(stdlib::builtins::module_def(&ctx));
1246 let sys_module = new_module(stdlib::sys::module_def(&ctx));
1247
1248 let import_func = ctx.none();
1249 let importlib = ctx.none();
1250 let profile_func = RefCell::new(ctx.none());
1251 let trace_func = RefCell::new(ctx.none());
1252 let signal_handlers = OnceCell::from(SignalHandlers::default());
1253
1254 let vm = Self {
1255 builtins,
1256 sys_module,
1257 ctx,
1258 datastack: core::cell::UnsafeCell::new(crate::datastack::DataStack::new()),
1259 wasm_id: None,
1260 exceptions: RefCell::default(),
1261 import_func,
1262 importlib,
1263 profile_func,
1264 trace_func,
1265 use_tracing: Cell::new(false),
1266 what_event: Cell::new(None),
1267 tracing_depth: Cell::new(0),
1268 recursion_limit: Cell::new(if cfg!(debug_assertions) { 256 } else { 1000 }),
1269 signal_handlers,
1270 signal_rx: None,
1271 repr_guards: RefCell::default(),
1272 state,
1273 initialized: false,
1274 recursion_depth: Cell::new(0),
1275 #[cfg(any(miri, target_env = "musl"))]
1276 native_recursion_depth: Cell::new(0),
1277 c_stack_soft_limit: Cell::new(Self::calculate_c_stack_soft_limit()),
1278 async_gen_firstiter: RefCell::new(None),
1279 async_gen_finalizer: RefCell::new(None),
1280 asyncio_running_loop: RefCell::new(None),
1281 asyncio_running_task: RefCell::new(None),
1282 context_stack: RefCell::default(),
1283 callable_cache: CallableCache::default(),
1284 pending_tailcall_frame: Cell::new(None),
1285 pending_tailcall_owner: core::cell::UnsafeCell::new(None),
1286 pending_gen_resume: core::cell::UnsafeCell::new(None),
1287 trampoline_stack: core::cell::UnsafeCell::new(Vec::new()),
1288 };
1289
1290 vm.builtins.init_dict(
1291 vm.ctx.intern_str("builtins"),
1292 crate::function::plain_doc(stdlib::builtins::DOC)
1293 .map(|doc| vm.ctx.intern_str(doc).to_owned()),
1294 &vm,
1295 );
1296 vm.sys_module.init_dict(
1297 vm.ctx.intern_str("sys"),
1298 crate::function::plain_doc(stdlib::sys::DOC)
1299 .map(|doc| vm.ctx.intern_str(doc).to_owned()),
1300 &vm,
1301 );
1302 vm
1304 }
1305
1306 #[cfg(feature = "encodings")]
1309 fn import_encodings(&mut self) -> PyResult<()> {
1310 self.import("encodings", 0).map_err(|import_err| {
1311 let rustpythonpath_env = crate::host_env::os::var("RUSTPYTHONPATH").ok();
1312 let pythonpath_env = crate::host_env::os::var("PYTHONPATH").ok();
1313 let env_set = rustpythonpath_env.as_ref().is_some() || pythonpath_env.as_ref().is_some();
1314 let path_contains_env = self.state.config.paths.module_search_paths.iter().any(|s| {
1315 Some(s.as_str()) == rustpythonpath_env.as_deref() || Some(s.as_str()) == pythonpath_env.as_deref()
1316 });
1317
1318 let guide_message = if cfg!(feature = "freeze-stdlib") {
1319 "`rustpython_pylib` may not be set while using `freeze-stdlib` feature. Try using `rustpython::InterpreterBuilder::init_stdlib` or manually call `builder.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)` in `rustpython_vm::Interpreter::builder()`."
1320 } else if !env_set {
1321 "Neither RUSTPYTHONPATH nor PYTHONPATH is set. Try setting one of them to the stdlib directory."
1322 } else if path_contains_env {
1323 "RUSTPYTHONPATH or PYTHONPATH is set, but it doesn't contain the encodings library. If you are customizing the RustPython vm/interpreter, try adding the stdlib directory to the path. If you are developing the RustPython interpreter, it might be a bug during development."
1324 } else {
1325 "RUSTPYTHONPATH or PYTHONPATH is set, but it wasn't loaded to `PyConfig::paths::module_search_paths`. If you are going to customize the RustPython vm/interpreter, those environment variables are not loaded in the Settings struct by default. Please try creating a customized instance of the Settings struct. If you are developing the RustPython interpreter, it might be a bug during development."
1326 };
1327
1328 let mut msg = format!(
1329 "RustPython could not import the encodings module. It usually means something went wrong. Please carefully read the following messages and follow the steps.\n\
1330 \n\
1331 {guide_message}");
1332 if !cfg!(feature = "freeze-stdlib") {
1333 msg += "\n\
1334 If you don't have access to a consistent external environment (e.g. targeting wasm, embedding \
1335 rustpython in another application), try enabling the `freeze-stdlib` feature.\n\
1336 If this is intended and you want to exclude the encodings module from your interpreter, please remove the `encodings` feature from `rustpython-vm` crate.";
1337 }
1338
1339 let err = self.new_runtime_error(msg);
1340 err.set_cause(Some(import_err));
1341 err
1342 })?;
1343 Ok(())
1344 }
1345
1346 fn import_ascii_utf8_encodings(&mut self) -> PyResult<()> {
1347 self.import("codecs", 0)?;
1350
1351 let (ascii_module_name, utf8_module_name, latin1_module_name) =
1354 if cfg!(feature = "freeze-stdlib") {
1355 ("encodings.ascii", "encodings.utf_8", "encodings.latin_1")
1356 } else {
1357 ("encodings_ascii", "encodings_utf_8", "encodings_latin_1")
1358 };
1359
1360 self.import(ascii_module_name, 0)?;
1363 let sys_modules = self.sys_module.get_attr(identifier!(self, modules), self)?;
1364 let ascii_module = sys_modules.get_item(ascii_module_name, self)?;
1365 let getregentry = ascii_module.get_attr("getregentry", self)?;
1366 let codec_info = getregentry.call((), self)?;
1367 self.state
1368 .codec_registry
1369 .register_manual("ascii", codec_info.try_into_value(self)?);
1370
1371 self.import(utf8_module_name, 0)?;
1374 let utf8_module = sys_modules.get_item(utf8_module_name, self)?;
1375 let getregentry = utf8_module.get_attr("getregentry", self)?;
1376 let codec_info = getregentry.call((), self)?;
1377 let utf8_codec: crate::codecs::PyCodec = codec_info.try_into_value(self)?;
1378 self.state
1379 .codec_registry
1380 .register_manual("utf-8", utf8_codec.clone());
1381 self.state
1382 .codec_registry
1383 .register_manual("utf8", utf8_codec);
1384
1385 self.import(latin1_module_name, 0)?;
1388 let latin1_module = sys_modules.get_item(latin1_module_name, self)?;
1389 let getregentry = latin1_module.get_attr("getregentry", self)?;
1390 let codec_info = getregentry.call((), self)?;
1391 let latin1_codec: crate::codecs::PyCodec = codec_info.try_into_value(self)?;
1392 for name in ["latin-1", "latin_1", "latin1", "iso8859-1", "iso8859_1"] {
1393 self.state
1394 .codec_registry
1395 .register_manual(name, latin1_codec.clone());
1396 }
1397 Ok(())
1398 }
1399
1400 fn initialize(&mut self) {
1401 flame_guard!("init VirtualMachine");
1402
1403 assert!(!self.initialized, "Double Initialize Error");
1404
1405 #[cfg(feature = "threading")]
1408 if self.state.is_main_interpreter() {
1409 stdlib::_thread::init_main_thread_ident(self);
1410 }
1411
1412 let prewarmed_memory_errors: Vec<_> = (0..PyMemoryError::MAX_FREELIST)
1413 .map(|_| self.no_memory_error())
1414 .collect();
1415 drop(prewarmed_memory_errors);
1416
1417 stdlib::builtins::init_module(self, &self.builtins);
1418 let callable_cache_init = self.init_callable_cache();
1419 self.expect_pyresult(callable_cache_init, "failed to initialize callable cache");
1420 stdlib::sys::init_module(self, &self.sys_module, &self.builtins);
1421 self.expect_pyresult(
1422 stdlib::sys::set_bootstrap_stderr(self),
1423 "failed to initialize bootstrap stderr",
1424 );
1425
1426 let mut essential_init = || -> PyResult {
1427 import::import_builtin(self, "_typing")?;
1428 #[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))]
1429 import::import_builtin(self, "_signal")?;
1430 #[cfg(any(feature = "parser", feature = "compiler"))]
1431 import::import_builtin(self, "_ast")?;
1432 #[cfg(not(feature = "threading"))]
1433 import::import_frozen(self, "_thread")?;
1434 let importlib = import::init_importlib_base(self)?;
1435 self.import_ascii_utf8_encodings()?;
1436
1437 {
1438 let io = import::import_builtin(self, "_io")?;
1439
1440 #[cfg(all(feature = "host_env", feature = "stdio"))]
1442 let make_stdio = |name: &str, fd: i32, write: bool| -> PyResult<PyObjectRef> {
1443 let buffered_stdio = self.state.config.settings.buffered_stdio;
1444 let unbuffered = write && !buffered_stdio;
1445 let buf = crate::stdlib::_io::open(
1446 self.ctx.new_int(fd).into(),
1447 Some(if write { "wb" } else { "rb" }),
1448 crate::stdlib::_io::OpenArgs {
1449 buffering: if unbuffered { 0 } else { -1 },
1450 closefd: false,
1451 ..Default::default()
1452 },
1453 self,
1454 )?;
1455 let raw = if unbuffered {
1456 buf.clone()
1457 } else {
1458 buf.get_attr("raw", self)?
1459 };
1460 raw.set_attr("name", self.ctx.new_str(format!("<{name}>")), self)?;
1461 let isatty = self.call_method(&raw, "isatty", ())?.is_true(self)?;
1462 let write_through = !buffered_stdio;
1463 let line_buffering = buffered_stdio && (isatty || fd == 2);
1464
1465 let newline = if cfg!(windows) { None } else { Some("\n") };
1466 let encoding = self.state.config.settings.stdio_encoding.as_deref();
1467 let errors = if fd == 2 {
1469 Some("backslashreplace")
1470 } else {
1471 self.state
1472 .config
1473 .settings
1474 .stdio_errors
1475 .as_deref()
1476 .or_else(|| {
1477 Some(if self.state.config.settings.stdio_encoding.is_some() {
1478 "strict"
1479 } else {
1480 "surrogateescape"
1481 })
1482 })
1483 };
1484
1485 let stdio = self.call_method(
1486 &io,
1487 "TextIOWrapper",
1488 (
1489 buf,
1490 encoding,
1491 errors,
1492 newline,
1493 line_buffering,
1494 write_through,
1495 ),
1496 )?;
1497 let mode = if write { "w" } else { "r" };
1498 stdio.set_attr("mode", self.ctx.new_str(mode), self)?;
1499 Ok::<_, self::PyBaseExceptionRef>(stdio)
1500 };
1501
1502 #[cfg(all(not(feature = "host_env"), feature = "stdio"))]
1504 let make_stdio = |name: &str, fd: i32, write: bool| {
1505 let mode = if write { "w" } else { "r" };
1506 let stdio = stdlib::sys::SandboxStdio {
1507 fd,
1508 name: format!("<{name}>"),
1509 mode: mode.to_owned(),
1510 }
1511 .into_ref(&self.ctx);
1512 Ok(stdio.into())
1513 };
1514
1515 #[cfg(not(feature = "stdio"))]
1517 let make_stdio = |_name: &str, _fd: i32, _write: bool| {
1518 Ok(crate::builtins::PyNone.into_pyobject(self))
1519 };
1520
1521 let set_stdio = |name, fd, write| {
1522 let stdio: PyObjectRef = make_stdio(name, fd, write)?;
1523 let dunder_name = self.ctx.intern_str(format!("__{name}__"));
1524 self.sys_module.set_attr(
1525 dunder_name, stdio.clone(),
1527 self,
1528 )?;
1529 self.sys_module.set_attr(name, stdio, self)?;
1530 Ok(())
1531 };
1532 set_stdio("stdin", 0, false)?;
1533 set_stdio("stdout", 1, true)?;
1534 set_stdio("stderr", 2, true)?;
1535
1536 let io_open = io.get_attr("open", self)?;
1537 self.builtins.set_attr("open", io_open, self)?;
1538 }
1539
1540 Ok(importlib)
1541 };
1542
1543 let res = essential_init();
1544 let importlib = self.expect_pyresult(res, "essential initialization failed");
1545
1546 #[cfg(feature = "host_env")]
1547 if self.state.config.settings.allow_external_library
1548 && cfg!(feature = "rustpython-compiler")
1549 && let Err(e) = import::init_importlib_package(self, &importlib)
1550 {
1551 eprintln!(
1552 "importlib initialization failed. This is critical for many complicated packages."
1553 );
1554 self.print_exception(&e);
1555 }
1556
1557 #[cfg(not(feature = "host_env"))]
1558 let _ = importlib;
1559
1560 let _expect_stdlib = cfg!(feature = "freeze-stdlib")
1561 || !self.state.config.paths.module_search_paths.is_empty();
1562
1563 #[cfg(feature = "encodings")]
1564 if _expect_stdlib {
1565 if let Err(e) = self.import_encodings() {
1566 eprintln!(
1567 "encodings initialization failed. Only utf-8 encoding will be supported."
1568 );
1569 self.print_exception(&e);
1570 }
1571 } else {
1572 eprintln!(
1575 "feature `encodings` is enabled but `paths.module_search_paths` is empty. \
1576 Please add the library path to `settings.path_list`. If you intended to disable the entire standard library (including the `encodings` feature), please also make sure to disable the `encodings` feature.\n\
1577 Tip: You may also want to add `\"\"` to `settings.path_list` in order to enable importing from the current working directory."
1578 );
1579 }
1580
1581 self.initialized = true;
1582 }
1583
1584 pub fn set_user_signal_channel(&mut self, signal_rx: signal::UserSignalReceiver) {
1586 self.signal_rx = Some(signal_rx);
1587 }
1588
1589 pub fn run_pyc_bytes(&self, pyc_bytes: &[u8], scope: Scope) -> PyResult<()> {
1618 let code = PyCode::from_pyc(pyc_bytes, Some("<pyc_bytes>"), None, None, self)?;
1619 self.with_simple_run("<source>", |_module_dict| {
1620 self.run_code_obj(code, scope)?;
1621 Ok(())
1622 })
1623 }
1624
1625 pub fn run_code_obj(&self, code: PyRef<PyCode>, scope: Scope) -> PyResult {
1626 self.run_code_obj_with_closure(code, scope, None)
1627 }
1628
1629 pub(crate) fn run_code_obj_with_closure(
1630 &self,
1631 code: PyRef<PyCode>,
1632 scope: Scope,
1633 closure: Option<PyRef<crate::builtins::PyTuple<crate::builtins::function::PyCellRef>>>,
1634 ) -> PyResult {
1635 use crate::builtins::PyFunction;
1636
1637 let mut func = PyFunction::new(code, scope.globals.clone(), self)?;
1639 if let Some(closure) = closure {
1640 func.closure = Some(closure);
1641 }
1642 let func = func.into_ref(&self.ctx);
1643 func.invoke_with_locals(FuncArgs::default(), scope.locals, self)
1644 }
1645
1646 #[cold]
1647 pub fn run_unraisable(&self, e: PyBaseExceptionRef, msg: Option<String>, object: PyObjectRef) {
1648 if self.state.finalizing.load(Ordering::Acquire) {
1652 self.write_unraisable_to_stderr(&e, msg.as_deref(), &object);
1653 return;
1654 }
1655
1656 let sys_module = self.import("sys", 0).unwrap();
1657 let unraisablehook = sys_module.get_attr("unraisablehook", self).unwrap();
1658
1659 let exc_type = e.class().to_owned();
1660 let exc_traceback = e.traceback().to_pyobject(self); let exc_value = e.into();
1662 let args = stdlib::sys::UnraisableHookArgsData {
1663 exc_type,
1664 exc_value,
1665 exc_traceback,
1666 err_msg: self.new_pyobj(msg),
1667 object,
1668 };
1669 if let Err(e) = unraisablehook.call((args,), self) {
1670 println!("{}", e.as_object().repr(self).unwrap());
1671 }
1672 }
1673
1674 fn write_unraisable_to_stderr(
1677 &self,
1678 e: &Py<PyBaseException>,
1679 msg: Option<&str>,
1680 object: &PyObject,
1681 ) {
1682 let stderr = crate::stdlib::sys::get_stderr(self).ok();
1684
1685 let write_to_stderr = |s: &str, stderr: &Option<PyObjectRef>, vm: &Self| {
1686 if let Some(stderr) = stderr {
1687 let _ = vm.call_method(stderr, "write", (s.to_owned(),));
1688 } else {
1689 eprint!("{s}");
1690 }
1691 };
1692
1693 if self.is_none(object) {
1694 if let Some(msg) = msg {
1695 write_to_stderr(&format!("{msg}:\n"), &stderr, self);
1696 }
1697 } else {
1698 let msg_str = if let Some(msg) = msg {
1699 format!("{msg}: ")
1700 } else {
1701 "Exception ignored in: ".to_owned()
1702 };
1703 write_to_stderr(&msg_str, &stderr, self);
1704
1705 let repr_result = object.repr(self);
1706 let repr_wtf8 = repr_result
1707 .as_ref()
1708 .map_or_else(|_| "<object repr failed>".as_ref(), |s| s.as_wtf8());
1709 write_to_stderr(&format!("{repr_wtf8}\n"), &stderr, self);
1710 }
1711
1712 let exc_type_name = e.class().name();
1714 let msg = match e.as_object().str(self) {
1715 Ok(exc_str) if !exc_str.as_wtf8().is_empty() => {
1716 format!("{}: {}\n", exc_type_name, exc_str.as_wtf8())
1717 }
1718 _ => format!("{exc_type_name}\n"),
1719 };
1720 write_to_stderr(&msg, &stderr, self);
1721
1722 if let Some(ref stderr) = stderr {
1724 let _ = self.call_method(stderr, "flush", ());
1725 }
1726 }
1727
1728 #[inline(always)]
1733 pub(crate) fn set_pending_tailcall(&self, iframe: &mut crate::frame::InterpreterFrame) {
1734 self.pending_tailcall_frame
1735 .set(Some(PendingFrame(core::ptr::NonNull::from(iframe))));
1736 }
1737
1738 #[inline(always)]
1740 pub(crate) fn set_pending_tailcall_owner(&self, owner: PyObjectRef) {
1741 let slot = unsafe { &mut *self.pending_tailcall_owner.get() };
1742 debug_assert!(slot.is_none(), "pending TailCall owner was not consumed");
1743 *slot = Some(owner);
1744 }
1745
1746 #[inline(always)]
1748 fn take_pending_tailcall_owner(&self) -> PyObjectRef {
1749 unsafe { &mut *self.pending_tailcall_owner.get() }
1750 .take()
1751 .expect("TailCall without pending owner")
1752 }
1753
1754 #[inline]
1758 pub(crate) fn set_pending_gen_resume(
1759 &self,
1760 jen: PyObjectRef,
1761 value: PyObjectRef,
1762 cont: crate::frame::GenCont,
1763 ) {
1764 let slot = unsafe { &mut *self.pending_gen_resume.get() };
1767 debug_assert!(slot.is_none(), "pending GenResume was not consumed");
1768 *slot = Some(PendingGenResume { jen, value, cont });
1769 }
1770
1771 #[inline]
1773 fn take_pending_gen_resume(&self) -> PendingGenResume {
1774 unsafe { &mut *self.pending_gen_resume.get() }
1776 .take()
1777 .expect("GenResume without a parked generator")
1778 }
1779
1780 #[inline]
1782 fn trampoline_push(&self, frame: SuspendedFrame) {
1783 unsafe { (*self.trampoline_stack.get()).push(frame) }
1785 }
1786
1787 #[inline]
1790 fn trampoline_pop(&self, base: usize) -> Option<SuspendedFrame> {
1791 let stack = unsafe { &mut *self.trampoline_stack.get() };
1793 if stack.len() > base {
1794 stack.pop()
1795 } else {
1796 None
1797 }
1798 }
1799
1800 #[inline]
1803 fn trampoline_depth(&self) -> usize {
1804 unsafe { (*self.trampoline_stack.get()).len() }
1806 }
1807
1808 #[inline(always)]
1810 fn take_pending_tailcall(&self) -> *mut crate::frame::InterpreterFrame {
1811 self.pending_tailcall_frame
1812 .take()
1813 .expect("TailCall without pending frame")
1814 .0
1815 .as_ptr()
1816 }
1817
1818 #[inline(always)]
1823 pub fn run_frame_fast(&self, iframe: &mut crate::frame::InterpreterFrame) -> PyResult {
1824 use crate::frame::ExecutionResult;
1825
1826 let entry_state = self.enter_iframe(iframe)?;
1827 let result =
1828 crate::frame::run_iframe(iframe, crate::frame::Flatten::CallAndGenResume, self);
1829
1830 match result {
1831 Ok(ExecutionResult::Return(value)) => {
1832 self.exit_iframe(entry_state);
1833 Ok(value)
1834 }
1835 Ok(first @ (ExecutionResult::TailCall | ExecutionResult::GenResume)) => {
1836 match self.run_trampoline(
1837 iframe,
1838 FrameKind::Entry(entry_state),
1839 TrampolineStart::Ran(Ok(first)),
1840 )? {
1841 ExecutionResult::Return(value) => Ok(value),
1842 _ => panic!("non-return result from a plain call frame"),
1843 }
1844 }
1845 Ok(ExecutionResult::Yield(_)) => panic!("Yield in non-generator frame"),
1846 Err(exc) => {
1847 self.exit_iframe(entry_state);
1848 Err(exc)
1849 }
1850 }
1851 }
1852
1853 #[inline(always)]
1862 pub(crate) fn run_gen_frame(
1863 &self,
1864 iframe: &mut crate::frame::InterpreterFrame,
1865 ) -> PyResult<ExecutionResult> {
1866 match crate::frame::run_iframe(iframe, crate::frame::Flatten::GenResume, self) {
1867 Ok(first @ (ExecutionResult::TailCall | ExecutionResult::GenResume)) => {
1868 self.run_trampoline(iframe, FrameKind::GenEntry, TrampolineStart::Ran(Ok(first)))
1869 }
1870 result => result,
1871 }
1872 }
1873
1874 #[inline(always)]
1880 pub(crate) fn run_gen_frame_delegating(
1881 &self,
1882 iframe: &mut crate::frame::InterpreterFrame,
1883 delegate: PyObjectRef,
1884 value: PyObjectRef,
1885 cont: crate::frame::GenCont,
1886 ) -> PyResult<ExecutionResult> {
1887 self.run_trampoline(
1888 iframe,
1889 FrameKind::GenEntry,
1890 TrampolineStart::Delegating {
1891 delegate,
1892 value,
1893 cont,
1894 },
1895 )
1896 }
1897
1898 #[inline(always)]
1901 fn trampoline_run(
1902 &self,
1903 iframe: &mut crate::frame::InterpreterFrame,
1904 kind: &FrameKind,
1905 ) -> PyResult<ExecutionResult> {
1906 crate::frame::run_iframe(iframe, kind.flatten(), self)
1907 }
1908
1909 #[inline]
1911 fn release_trampoline_callee(&self, mut iframe: TrampolineIFrame) {
1912 unsafe {
1915 if let Some((base, size)) = iframe.as_mut().release_datastack_frame() {
1916 self.datastack_pop_frame(base, size);
1917 }
1918 }
1919 }
1920
1921 fn trampoline_resume_gen(&self, jen: PyObjectRef, value: PyObjectRef) -> GenEntered {
1932 use crate::coroutine::FlatEnter;
1933
1934 let mut jen = jen;
1935 let mut value = value;
1936 loop {
1937 let (state, sent) = match crate::coroutine::flat_resume_enter(jen, value, self) {
1938 Ok(FlatEnter::Entered { state, value }) => (state, value),
1939 Ok(FlatEnter::Exhausted) => {
1940 return GenEntered::Failed(Outcome::GenStop(None));
1941 }
1942 Err(exc) => return GenEntered::Failed(Outcome::Raise(exc)),
1943 };
1944 let mut iframe = unsafe { TrampolineIFrame::from_ptr(state.iframe_ptr()) };
1947 if let Some(sent) = sent {
1948 if let Some((delegate, cont)) =
1949 crate::frame::yield_from_delegate(iframe.as_mut(), self)
1950 && crate::frame::gen_collapse_allowed(self)
1951 {
1952 crate::frame::park_at_send(iframe.as_mut(), cont);
1953 self.trampoline_push(SuspendedFrame {
1954 iframe,
1955 kind: FrameKind::Gen(state),
1956 callee_owner: None,
1957 cont,
1958 });
1959 jen = delegate;
1960 value = sent;
1961 continue;
1962 }
1963 iframe.as_mut().localsplus.push_stack(sent);
1964 }
1965 let result =
1966 crate::frame::run_iframe(iframe.as_mut(), crate::frame::Flatten::GenResume, self);
1967 return GenEntered::Ran {
1968 iframe,
1969 state,
1970 result,
1971 };
1972 }
1973 }
1974
1975 fn trampoline_finish_gen(
1978 &self,
1979 state: crate::coroutine::FlatResume,
1980 result: PyResult<ExecutionResult>,
1981 ) -> Outcome {
1982 match crate::coroutine::flat_resume_exit(state, result, self) {
1983 Ok(PyIterReturn::Return(value)) => Outcome::Value(value),
1984 Ok(PyIterReturn::StopIteration(value)) => Outcome::GenStop(value),
1985 Err(exc) => Outcome::Raise(exc),
1986 }
1987 }
1988
1989 #[cold]
1993 #[inline(never)]
1994 fn run_trampoline(
1995 &self,
1996 iframe: &mut crate::frame::InterpreterFrame,
1997 kind: FrameKind,
1998 start: TrampolineStart,
1999 ) -> PyResult<ExecutionResult> {
2000 use crate::frame::ExecutionResult;
2001
2002 let iframe = TrampolineIFrame::from_mut(iframe);
2003
2004 enum Action {
2007 EnterCallee(TrampolineIFrame),
2009 ResumeGen {
2012 jen: PyObjectRef,
2013 value: PyObjectRef,
2014 },
2015 Deliver(Outcome),
2017 }
2018
2019 let base = self.trampoline_depth();
2024
2025 macro_rules! dispatch {
2029 ($iframe:expr, $kind:expr, $result:expr) => {{
2030 let iframe = $iframe;
2031 let kind = $kind;
2032 match $result {
2033 Ok(ExecutionResult::TailCall) => {
2034 let callee_owner = self.take_pending_tailcall_owner();
2035 let callee =
2038 unsafe { TrampolineIFrame::from_ptr(self.take_pending_tailcall()) };
2039 self.trampoline_push(SuspendedFrame {
2040 iframe,
2041 kind,
2042 callee_owner: Some(callee_owner),
2043 cont: crate::frame::GenCont::NONE,
2044 });
2045 Action::EnterCallee(callee)
2046 }
2047 Ok(ExecutionResult::GenResume) => {
2048 let PendingGenResume { jen, value, cont } = self.take_pending_gen_resume();
2049 self.trampoline_push(SuspendedFrame {
2052 iframe,
2053 kind,
2054 callee_owner: None,
2055 cont,
2056 });
2057 Action::ResumeGen { jen, value }
2058 }
2059 result => match kind {
2062 FrameKind::GenEntry => {
2063 debug_assert_eq!(self.trampoline_depth(), base);
2064 return result;
2065 }
2066 FrameKind::Entry(state) => {
2067 debug_assert_eq!(self.trampoline_depth(), base);
2068 self.exit_iframe(state);
2069 return match result {
2070 Ok(ExecutionResult::Yield(_)) => {
2071 panic!("Yield in non-generator frame")
2072 }
2073 result => result,
2074 };
2075 }
2076 FrameKind::Callee(state) => {
2077 self.exit_iframe(state);
2078 self.release_trampoline_callee(iframe);
2079 match result {
2080 Ok(ExecutionResult::Return(value)) => {
2081 Action::Deliver(Outcome::Value(value))
2082 }
2083 Ok(ExecutionResult::Yield(_)) => {
2084 panic!("Yield in non-generator frame")
2085 }
2086 Ok(_) => unreachable!("unfinished frame result"),
2087 Err(exc) => Action::Deliver(Outcome::Raise(exc)),
2088 }
2089 }
2090 FrameKind::Gen(state) => {
2091 Action::Deliver(self.trampoline_finish_gen(state, result))
2092 }
2093 },
2094 }
2095 }};
2096 }
2097
2098 let mut action = match start {
2099 TrampolineStart::Ran(result) => dispatch!(iframe, kind, result),
2100 TrampolineStart::Delegating {
2101 delegate,
2102 value,
2103 cont,
2104 } => {
2105 self.trampoline_push(SuspendedFrame {
2106 iframe,
2107 kind,
2108 callee_owner: None,
2109 cont,
2110 });
2111 Action::ResumeGen {
2112 jen: delegate,
2113 value,
2114 }
2115 }
2116 };
2117
2118 loop {
2119 action = match action {
2120 Action::EnterCallee(mut callee) => {
2121 match self.enter_iframe_unchecked(callee.as_mut()) {
2122 Ok(state) => {
2123 let result = crate::frame::run_iframe(
2124 callee.as_mut(),
2125 crate::frame::Flatten::CallAndGenResume,
2126 self,
2127 );
2128 dispatch!(callee, FrameKind::Callee(state), result)
2129 }
2130 Err(exc) => {
2131 self.release_trampoline_callee(callee);
2132 Action::Deliver(Outcome::Raise(exc))
2133 }
2134 }
2135 }
2136
2137 Action::ResumeGen { jen, value } => match self.trampoline_resume_gen(jen, value) {
2138 GenEntered::Ran {
2139 iframe,
2140 state,
2141 result,
2142 } => dispatch!(iframe, FrameKind::Gen(state), result),
2143 GenEntered::Failed(outcome) => Action::Deliver(outcome),
2144 },
2145
2146 Action::Deliver(outcome) => {
2147 let SuspendedFrame {
2151 mut iframe,
2152 kind,
2153 callee_owner,
2154 cont,
2155 } = self
2156 .trampoline_pop(base)
2157 .expect("trampoline outcome with no frame to deliver it to");
2158 drop(callee_owner);
2165 match outcome {
2166 Outcome::Value(value)
2169 if cont.is_some() && crate::frame::gen_collapse_allowed(self) =>
2170 {
2171 crate::frame::park_after_yield_from(iframe.as_mut(), cont.resumed_at);
2172 match kind {
2173 FrameKind::Gen(state) => {
2174 Action::Deliver(self.trampoline_finish_gen(
2175 state,
2176 Ok(ExecutionResult::Yield(value)),
2177 ))
2178 }
2179 kind => {
2183 debug_assert!(matches!(kind, FrameKind::GenEntry));
2184 debug_assert_eq!(self.trampoline_depth(), base);
2185 return Ok(ExecutionResult::Yield(value));
2186 }
2187 }
2188 }
2189 Outcome::Value(value) => {
2190 iframe.as_mut().localsplus.push_stack(value);
2191 let result = self.trampoline_run(iframe.as_mut(), &kind);
2192 dispatch!(iframe, kind, result)
2193 }
2194 Outcome::GenStop(value) => {
2195 debug_assert!(
2196 cont.is_some(),
2197 "a generator finished with no continuation to apply"
2198 );
2199 let result = match crate::frame::trampoline_gen_stop(
2200 iframe.as_mut(),
2201 value,
2202 cont,
2203 self,
2204 ) {
2205 Ok(()) => self.trampoline_run(iframe.as_mut(), &kind),
2206 Err(exc) => Err(exc),
2207 };
2208 dispatch!(iframe, kind, result)
2209 }
2210 Outcome::Raise(exc) => {
2211 let result = match crate::frame::trampoline_handle_exception(
2212 iframe.as_mut(),
2213 &exc,
2214 self,
2215 ) {
2216 Ok(None) => self.trampoline_run(iframe.as_mut(), &kind),
2218 Ok(Some(result)) => Ok(result),
2219 Err(exc) => Err(exc),
2220 };
2221 dispatch!(iframe, kind, result)
2222 }
2223 }
2224 }
2225 };
2226 }
2227 }
2228
2229 pub fn run_frame(&self, frame: FrameObjectRef) -> PyResult {
2230 debug_assert!(
2236 !frame.as_object().is_gc_tracked(),
2237 "datastack frame is GC-tracked before execution"
2238 );
2239 match self.with_frame(frame, |f| f.run(self))? {
2240 ExecutionResult::Return(value) => Ok(value),
2241 _ => panic!("Got unexpected result from function"),
2242 }
2243 }
2244
2245 fn with_simple_run(
2247 &self,
2248 path: &str,
2249 run: impl FnOnce(&Py<PyDict>) -> PyResult<()>,
2250 ) -> PyResult<()> {
2251 let sys_modules = self.sys_module.get_attr(identifier!(self, modules), self)?;
2252 let main_module = sys_modules.get_item(identifier!(self, __main__), self)?;
2253 let module_dict = main_module.dict().expect("main module must have __dict__");
2254
2255 let set_file_name = !module_dict.contains_key(identifier!(self, __file__), self);
2257 if set_file_name {
2258 module_dict.set_item(
2259 identifier!(self, __file__),
2260 self.ctx.new_str(path).into(),
2261 self,
2262 )?;
2263 module_dict.set_item(identifier!(self, __cached__), self.ctx.none(), self)?;
2264 }
2265
2266 let result = run(&module_dict);
2267
2268 self.flush_io();
2269
2270 if set_file_name {
2272 let _ = module_dict.del_item(identifier!(self, __file__), self);
2273 let _ = module_dict.del_item(identifier!(self, __cached__), self);
2274 }
2275
2276 result
2277 }
2278
2279 fn flush_io(&self) {
2283 if let Ok(stdout) = self.sys_module.get_attr("stdout", self) {
2284 let _ = self.call_method(&stdout, identifier!(self, flush).as_str(), ());
2285 }
2286 if let Ok(stderr) = self.sys_module.get_attr("stderr", self) {
2287 let _ = self.call_method(&stderr, identifier!(self, flush).as_str(), ());
2288 }
2289 }
2290
2291 pub fn finalize_modules(&self) {
2295 self.finalize_modules_delete_special();
2297
2298 let module_weakrefs = self.finalize_remove_modules();
2302
2303 self.finalize_clear_modules_dict();
2305
2306 self.state.gc.collect_force(2);
2310
2311 self.finalize_clear_module_dicts(&module_weakrefs);
2314
2315 self.state.gc.collect_force(2);
2317
2318 self.finalize_clear_sys_builtins_dict();
2320 }
2321
2322 fn finalize_modules_delete_special(&self) {
2324 let none = self.ctx.none();
2325 let sys_dict = self.sys_module.dict();
2326
2327 for attr in &[
2329 "path",
2330 "argv",
2331 "ps1",
2332 "ps2",
2333 "last_exc",
2334 "last_type",
2335 "last_value",
2336 "last_traceback",
2337 "path_importer_cache",
2338 "meta_path",
2339 "path_hooks",
2340 ] {
2341 let _ = sys_dict.set_item(*attr, none.clone(), self);
2342 }
2343
2344 for (std_name, dunder_name) in &[
2346 ("stdin", "__stdin__"),
2347 ("stdout", "__stdout__"),
2348 ("stderr", "__stderr__"),
2349 ] {
2350 let restored = sys_dict
2351 .get_item_opt(*dunder_name, self)
2352 .ok()
2353 .flatten()
2354 .unwrap_or_else(|| none.clone());
2355 let _ = sys_dict.set_item(*std_name, restored, self);
2356 }
2357
2358 let _ = self.builtins.dict().set_item("_", none, self);
2360 }
2361
2362 fn finalize_remove_modules(&self) -> Vec<(String, PyRef<PyWeak>)> {
2366 let mut module_weakrefs = Vec::new();
2367
2368 let Ok(modules) = self.sys_module.get_attr(identifier!(self, modules), self) else {
2369 return module_weakrefs;
2370 };
2371 let Some(modules_dict) = modules.downcast_ref::<PyDict>() else {
2372 return module_weakrefs;
2373 };
2374
2375 let none = self.ctx.none();
2376 let items: Vec<_> = modules_dict.into_iter().collect();
2377
2378 for (key, value) in items {
2379 let name = key
2380 .downcast_ref::<PyUtf8Str>()
2381 .map(|s| s.as_str().to_owned())
2382 .unwrap_or_default();
2383
2384 if value.downcast_ref::<PyModule>().is_some()
2386 && let Ok(weak) = value.downgrade(None, self)
2387 {
2388 module_weakrefs.push((name, weak));
2389 }
2390
2391 let _ = modules_dict.set_item(&*key, none.clone(), self);
2393 }
2394
2395 module_weakrefs
2396 }
2397
2398 fn finalize_clear_modules_dict(&self) {
2400 if let Ok(modules) = self.sys_module.get_attr(identifier!(self, modules), self)
2401 && let Some(modules_dict) = modules.downcast_ref::<PyDict>()
2402 {
2403 modules_dict.clear();
2404 }
2405 }
2406
2407 fn finalize_clear_module_dicts(&self, module_weakrefs: &[(String, PyRef<PyWeak>)]) {
2410 let builtins_dict = self.builtins.dict();
2411 let sys_dict = self.sys_module.dict();
2412
2413 for (_name, weakref) in module_weakrefs.iter().rev() {
2414 let Some(module_obj) = weakref.upgrade() else {
2415 continue;
2416 };
2417 let Some(module) = module_obj.downcast_ref::<PyModule>() else {
2418 continue;
2419 };
2420
2421 let dict = module.dict();
2422 if dict.is(&builtins_dict) || dict.is(&sys_dict) {
2424 continue;
2425 }
2426
2427 Self::module_clear_dict(&dict, self);
2428 }
2429 }
2430
2431 pub(crate) fn module_clear_dict(dict: &Py<PyDict>, vm: &Self) {
2435 let none = vm.ctx.none();
2436
2437 for (key, value) in dict.into_iter().collect::<Vec<_>>() {
2439 if vm.is_none(&value) {
2440 continue;
2441 }
2442 if let Some(key_str) = key.downcast_ref::<PyStr>() {
2443 let name = key_str.as_wtf8();
2444 if name.starts_with("_") && name != "__builtins__" {
2445 let _ = dict.set_item(key_str, none.clone(), vm);
2446 }
2447 }
2448 }
2449
2450 for (key, value) in dict.into_iter().collect::<Vec<_>>() {
2452 if vm.is_none(&value) {
2453 continue;
2454 }
2455 if let Some(key_str) = key.downcast_ref::<PyStr>()
2456 && key_str.as_bytes() != b"__builtins__"
2457 {
2458 let _ = dict.set_item(key_str.as_wtf8(), none.clone(), vm);
2459 }
2460 }
2461 }
2462
2463 fn finalize_clear_sys_builtins_dict(&self) {
2465 Self::module_clear_dict(&self.sys_module.dict(), self);
2466 Self::module_clear_dict(&self.builtins.dict(), self);
2467 }
2468
2469 pub fn current_recursion_depth(&self) -> usize {
2470 self.recursion_depth.get()
2471 }
2472
2473 #[cfg_attr(any(miri, target_env = "musl"), allow(dead_code))]
2477 pub(crate) const STACK_MARGIN_BYTES: usize =
2480 (if cfg!(debug_assertions) { 16384 } else { 4096 }) * core::mem::size_of::<usize>();
2481
2482 #[cfg(any(miri, target_env = "musl"))]
2487 const NATIVE_RECURSION_LIMIT_UNMEASURED: usize =
2488 if cfg!(debug_assertions) { 500 } else { 1500 };
2489
2490 #[cfg(all(not(miri), not(target_env = "musl"), windows))]
2493 fn get_stack_bounds() -> (usize, usize) {
2494 crate::host_env::windows::current_thread_stack_bounds()
2495 }
2496
2497 #[cfg(all(not(miri), not(target_env = "musl"), not(windows)))]
2500 fn get_stack_bounds() -> (usize, usize) {
2501 #[cfg(any(target_os = "linux", target_os = "android"))]
2503 {
2504 use libc::{
2505 pthread_attr_destroy, pthread_attr_getstack, pthread_attr_t, pthread_getattr_np,
2506 pthread_self,
2507 };
2508 let mut attr: pthread_attr_t = unsafe { core::mem::zeroed() };
2509 unsafe {
2510 if pthread_getattr_np(pthread_self(), &mut attr) == 0 {
2511 let mut stack_addr: *mut libc::c_void = core::ptr::null_mut();
2512 let mut stack_size: libc::size_t = 0;
2513 if pthread_attr_getstack(&attr, &mut stack_addr, &mut stack_size) == 0 {
2514 pthread_attr_destroy(&mut attr);
2515 let base = stack_addr as usize;
2516 let top = base + stack_size;
2517 return (base, top);
2518 }
2519 pthread_attr_destroy(&mut attr);
2520 }
2521 }
2522 }
2523
2524 #[cfg(target_os = "macos")]
2525 {
2526 use libc::{pthread_get_stackaddr_np, pthread_get_stacksize_np, pthread_self};
2527 unsafe {
2528 let thread = pthread_self();
2529 let stack_top = pthread_get_stackaddr_np(thread) as usize;
2530 let stack_size = pthread_get_stacksize_np(thread);
2531 let stack_base = stack_top - stack_size;
2532 return (stack_base, stack_top);
2533 }
2534 }
2535
2536 #[allow(unreachable_code)]
2538 {
2539 let current_sp = psm::stack_pointer() as usize;
2540 let estimated_size = 8 * 1024 * 1024;
2542 let base = current_sp.saturating_sub(estimated_size);
2543 let top = current_sp + 1024 * 1024; (base, top)
2545 }
2546 }
2547
2548 #[cfg(all(not(miri), not(target_env = "musl")))]
2554 fn calculate_c_stack_soft_limit() -> usize {
2555 let (base, top) = Self::get_stack_bounds();
2556 let stack_size = top.saturating_sub(base);
2557 let margin = (Self::STACK_MARGIN_BYTES * 2).min(stack_size / 2);
2558 base + margin
2559 }
2560
2561 #[cfg(any(miri, target_env = "musl"))]
2565 fn calculate_c_stack_soft_limit() -> usize {
2566 0
2567 }
2568
2569 #[cfg(all(not(miri), not(target_env = "musl")))]
2573 #[inline(always)]
2574 pub(crate) fn check_c_stack_overflow(&self) -> bool {
2575 let current_sp = psm::stack_pointer() as usize;
2576 let soft_limit = self.c_stack_soft_limit.get();
2577 current_sp < soft_limit
2578 }
2579
2580 #[cfg(any(miri, target_env = "musl"))]
2583 #[inline(always)]
2584 pub(crate) fn check_c_stack_overflow(&self) -> bool {
2585 false
2586 }
2587
2588 pub fn with_recursion<R, F: FnOnce() -> PyResult<R>>(&self, _where: &str, f: F) -> PyResult<R> {
2596 #[cfg(any(miri, target_env = "musl"))]
2600 let counted_too_deep =
2601 self.native_recursion_depth.get() >= Self::NATIVE_RECURSION_LIMIT_UNMEASURED;
2602 #[cfg(not(any(miri, target_env = "musl")))]
2603 let counted_too_deep = false;
2604
2605 if counted_too_deep || self.check_c_stack_overflow() {
2606 return Err(
2607 self.new_recursion_error(format!("maximum recursion depth exceeded {_where}"))
2608 );
2609 }
2610
2611 #[cfg(any(miri, target_env = "musl"))]
2612 let _native_depth_guard = {
2613 self.native_recursion_depth.update(|d| d + 1);
2614 scopeguard::guard((), |()| {
2615 self.native_recursion_depth.update(|d| d.saturating_sub(1))
2616 })
2617 };
2618
2619 f()
2620 }
2621
2622 pub fn with_frame<R, F: FnOnce(FrameObjectRef) -> PyResult<R>>(
2623 &self,
2624 frame: FrameObjectRef,
2625 f: F,
2626 ) -> PyResult<R> {
2627 self.check_recursive_call("")?;
2628
2629 if self.check_c_stack_overflow() {
2635 return Err(self.new_recursion_error(String::new()));
2636 }
2637
2638 self.recursion_depth.update(|d| d + 1);
2639 let _depth_guard = scopeguard::guard((), |()| {
2642 self.recursion_depth.update(|d| d.saturating_sub(1))
2643 });
2644
2645 #[cfg(all(not(unix), feature = "threading"))]
2646 crate::vm::thread::push_thread_frame(FramePtr(NonNull::from(&*frame)));
2647 let iframe = frame.iframe() as *const crate::frame::InterpreterFrame;
2648 let old_chain = crate::vm::thread::set_current_frame(iframe);
2649 {
2650 #[allow(unused_imports)]
2651 use rustpython_common::atomic::Radium;
2652 frame
2653 .iframe()
2654 .previous
2655 .store(old_chain as usize, core::sync::atomic::Ordering::Relaxed);
2656 }
2657 let save_exc = frame.iframe().code().has_exc_handling;
2658 let saved_exc = if save_exc {
2659 self.current_exception()
2660 } else {
2661 None
2662 };
2663 let old_owner = frame.iframe().owner.swap(
2664 crate::frame::FrameOwner::Thread as i8,
2665 core::sync::atomic::Ordering::AcqRel,
2666 );
2667
2668 let result = self.dispatch_traced_frame(&frame, |frame| f(frame.to_owned()));
2669
2670 if !old_chain.is_null() {
2673 let strong = frame.as_object().strong_count();
2674 if strong > 1 {
2681 let mut guard = frame.iframe().cold().retained_back.lock();
2682 if guard.is_none() {
2683 let prev_iframe = unsafe { &*old_chain };
2684 if let Some(fo) = prev_iframe.frame_obj() {
2685 *guard = Some(fo.to_owned());
2686 }
2687 }
2688 }
2689 }
2690
2691 frame
2692 .iframe()
2693 .owner
2694 .store(old_owner, core::sync::atomic::Ordering::Release);
2695 if save_exc {
2696 self.restore_exception(saved_exc);
2697 }
2698 {
2701 #[allow(unused_imports)]
2702 use rustpython_common::atomic::Radium;
2703 frame
2704 .iframe()
2705 .previous
2706 .store(0, core::sync::atomic::Ordering::Relaxed);
2707 }
2708 let _ = crate::vm::thread::set_current_frame(old_chain);
2709 #[cfg(all(not(unix), feature = "threading"))]
2710 crate::vm::thread::pop_thread_frame();
2711 scopeguard::ScopeGuard::into_inner(_depth_guard);
2713 self.recursion_depth.update(|d| d - 1);
2714
2715 result
2716 }
2717
2718 #[inline]
2722 pub(crate) fn enter_iframe(
2723 &self,
2724 iframe: &mut crate::frame::InterpreterFrame,
2725 ) -> PyResult<IframeEntryState> {
2726 self.check_recursive_call("")?;
2727
2728 self.enter_iframe_unchecked(iframe)
2730 }
2731
2732 #[inline(always)]
2737 pub(crate) fn enter_iframe_unchecked(
2738 &self,
2739 iframe: &mut crate::frame::InterpreterFrame,
2740 ) -> PyResult<IframeEntryState> {
2741 if self.check_c_stack_overflow() {
2742 return Err(self.new_recursion_error(String::new()));
2743 }
2744
2745 self.recursion_depth.update(|d| d + 1);
2746
2747 let iframe_ptr = iframe as *const crate::frame::InterpreterFrame;
2748 let old_chain = crate::vm::thread::set_current_frame(iframe_ptr);
2749 {
2750 #[allow(unused_imports)]
2751 use rustpython_common::atomic::Radium;
2752 iframe
2753 .previous
2754 .store(old_chain as usize, core::sync::atomic::Ordering::Relaxed);
2755 }
2756 let save_exc = iframe.code().has_exc_handling;
2757 let saved_exc = if save_exc {
2758 self.current_exception()
2759 } else {
2760 None
2761 };
2762
2763 Ok(IframeEntryState {
2764 iframe_ptr,
2765 old_chain,
2766 saved_exc,
2767 save_exc,
2768 })
2769 }
2770
2771 pub(crate) fn exit_iframe(&self, state: IframeEntryState) {
2774 let IframeEntryState {
2775 iframe_ptr,
2776 old_chain,
2777 saved_exc,
2778 save_exc,
2779 } = state;
2780
2781 {
2786 let mat_ptr = unsafe {
2787 let field_ptr = core::ptr::addr_of!((*iframe_ptr).materialized);
2788 core::ptr::read_volatile(field_ptr as *const usize)
2789 };
2790 if mat_ptr != 0 {
2791 let fo = unsafe { &*(mat_ptr as *const crate::Py<crate::frame::FrameObject>) };
2792 unsafe {
2793 let live_iframe = &*iframe_ptr;
2794 fo.iframe_mut()
2795 .localsplus
2796 .sync_fastlocals_from(&live_iframe.localsplus);
2797 fo.iframe_mut().prev_line.set(live_iframe.prev_line.get());
2798 #[allow(unused_imports)]
2799 use rustpython_common::atomic::Radium;
2800 fo.iframe_mut().lasti.store(
2801 live_iframe
2802 .lasti
2803 .load(core::sync::atomic::Ordering::Relaxed),
2804 core::sync::atomic::Ordering::Relaxed,
2805 );
2806 }
2807 fo.iframe().detach();
2810 if !old_chain.is_null() {
2811 let prev_iframe = unsafe { &*old_chain };
2812 let back_fo = prev_iframe.materialize_chain(self);
2813 *fo.iframe().cold().retained_back.lock() = Some(back_fo);
2814 }
2815 fo.iframe().owner.store(
2816 crate::frame::FrameOwner::FrameObject as i8,
2817 core::sync::atomic::Ordering::Release,
2818 );
2819 }
2820 }
2821
2822 if save_exc {
2823 self.restore_exception(saved_exc);
2824 }
2825 {
2828 #[allow(unused_imports)]
2829 use rustpython_common::atomic::Radium;
2830 unsafe {
2831 (*iframe_ptr)
2832 .previous
2833 .store(0, core::sync::atomic::Ordering::Relaxed);
2834 }
2835 }
2836 let _ = crate::vm::thread::set_current_frame(old_chain);
2837 self.recursion_depth.update(|d| d - 1);
2838
2839 {
2842 let mat_ptr = unsafe {
2843 let field_ptr = core::ptr::addr_of!((*iframe_ptr).materialized);
2844 core::ptr::read_volatile(field_ptr as *const usize)
2845 };
2846 if mat_ptr != 0 {
2847 let fo = unsafe { &*(mat_ptr as *const crate::Py<crate::frame::FrameObject>) };
2848 unsafe {
2849 crate::gc_state::gc_state().track_object(
2850 core::ptr::NonNull::from(fo.as_object()),
2851 crate::gc_state::current_owner(),
2852 );
2853 let live_iframe = &*iframe_ptr;
2854 live_iframe.cold().temporary_refs.lock().clear();
2855 }
2856 }
2857 }
2858 }
2859
2860 #[inline(always)]
2876 pub(crate) fn gen_frame_link(
2877 &self,
2878 frame: &Py<FrameObject>,
2879 exc: Option<PyBaseExceptionRef>,
2880 ) -> PyResult<GenFrameLink> {
2881 self.check_recursive_call("")?;
2882 if self.check_c_stack_overflow() {
2883 return Err(self.new_recursion_error(String::new()));
2884 }
2885 self.recursion_depth.update(|d| d + 1);
2886
2887 #[cfg(all(not(unix), feature = "threading"))]
2890 crate::vm::thread::push_thread_frame(FramePtr(NonNull::from(frame)));
2891 let iframe = frame.iframe() as *const crate::frame::InterpreterFrame;
2892 let old_chain = crate::vm::thread::set_current_frame(iframe);
2893 {
2894 #[allow(unused_imports)]
2895 use rustpython_common::atomic::Radium;
2896 frame
2897 .iframe()
2898 .previous
2899 .store(old_chain as usize, core::sync::atomic::Ordering::Relaxed);
2900 }
2901 self.push_exception(exc);
2903 let old_owner = frame.iframe().owner.swap(
2904 crate::frame::FrameOwner::Thread as i8,
2905 core::sync::atomic::Ordering::AcqRel,
2906 );
2907 Ok(GenFrameLink {
2908 old_chain,
2909 old_owner,
2910 })
2911 }
2912
2913 #[inline(always)]
2916 pub(crate) fn gen_frame_unlink(&self, frame: &Py<FrameObject>, link: GenFrameLink) {
2917 frame
2918 .iframe()
2919 .owner
2920 .store(link.old_owner, core::sync::atomic::Ordering::Release);
2921 self.pop_exception();
2922 {
2925 #[allow(unused_imports)]
2926 use rustpython_common::atomic::Radium;
2927 frame
2928 .iframe()
2929 .previous
2930 .store(0, core::sync::atomic::Ordering::Relaxed);
2931 }
2932 let _ = crate::vm::thread::set_current_frame(link.old_chain);
2933 #[cfg(all(not(unix), feature = "threading"))]
2934 crate::vm::thread::pop_thread_frame();
2935 self.recursion_depth.update(|d| d - 1);
2936 }
2937
2938 pub fn resume_gen_frame<R, F: FnOnce(&Py<FrameObject>) -> PyResult<R>>(
2942 &self,
2943 frame: &FrameObjectRef,
2944 exc: Option<PyBaseExceptionRef>,
2945 f: F,
2946 ) -> PyResult<R> {
2947 let link = self.gen_frame_link(frame, exc)?;
2948 let _depth_guard = scopeguard::guard((), |()| {
2955 self.recursion_depth.update(|d| d.saturating_sub(1))
2956 });
2957
2958 let result = self.dispatch_traced_frame(frame, |frame| f(frame));
2959
2960 scopeguard::ScopeGuard::into_inner(_depth_guard);
2964 self.gen_frame_unlink(frame, link);
2965
2966 result
2967 }
2968
2969 fn dispatch_traced_frame<R, F: FnOnce(&Py<FrameObject>) -> PyResult<R>>(
2979 &self,
2980 frame: &Py<FrameObject>,
2981 f: F,
2982 ) -> PyResult<R> {
2983 use crate::protocol::TraceEvent;
2984
2985 let result = f(frame);
2990
2991 if result.is_err()
2995 && self.use_tracing.get()
2996 && (!self.is_none(&self.profile_func.borrow())
2997 || frame
2998 .iframe()
2999 .cold_opt()
3000 .is_some_and(|c| c.trace.lock().is_some()))
3001 {
3002 let ret_result = self.trace_event_what(
3003 TraceEvent::Return,
3004 crate::stdlib::sys::monitoring::MonitoringEvent::PyUnwind,
3005 None,
3006 );
3007 ret_result?;
3010 }
3011
3012 result
3013 }
3014
3015 #[cfg(feature = "rustpython-codegen")]
3018 pub fn compile_opts(&self) -> crate::compiler::CompileOpts {
3019 crate::compiler::CompileOpts {
3020 optimize: self.state.config.settings.optimize.min(2),
3021 debug_ranges: self.state.config.settings.code_debug_ranges,
3022 int_max_str_digits: self.state.int_max_str_digits.load(),
3023 allow_top_level_await: false,
3024 future_features: crate::bytecode::CodeFlags::empty(),
3025 dont_imply_dedent: false,
3026 recursion_limit: self.recursion_limit.get(),
3027 }
3028 }
3029
3030 pub fn audit<A: crate::function::IntoFuncArgs>(
3033 &self,
3034 event: &str,
3035 args: impl FnOnce() -> A,
3036 ) -> PyResult<()> {
3037 if self.state.audit_hooks.lock().is_empty() {
3038 return Ok(());
3039 }
3040 let event = self.ctx.new_str(event);
3041 let args = self.ctx.new_tuple(args().into_args(self).args);
3042 crate::stdlib::sys::sys::run_audit_hooks(&event, args.as_object(), self)
3043 }
3044
3045 #[inline]
3046 pub(crate) fn enter_tracing(&self) {
3047 self.tracing_depth.set(self.tracing_depth.get() + 1);
3048 }
3049
3050 #[inline]
3051 pub(crate) fn leave_tracing(&self) {
3052 let depth = self.tracing_depth.get();
3053 debug_assert!(depth > 0);
3054 self.tracing_depth.set(depth.saturating_sub(1));
3055 }
3056
3057 #[inline]
3058 pub(crate) fn tracing_is_suppressed(&self) -> bool {
3059 self.tracing_depth.get() != 0
3060 }
3061
3062 fn check_recursive_call(&self, _where: &str) -> PyResult<()> {
3064 if self.recursion_depth.get() >= self.recursion_limit.get() {
3065 Err(self.new_recursion_error(format!("maximum recursion depth exceeded {_where}")))
3066 } else {
3067 Ok(())
3068 }
3069 }
3070
3071 pub fn current_frame(&self) -> Option<FrameObjectRef> {
3072 crate::frame::current_thread_frame_materialize(self)
3073 }
3074
3075 pub fn current_locals(&self) -> PyResult<ArgMapping> {
3076 crate::frame::current_thread_frame_materialize(self)
3078 .expect("called current_locals but no frames on the stack")
3079 .locals(self)
3080 }
3081
3082 pub fn current_globals(&self) -> PyDictRef {
3083 let ptr = crate::vm::thread::get_current_frame();
3084 if !ptr.is_null() {
3085 return unsafe { (*ptr).globals().to_owned() };
3086 }
3087 crate::frame::current_globals().expect("called current_globals but no frames on the stack")
3088 }
3089
3090 pub fn try_class(&self, module: &'static str, class: &'static str) -> PyResult<PyTypeRef> {
3091 let class = self
3092 .import(module, 0)?
3093 .get_attr(class, self)?
3094 .downcast()
3095 .expect("not a class");
3096 Ok(class)
3097 }
3098
3099 pub fn class(&self, module: &'static str, class: &'static str) -> PyTypeRef {
3100 let module = self
3101 .import(module, 0)
3102 .unwrap_or_else(|_| panic!("unable to import {module}"));
3103
3104 let class = module
3105 .get_attr(class, self)
3106 .unwrap_or_else(|_| panic!("module {module:?} has no class {class}"));
3107 class.downcast().expect("not a class")
3108 }
3109
3110 #[inline]
3116 pub fn import<'a>(&self, module_name: impl AsPyStr<'a>, level: usize) -> PyResult {
3117 let module_name = module_name.as_pystr(&self.ctx);
3118 self.import_inner(module_name, self.ctx.none(), level)
3119 }
3120
3121 #[inline]
3124 pub fn import_from<'a>(
3125 &self,
3126 module_name: impl AsPyStr<'a>,
3127 from_list: impl Into<PyObjectRef>,
3128 level: usize,
3129 ) -> PyResult {
3130 let module_name = module_name.as_pystr(&self.ctx);
3131 self.import_inner(module_name, from_list.into(), level)
3132 }
3133
3134 pub fn eval_get_builtin(&self, name: &'static PyStrInterned) -> PyResult {
3137 let builtins =
3138 crate::frame::current_builtins().unwrap_or_else(|| self.builtins.dict().into());
3139 if let Some(dict) = builtins.downcast_ref::<PyDict>() {
3140 match dict.get_item_opt(name, self)? {
3141 Some(value) => Ok(value),
3142 None => Err(self.new_attribute_error(name.to_string())),
3143 }
3144 } else {
3145 match builtins.get_item(name, self) {
3146 Ok(value) => Ok(value),
3147 Err(e) if e.fast_isinstance(self.ctx.exceptions.key_error) => {
3148 Err(self.new_attribute_error(name.to_string()))
3149 }
3150 Err(e) => Err(e),
3151 }
3152 }
3153 }
3154
3155 fn import_inner(&self, module: &Py<PyStr>, from_list: PyObjectRef, level: usize) -> PyResult {
3156 let builtins =
3157 crate::frame::current_builtins().unwrap_or_else(|| self.builtins.dict().into());
3158 let fromlist_empty = self.is_none(&from_list)
3163 || from_list
3164 .downcast_ref::<PyTuple>()
3165 .is_some_and(|tuple| tuple.as_slice().is_empty());
3166 if level == 0
3167 && fromlist_empty
3168 && builtins.is(self.builtins.dict().as_object())
3169 && let Some(cached) = self.try_import_cached(module)?
3170 {
3171 return Ok(cached);
3172 }
3173
3174 let import_func = if let Some(dict) = builtins.downcast_ref::<PyDict>() {
3175 match dict.get_item_opt(identifier!(self, __import__), self)? {
3176 Some(func) => func,
3177 None => {
3178 return Err(self.new_import_error("__import__ not found", module.to_owned()));
3179 }
3180 }
3181 } else {
3182 match builtins.get_item(identifier!(self, __import__), self) {
3183 Ok(func) => func,
3184 Err(e) if e.fast_isinstance(self.ctx.exceptions.key_error) => {
3185 return Err(self.new_import_error("__import__ not found", module.to_owned()));
3186 }
3187 Err(e) => return Err(e),
3188 }
3189 };
3190
3191 let (locals, globals) = if let Some(globals) = crate::frame::current_globals() {
3192 let locals_mapping = self.current_frame().map_or_else(
3195 || ArgMapping::from_dict_exact(globals.clone()),
3196 |f| f.iframe().locals.clone_mapping(self),
3197 );
3198 (Some(locals_mapping), Some(globals))
3199 } else {
3200 (None, None)
3201 };
3202 import_func
3203 .call((module.to_owned(), globals, locals, from_list, level), self)
3204 .inspect_err(|exc| import::remove_importlib_frames(self, exc))
3205 }
3206
3207 fn try_import_cached(&self, module: &Py<PyStr>) -> PyResult<Option<PyObjectRef>> {
3218 let current_import = self
3219 .builtins
3220 .get_attr(identifier!(self, __import__), self)
3221 .map_err(|_| self.new_import_error("__import__ not found", module.to_owned()))?;
3222 if !current_import.is(&self.import_func) {
3223 return Ok(None);
3227 }
3228
3229 let Some(name_str) = module.to_str() else {
3232 return Ok(None);
3233 };
3234 let sys_modules = self.sys_module.get_attr("modules", self)?;
3235 let Ok(found) = sys_modules.get_item(name_str, self) else {
3236 return Ok(None);
3237 };
3238 if self.is_none(&found) || import::is_module_initializing(&found, self)? {
3239 return Ok(None);
3240 }
3241
3242 let Some(dot) = name_str.find('.') else {
3243 return Ok(Some(found));
3244 };
3245 let top_name = &name_str[..dot];
3249 match sys_modules.get_item(top_name, self) {
3250 Ok(top) if !self.is_none(&top) => Ok(Some(top)),
3251 _ => Ok(None),
3252 }
3253 }
3254
3255 pub fn extract_elements_with<T, F>(&self, value: &PyObject, func: F) -> PyResult<Vec<T>>
3256 where
3257 F: Fn(PyObjectRef) -> PyResult<T>,
3258 {
3259 self.extract_elements_inner(value, LengthHint::Unasked, func)
3260 }
3261
3262 pub fn extract_elements_sized<T, F>(
3267 &self,
3268 value: &PyObject,
3269 held: &dyn Fn() -> usize,
3270 func: F,
3271 ) -> PyResult<Vec<T>>
3272 where
3273 F: Fn(PyObjectRef) -> PyResult<T>,
3274 {
3275 self.extract_elements_inner(value, LengthHint::Iterable(held), func)
3276 }
3277
3278 fn extract_elements_inner<T, F>(
3279 &self,
3280 value: &PyObject,
3281 hint: LengthHint<'_>,
3282 func: F,
3283 ) -> PyResult<Vec<T>>
3284 where
3285 F: Fn(PyObjectRef) -> PyResult<T>,
3286 {
3287 fn map_known_len<T, R>(
3292 items: impl ExactSizeIterator<Item = T>,
3293 func: impl Fn(T) -> PyResult<R>,
3294 ) -> PyResult<Vec<R>> {
3295 let mut results = Vec::with_capacity(items.len());
3296 for item in items {
3297 results.push(func(item)?);
3298 }
3299 Ok(results)
3300 }
3301
3302 let cls = value.class();
3306 let slice = if cls.is(self.ctx.types.tuple_type) {
3307 value.downcast_ref::<PyTuple>().unwrap().as_slice()
3308 } else if cls.is(self.ctx.types.list_type) {
3309 let list = value.downcast_ref::<PyList>().unwrap();
3315 let mut results = Vec::with_capacity(list.borrow_vec().len());
3316 let mut i = 0;
3317 loop {
3318 let elem = {
3319 let elements = list.borrow_vec();
3320 let Some(elem) = elements.get(i) else {
3321 break;
3322 };
3323 elem.clone()
3324 };
3326 results.push(func(elem)?);
3327 i += 1;
3328 }
3329 return Ok(results);
3330 } else if cls.is(self.ctx.types.set_type) {
3331 let keys = value.downcast_ref::<PySet>().unwrap().elements();
3332 return map_known_len(keys.into_iter(), func);
3333 } else if cls.is(self.ctx.types.frozenset_type) {
3334 let keys = value.downcast_ref::<PyFrozenSet>().unwrap().elements();
3335 return map_known_len(keys.into_iter(), func);
3336 } else if cls.is(self.ctx.types.dict_type) {
3337 let keys = value.downcast_ref::<PyDict>().unwrap().keys_vec();
3338 return map_known_len(keys.into_iter(), func);
3339 } else if cls.is(self.ctx.types.dict_keys_type) {
3340 let keys = value.downcast_ref::<PyDictKeys>().unwrap().dict.keys_vec();
3341 return map_known_len(keys.into_iter(), func);
3342 } else if cls.is(self.ctx.types.dict_values_type) {
3343 let values = value
3344 .downcast_ref::<PyDictValues>()
3345 .unwrap()
3346 .dict
3347 .values_vec();
3348 return map_known_len(values.into_iter(), func);
3349 } else if cls.is(self.ctx.types.dict_items_type) {
3350 let items = value
3351 .downcast_ref::<PyDictItems>()
3352 .unwrap()
3353 .dict
3354 .items_vec();
3355 return map_known_len(items.into_iter(), |(k, v)| {
3356 func(self.ctx.new_tuple(vec![k, v]).into())
3357 });
3358 } else {
3359 return self.map_py_iter(value, hint, func);
3360 };
3361 map_known_len(slice.iter(), |obj| func(obj.clone()))
3362 }
3363
3364 pub fn map_iterable_object_sized<F, R>(
3367 &self,
3368 obj: &PyObject,
3369 f: F,
3370 ) -> PyResult<PyResult<Vec<R>>>
3371 where
3372 F: FnMut(PyObjectRef) -> PyResult<R>,
3373 {
3374 self.map_iterable_object_inner(obj, LengthHint::Iterable(&|| 0), f)
3375 }
3376
3377 pub fn map_iterable_object<F, R>(&self, obj: &PyObject, f: F) -> PyResult<PyResult<Vec<R>>>
3378 where
3379 F: FnMut(PyObjectRef) -> PyResult<R>,
3380 {
3381 self.map_iterable_object_inner(obj, LengthHint::Unasked, f)
3382 }
3383
3384 fn map_iterable_object_inner<F, R>(
3385 &self,
3386 obj: &PyObject,
3387 hint: LengthHint<'_>,
3388 mut f: F,
3389 ) -> PyResult<PyResult<Vec<R>>>
3390 where
3391 F: FnMut(PyObjectRef) -> PyResult<R>,
3392 {
3393 match_class!(match obj {
3394 ref l @ PyList => {
3395 let mut i: usize = 0;
3396 let mut results = Vec::with_capacity(l.borrow_vec().len());
3397 loop {
3398 let elem = {
3399 let elements = &*l.borrow_vec();
3400 if i >= elements.len() {
3401 results.shrink_to_fit();
3402 return Ok(Ok(results));
3403 }
3404 elements[i].clone()
3405
3406 };
3408 match f(elem) {
3409 Ok(result) => results.push(result),
3410 Err(err) => return Ok(Err(err)),
3411 }
3412 i += 1;
3413 }
3414 }
3415 ref t @ PyTuple => Ok(t.as_slice().iter().cloned().map(f).collect()),
3416 obj => {
3418 Ok(self.map_py_iter(obj, hint, f))
3419 }
3420 })
3421 }
3422
3423 fn map_py_iter<F, R>(
3424 &self,
3425 value: &PyObject,
3426 hint: LengthHint<'_>,
3427 mut f: F,
3428 ) -> PyResult<Vec<R>>
3429 where
3430 F: FnMut(PyObjectRef) -> PyResult<R>,
3431 {
3432 let iter = value.to_owned().get_iter(self)?;
3433
3434 let mut results: Vec<R> = Vec::new();
3453 let mut cap = None;
3454 if let LengthHint::Iterable(held) = hint {
3455 cap = self.length_hint_opt(value.to_owned())?;
3456 if let Some(cap) = cap
3457 && held() <= (isize::MAX as usize) - cap
3458 {
3459 results
3460 .try_reserve_exact(cap)
3461 .map_err(|_| self.new_memory_error(""))?;
3462 }
3463 }
3464 for element in PyIterIter::new(self, iter.as_ref(), cap) {
3465 results.push(f(element?)?);
3466 }
3467 results.shrink_to_fit();
3468 Ok(results)
3469 }
3470
3471 pub fn get_attribute_opt<'a>(
3472 &self,
3473 obj: &PyObject,
3474 attr_name: impl AsPyStr<'a>,
3475 ) -> PyResult<Option<PyObjectRef>> {
3476 let attr_name = attr_name.as_pystr(&self.ctx);
3477 let getattro = obj.class().slots().getattro.load().unwrap();
3478 let result = if fn_addr(getattro) == fn_addr(PyBaseObject::getattro as GetattroFunc) {
3479 obj.generic_getattr_opt(attr_name, None, self)
3480 } else {
3481 obj.get_attr_inner(attr_name, self).map(Some)
3482 };
3483 match result {
3484 Ok(attr) => Ok(attr),
3485 Err(e) if e.fast_isinstance(self.ctx.exceptions.attribute_error) => Ok(None),
3486 Err(e) => Err(e),
3487 }
3488 }
3489
3490 pub fn set_attribute_error_context(
3491 &self,
3492 exc: &Py<PyBaseException>,
3493 obj: PyObjectRef,
3494 name: PyStrRef,
3495 ) {
3496 if exc.class().is(self.ctx.exceptions.attribute_error) {
3497 let exc = exc.as_object();
3498 let already_set = exc.get_attr("name", self).is_ok_and(|v| !self.is_none(&v));
3500 if already_set {
3501 return;
3502 }
3503 exc.set_attr("name", name, self).unwrap();
3504 exc.set_attr("obj", obj, self).unwrap();
3505 }
3506 }
3507
3508 pub fn get_method_or_type_error<F>(
3511 &self,
3512 obj: PyObjectRef,
3513 method_name: &'static PyStrInterned,
3514 err_msg: F,
3515 ) -> PyResult
3516 where
3517 F: FnOnce() -> String,
3518 {
3519 let method = obj
3520 .class()
3521 .get_attr(method_name)
3522 .ok_or_else(|| self.new_type_error(err_msg()))?;
3523 self.call_if_get_descriptor(&method, obj)
3524 }
3525
3526 pub(crate) fn get_method(
3528 &self,
3529 obj: PyObjectRef,
3530 method_name: &'static PyStrInterned,
3531 ) -> Option<PyResult> {
3532 let method = obj.get_class_attr(method_name)?;
3533 Some(self.call_if_get_descriptor(&method, obj))
3534 }
3535
3536 pub(crate) fn get_str_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
3537 let method_name = self.ctx.interned_str(method_name)?;
3538 self.get_method(obj, method_name)
3539 }
3540
3541 #[inline]
3550 pub(crate) fn eval_breaker_tripped(&self) -> bool {
3551 #[cfg(feature = "threading")]
3552 if thread::stop_requested_for_current_thread() || self.state.gc.collection_ready() {
3553 return true;
3554 }
3555 #[cfg(not(target_arch = "wasm32"))]
3556 {
3557 crate::signal::eval_breaker_pending()
3558 }
3559 #[cfg(target_arch = "wasm32")]
3560 {
3561 false
3562 }
3563 }
3564
3565 #[inline]
3566 pub fn check_signals(&self) -> PyResult<()> {
3569 #[cfg(feature = "threading")]
3570 if self.state.finalizing.load(Ordering::Acquire)
3571 && stdlib::_thread::get_ident() != self.state.finalizing_thread_ident.load()
3572 {
3573 thread::hang_current_thread(&self.state);
3578 }
3579
3580 #[cfg(feature = "threading")]
3582 thread::suspend_if_needed(&self.state);
3583
3584 #[cfg(feature = "threading")]
3586 if crate::signal::qsbr_bit_set() && thread::qsbr_break_requested() {
3587 thread::qsbr_checkpoint();
3588 }
3589
3590 #[cfg(not(target_arch = "wasm32"))]
3591 crate::signal::check_signals(self)?;
3592
3593 Ok(())
3594 }
3595
3596 #[cfg(feature = "threading")]
3602 pub(crate) fn run_scheduled_gc(&self) {
3603 if self.state.gc.collection_ready() {
3604 self.state.gc.collect(0);
3605 }
3606 }
3607
3608 pub(crate) fn push_exception(&self, exc: Option<PyBaseExceptionRef>) {
3616 #[cfg(feature = "threading")]
3617 let may_change_top = exc.is_some();
3618 self.exceptions.borrow_mut().stack.push(exc);
3619 #[cfg(feature = "threading")]
3620 if may_change_top {
3621 thread::update_thread_exception(self.topmost_exception());
3622 }
3623 }
3624
3625 pub(crate) fn pop_exception(&self) -> Option<PyBaseExceptionRef> {
3632 let exc = self
3633 .exceptions
3634 .borrow_mut()
3635 .stack
3636 .pop()
3637 .expect("pop_exception() without nested exc stack");
3638 #[cfg(feature = "threading")]
3639 if exc.is_some() {
3640 thread::update_thread_exception(self.topmost_exception());
3641 }
3642 exc
3643 }
3644
3645 pub fn current_exception(&self) -> Option<PyBaseExceptionRef> {
3646 self.exceptions.borrow().stack.last().cloned().flatten()
3647 }
3648
3649 pub fn set_exception(&self, exc: Option<PyBaseExceptionRef>) {
3651 let mut excs = self.exceptions.borrow_mut();
3653 debug_assert!(
3654 !excs.stack.is_empty(),
3655 "set_exception called with empty exception stack"
3656 );
3657 if let Some(top) = excs.stack.last_mut() {
3658 let prev = core::mem::replace(top, exc);
3659 drop(excs);
3660 drop(prev);
3661 } else {
3662 excs.stack.push(exc);
3663 drop(excs);
3664 }
3665 #[cfg(feature = "threading")]
3666 thread::update_thread_exception(self.topmost_exception());
3667 }
3668
3669 pub(crate) fn restore_exception(&self, saved: Option<PyBaseExceptionRef>) {
3676 let excs = self.exceptions.borrow();
3677 let unchanged = match (excs.stack.last(), &saved) {
3678 (Some(Some(current)), Some(saved)) => current.is(saved),
3679 (Some(None), None) => true,
3680 _ => false,
3681 };
3682 drop(excs);
3683 if !unchanged {
3684 self.set_exception(saved);
3685 }
3686 }
3687
3688 pub fn take_raised_exception(&self) -> Option<PyBaseExceptionRef> {
3689 let mut excs = self.exceptions.borrow_mut();
3690 if let Some(top) = excs.stack.last_mut() {
3691 let exc = top.take();
3692 drop(excs);
3693 #[cfg(feature = "threading")]
3694 thread::update_thread_exception(self.topmost_exception());
3695 exc
3696 } else {
3697 None
3698 }
3699 }
3700
3701 pub(crate) fn chain_stack_item(&self, exception: &Py<PyBaseException>) {
3705 if self.current_exception().is_some() {
3706 self.contextualize_exception(exception);
3707 }
3708 }
3709
3710 pub(crate) fn contextualize_exception(&self, exception: &Py<PyBaseException>) {
3711 if let Some(context_exc) = self.topmost_exception()
3712 && !context_exc.is(exception)
3713 {
3714 let mut o = context_exc.clone();
3717 let mut slow_o = context_exc.clone();
3718 let mut slow_update_toggle = false;
3719 while let Some(context) = o.__context__() {
3720 if context.is(exception) {
3721 o.set_context(None);
3722 break;
3723 }
3724 o = context;
3725 if o.is(&slow_o) {
3726 break;
3728 }
3729 if slow_update_toggle && let Some(slow_context) = slow_o.__context__() {
3730 slow_o = slow_context;
3731 }
3732 slow_update_toggle = !slow_update_toggle;
3733 }
3734 exception.set_context(Some(context_exc))
3735 }
3736 }
3737
3738 pub(crate) fn topmost_exception(&self) -> Option<PyBaseExceptionRef> {
3739 let excs = self.exceptions.borrow();
3740 excs.stack.iter().rev().find_map(|e| e.clone())
3741 }
3742
3743 pub fn handle_exit_exception(&self, exc: PyBaseExceptionRef) -> u32 {
3744 if exc.fast_isinstance(self.ctx.exceptions.system_exit) {
3745 let code = exc
3746 .as_object()
3747 .get_attr("code", self)
3748 .unwrap_or_else(|_| exc.as_object().to_owned());
3749 let msg = match_class!(match code {
3750 ref i @ PyInt => {
3751 use num_traits::cast::ToPrimitive;
3752 let code = i
3754 .as_bigint()
3755 .to_u32()
3756 .or_else(|| i.as_bigint().to_i32().map(|v| v as u32))
3757 .unwrap_or(-1i32 as u32);
3758 return code;
3759 }
3760 code => {
3761 if self.is_none(&code) {
3762 return 0;
3763 }
3764 code.str(self).ok()
3765 }
3766 });
3767 if let Some(msg) = msg {
3768 if let Ok(stderr) = stdlib::sys::get_stderr(self) {
3770 let _ = self.call_method(&stderr, "write", (msg,));
3771 let _ = self.call_method(&stderr, "write", ("\n",));
3772 }
3773 }
3774 1
3775 } else if exc.fast_isinstance(self.ctx.exceptions.keyboard_interrupt) {
3776 self.print_exception(&exc);
3777 cfg_select! {
3778 unix => {
3779 if crate::host_env::signal::set_sigint_default_onstack().is_ok() {
3780 self.flush_std();
3781 crate::host_env::signal::send_sigint_to_self()
3782 .expect("Expect to be killed.");
3783 }
3784
3785 (libc::SIGINT as u32) + 128
3786 }
3787 windows => 0xC000013A,
3789 _ => 1,
3790 }
3791 } else {
3792 self.print_exception(&exc);
3793 1
3794 }
3795 }
3796
3797 #[doc(hidden)]
3798 pub fn __module_set_attr(
3799 &self,
3800 module: &Py<PyModule>,
3801 attr_name: &'static PyStrInterned,
3802 attr_value: impl Into<PyObjectRef>,
3803 ) -> PyResult<()> {
3804 let val = attr_value.into();
3805 module
3806 .as_object()
3807 .generic_setattr(attr_name, PySetterValue::Assign(val), self)
3808 }
3809
3810 pub fn insert_sys_path(&self, obj: PyObjectRef) -> PyResult<()> {
3811 let sys_path = self.sys_module.get_attr("path", self).unwrap();
3812 self.call_method(&sys_path, "insert", (0, obj))?;
3813 Ok(())
3814 }
3815
3816 pub fn run_module(&self, module: &str) -> PyResult<()> {
3817 let runpy = self.import("runpy", 0)?;
3818 let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?;
3819 run_module_as_main.call((module,), self)?;
3820 Ok(())
3821 }
3822
3823 pub fn fs_encoding(&self) -> &'static PyStrInterned {
3824 identifier!(self, utf_8)
3825 }
3826
3827 pub fn fs_encode_errors(&self) -> &'static PyUtf8StrInterned {
3828 if cfg!(windows) {
3829 identifier_utf8!(self, surrogatepass)
3830 } else {
3831 identifier_utf8!(self, surrogateescape)
3832 }
3833 }
3834
3835 pub fn fsdecode(&self, s: impl Into<OsString>) -> PyStrRef {
3836 match s.into().into_string() {
3837 Ok(s) => self.ctx.new_str(s),
3838 Err(s) => {
3839 let bytes = self.ctx.new_bytes(s.into_encoded_bytes());
3840 let errors = self.fs_encode_errors().to_owned();
3841 let res = self.state.codec_registry.decode_text(
3842 bytes.into(),
3843 "utf-8",
3844 Some(errors),
3845 self,
3846 );
3847 self.expect_pyresult(res, "fsdecode should be lossless and never fail")
3848 }
3849 }
3850 }
3851
3852 pub fn fsencode<'a>(&self, s: &'a Py<PyStr>) -> PyResult<Cow<'a, OsStr>> {
3853 if cfg!(windows) || s.is_utf8() {
3854 let s = unsafe { OsStr::from_encoded_bytes_unchecked(s.as_bytes()) };
3857 return Ok(Cow::Borrowed(s));
3858 }
3859 let errors = self.fs_encode_errors().to_owned();
3860 let bytes = self
3861 .state
3862 .codec_registry
3863 .encode_text(s.to_owned(), "utf-8", Some(errors), self)?
3864 .as_bytes()
3865 .to_vec();
3866 let s = unsafe { OsString::from_encoded_bytes_unchecked(bytes) };
3869 Ok(Cow::Owned(s))
3870 }
3871}
3872
3873impl AsRef<Context> for VirtualMachine {
3874 fn as_ref(&self) -> &Context {
3875 &self.ctx
3876 }
3877}
3878
3879#[must_use]
3882pub fn resolve_frozen_alias(name: &str) -> &str {
3883 match name {
3884 "_frozen_importlib" => "importlib._bootstrap",
3885 "_frozen_importlib_external" => "importlib._bootstrap_external",
3886 "encodings_ascii" => "encodings.ascii",
3887 "encodings_utf_8" => "encodings.utf_8",
3888 "encodings_latin_1" => "encodings.latin_1",
3889 "__hello_alias__" | "__phello_alias__" | "__phello_alias__.spam" => "__hello__",
3890 "__phello__.__init__" => "<__phello__",
3891 "__phello__.ham.__init__" => "<__phello__.ham",
3892 "__hello_only__" => "",
3893 _ => name,
3894 }
3895}
3896
3897#[cfg(test)]
3898mod tests {
3899 use super::*;
3900
3901 #[test]
3902 fn nested_frozen() {
3903 use rustpython_vm as vm;
3904
3905 vm::Interpreter::builder(Default::default())
3906 .add_frozen_modules(rustpython_vm::py_freeze!(
3907 dir = "../../../../extra_tests/snippets"
3908 ))
3909 .build()
3910 .enter(|vm| {
3911 let scope = vm.new_scope_with_builtins();
3912
3913 let source = "from dir_module.dir_module_inner import value2";
3914 let code_obj = vm
3915 .compile(source, vm::compiler::Mode::Exec, "<embedded>")
3916 .map_err(|err| err.into_pyexception(vm, Some(source)))
3917 .unwrap();
3918
3919 if let Err(e) = vm.run_code_obj(code_obj, scope) {
3920 vm.print_exception(&e);
3921 panic!();
3922 }
3923 })
3924 }
3925
3926 #[test]
3927 fn frozen_origname_matches() {
3928 use rustpython_vm as vm;
3929
3930 vm::Interpreter::builder(Default::default())
3931 .build()
3932 .enter(|vm| {
3933 let check = |name, expected| {
3934 let module = import::import_frozen(vm, name).unwrap();
3935 let origname: PyStrRef = module
3936 .get_attr("__origname__", vm)
3937 .unwrap()
3938 .try_into_value(vm)
3939 .unwrap();
3940 assert_eq!(origname.as_wtf8(), expected);
3941 };
3942
3943 check("_frozen_importlib", "importlib._bootstrap");
3944 check(
3945 "_frozen_importlib_external",
3946 "importlib._bootstrap_external",
3947 );
3948 });
3949 }
3950}