#[cfg(all(unix, feature = "threading"))]
use super::StopTheWorldState;
use super::{Context, PyConfig, PyGlobalState, VirtualMachine, setting::Settings, thread};
use crate::{
PyResult, builtins, common::rc::PyRc, frozen::FrozenModule, getpath, py_freeze, stdlib::atexit,
vm::PyBaseExceptionRef,
};
use alloc::collections::BTreeMap;
use core::sync::atomic::Ordering;
type InitFunc = Box<dyn FnOnce(&mut VirtualMachine)>;
const EXITCODE_FLUSH_FAILURE: u32 = 120;
pub struct InterpreterBuilder {
settings: Settings,
pub ctx: PyRc<Context>,
module_defs: Vec<&'static builtins::PyModuleDef>,
frozen_modules: Vec<(&'static str, FrozenModule)>,
init_hooks: Vec<InitFunc>,
}
fn initialize_main_vm<F>(
settings: Settings,
ctx: PyRc<Context>,
module_defs: Vec<&'static builtins::PyModuleDef>,
frozen_modules: Vec<(&'static str, FrozenModule)>,
init_hooks: Vec<InitFunc>,
init: F,
) -> (VirtualMachine, PyRc<PyGlobalState>)
where
F: FnOnce(&mut VirtualMachine),
{
use crate::codecs::CodecsRegistry;
use crate::common::hash::HashSecret;
use crate::common::lock::PyMutex;
use crate::warn::WarningsState;
use core::sync::atomic::{AtomicBool, AtomicU64};
use crossbeam_utils::atomic::AtomicCell;
let paths = getpath::init_path_config(&settings);
let config = PyConfig::new(settings, paths);
let mut all_module_defs: BTreeMap<&'static str, &'static builtins::PyModuleDef> =
crate::stdlib::builtin_module_defs(&ctx)
.into_iter()
.chain(module_defs)
.map(|def| (def.name.as_str(), def))
.collect();
if let Some(&sysconfigdata_def) = all_module_defs.get("_sysconfigdata") {
use std::sync::OnceLock;
static SYSCONFIGDATA_NAME: OnceLock<&'static str> = OnceLock::new();
let leaked_name = *SYSCONFIGDATA_NAME.get_or_init(|| {
let name = crate::stdlib::sys::sysconfigdata_name();
Box::leak(name.into_boxed_str())
});
all_module_defs.insert(leaked_name, sysconfigdata_def);
}
let seed = match config.settings.hash_seed {
Some(seed) => seed,
None => super::process_hash_secret_seed(),
};
let hash_secret = HashSecret::new(seed);
let codec_registry = CodecsRegistry::new(&ctx);
let warnings = WarningsState::init_state(&ctx);
let int_max_str_digits = AtomicCell::new(match config.settings.int_max_str_digits {
-1 => 4300,
other => other,
} as usize);
let mut frozen: std::collections::HashMap<&'static str, FrozenModule, ahash::RandomState> =
core_frozen_inits().collect();
frozen.extend(frozen_modules);
let global_state = PyRc::new(PyGlobalState {
config,
module_defs: all_module_defs,
frozen,
stacksize: AtomicCell::new(0),
thread_count: AtomicCell::new(0),
hash_secret,
atexit_funcs: PyMutex::default(),
codec_registry,
finalizing: AtomicBool::new(false),
warnings,
override_frozen_modules: AtomicCell::new(0),
before_forkers: PyMutex::default(),
after_forkers_child: PyMutex::default(),
after_forkers_parent: PyMutex::default(),
int_max_str_digits,
switch_interval: AtomicCell::new(0.005),
global_trace_func: PyMutex::default(),
global_profile_func: PyMutex::default(),
#[cfg(feature = "threading")]
main_thread_ident: AtomicCell::new(0),
#[cfg(feature = "threading")]
thread_frames: parking_lot::Mutex::new(std::collections::HashMap::new()),
#[cfg(feature = "threading")]
thread_handles: parking_lot::Mutex::new(Vec::new()),
#[cfg(feature = "threading")]
shutdown_handles: parking_lot::Mutex::new(Vec::new()),
monitoring: PyMutex::default(),
monitoring_events: AtomicCell::new(0),
instrumentation_version: AtomicU64::new(0),
#[cfg(all(unix, feature = "threading"))]
stop_the_world: StopTheWorldState::new(),
});
let mut vm = VirtualMachine::new(ctx, global_state);
for hook in init_hooks {
hook(&mut vm);
}
init(&mut vm);
vm.initialize();
let global_state = vm.state.clone();
(vm, global_state)
}
impl InterpreterBuilder {
pub fn new() -> Self {
Self {
settings: Settings::default(),
ctx: Context::genesis().clone(),
module_defs: Vec::new(),
frozen_modules: Vec::new(),
init_hooks: Vec::new(),
}
}
pub fn settings(mut self, settings: Settings) -> Self {
self.settings = settings;
self
}
pub fn add_native_module(self, def: &'static builtins::PyModuleDef) -> Self {
self.add_native_modules(&[def])
}
pub fn add_native_modules(mut self, defs: &[&'static builtins::PyModuleDef]) -> Self {
self.module_defs.extend_from_slice(defs);
self
}
pub fn init_hook<F>(mut self, init: F) -> Self
where
F: FnOnce(&mut VirtualMachine) + 'static,
{
self.init_hooks.push(Box::new(init));
self
}
pub fn add_frozen_modules<I>(mut self, frozen: I) -> Self
where
I: IntoIterator<Item = (&'static str, FrozenModule)>,
{
self.frozen_modules.extend(frozen);
self
}
pub fn build(self) -> Interpreter {
let (vm, global_state) = initialize_main_vm(
self.settings,
self.ctx,
self.module_defs,
self.frozen_modules,
self.init_hooks,
|_| {}, );
Interpreter { global_state, vm }
}
pub fn interpreter(self) -> Interpreter {
self.build()
}
}
impl Default for InterpreterBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct Interpreter {
pub global_state: PyRc<PyGlobalState>,
vm: VirtualMachine,
}
impl Interpreter {
pub fn builder(settings: Settings) -> InterpreterBuilder {
InterpreterBuilder::new().settings(settings)
}
pub fn without_stdlib(settings: Settings) -> Self {
Self::with_init(settings, |_| {})
}
pub fn with_init<F>(settings: Settings, init: F) -> Self
where
F: FnOnce(&mut VirtualMachine),
{
let (vm, global_state) = initialize_main_vm(
settings,
Context::genesis().clone(),
Vec::new(), Vec::new(), Vec::new(), init,
);
Self { global_state, vm }
}
pub fn enter<F, R>(&self, f: F) -> R
where
F: FnOnce(&VirtualMachine) -> R,
{
thread::enter_vm(&self.vm, || f(&self.vm))
}
pub fn enter_and_expect<F, R>(&self, f: F, msg: &str) -> R
where
F: FnOnce(&VirtualMachine) -> PyResult<R>,
{
self.enter(|vm| {
let result = f(vm);
vm.expect_pyresult(result, msg)
})
}
pub fn run<F>(self, f: F) -> u32
where
F: FnOnce(&VirtualMachine) -> PyResult<()>,
{
let res = self.enter(|vm| f(vm));
self.finalize(res.err())
}
pub fn finalize(self, exc: Option<PyBaseExceptionRef>) -> u32 {
self.enter(|vm| {
let mut flush_status = vm.flush_std();
let exit_code = if let Some(exc) = exc {
vm.handle_exit_exception(exc)
} else {
0
};
if let Ok(threading) = vm.import("threading", 0)
&& let Ok(shutdown) = threading.get_attr("_shutdown", vm)
&& let Err(e) = shutdown.call((), vm)
{
vm.run_unraisable(
e,
Some("Exception ignored in threading shutdown".to_owned()),
threading,
);
}
atexit::_run_exitfuncs(vm);
vm.state.finalizing.store(true, Ordering::Release);
crate::gc_state::gc_state().collect_force(2);
vm.finalize_modules();
if vm.flush_std() < 0 && flush_status == 0 {
flush_status = -1;
}
if exit_code == 0 && flush_status < 0 {
EXITCODE_FLUSH_FAILURE
} else {
exit_code
}
})
}
}
fn core_frozen_inits() -> impl Iterator<Item = (&'static str, FrozenModule)> {
let iter = core::iter::empty();
macro_rules! ext_modules {
($iter:ident, $($t:tt)*) => {
let $iter = $iter.chain(py_freeze!($($t)*));
};
}
ext_modules!(
iter,
dir = "../../Lib/python_builtins",
crate_name = "rustpython_compiler_core"
);
ext_modules!(
iter,
dir = "../../Lib/core_modules",
crate_name = "rustpython_compiler_core"
);
let mut entries: Vec<_> = iter.collect();
if let Some(hello_code) = entries
.iter()
.find(|(n, _)| *n == "__hello__")
.map(|(_, m)| m.code)
{
entries.push((
"__hello_alias__",
FrozenModule {
code: hello_code,
package: false,
},
));
entries.push((
"__phello_alias__",
FrozenModule {
code: hello_code,
package: true,
},
));
entries.push((
"__phello_alias__.spam",
FrozenModule {
code: hello_code,
package: false,
},
));
entries.push((
"__hello_only__",
FrozenModule {
code: hello_code,
package: false,
},
));
}
if let Some(code) = entries
.iter()
.find(|(n, _)| *n == "__phello__")
.map(|(_, m)| m.code)
{
entries.push((
"__phello__.__init__",
FrozenModule {
code,
package: false,
},
));
}
if let Some(code) = entries
.iter()
.find(|(n, _)| *n == "__phello__.ham")
.map(|(_, m)| m.code)
{
entries.push((
"__phello__.ham.__init__",
FrozenModule {
code,
package: false,
},
));
}
entries.into_iter()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
PyObjectRef,
builtins::{PyStr, int},
};
use malachite_bigint::ToBigInt;
#[test]
fn test_add_py_integers() {
Interpreter::without_stdlib(Default::default()).enter(|vm| {
let a: PyObjectRef = vm.ctx.new_int(33_i32).into();
let b: PyObjectRef = vm.ctx.new_int(12_i32).into();
let res = vm._add(&a, &b).unwrap();
let value = int::get_value(&res);
assert_eq!(*value, 45_i32.to_bigint().unwrap());
})
}
#[test]
fn test_multiply_str() {
Interpreter::without_stdlib(Default::default()).enter(|vm| {
let a = vm.new_pyobj(crate::common::ascii!("Hello "));
let b = vm.new_pyobj(4_i32);
let res = vm._mul(&a, &b).unwrap();
let value = res.downcast_ref::<PyStr>().unwrap();
assert_eq!(value.as_wtf8(), "Hello Hello Hello Hello ")
})
}
}