1use super::PyMethod;
2use crate::{
3 builtins::{pystr::AsPyStr, PyBaseExceptionRef, PyList, PyStrInterned},
4 function::IntoFuncArgs,
5 identifier,
6 object::{AsObject, PyObject, PyObjectRef, PyResult},
7 stdlib::sys,
8 vm::VirtualMachine,
9};
10
11impl VirtualMachine {
13 #[track_caller]
14 #[cold]
15 fn _py_panic_failed(&self, exc: PyBaseExceptionRef, msg: &str) -> ! {
16 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
17 {
18 self.print_exception(exc);
19 self.flush_std();
20 panic!("{msg}")
21 }
22 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
23 {
24 use wasm_bindgen::prelude::*;
25 #[wasm_bindgen]
26 extern "C" {
27 #[wasm_bindgen(js_namespace = console)]
28 fn error(s: &str);
29 }
30 let mut s = String::new();
31 self.write_exception(&mut s, &exc).unwrap();
32 error(&s);
33 panic!("{}; exception backtrace above", msg)
34 }
35 }
36
37 pub(crate) fn flush_std(&self) {
38 let vm = self;
39 if let Ok(stdout) = sys::get_stdout(vm) {
40 let _ = vm.call_method(&stdout, identifier!(vm, flush).as_str(), ());
41 }
42 if let Ok(stderr) = sys::get_stderr(vm) {
43 let _ = vm.call_method(&stderr, identifier!(vm, flush).as_str(), ());
44 }
45 }
46
47 #[track_caller]
48 pub fn unwrap_pyresult<T>(&self, result: PyResult<T>) -> T {
49 match result {
50 Ok(x) => x,
51 Err(exc) => {
52 self._py_panic_failed(exc, "called `vm.unwrap_pyresult()` on an `Err` value")
53 }
54 }
55 }
56 #[track_caller]
57 pub fn expect_pyresult<T>(&self, result: PyResult<T>, msg: &str) -> T {
58 match result {
59 Ok(x) => x,
60 Err(exc) => self._py_panic_failed(exc, msg),
61 }
62 }
63
64 pub fn is_none(&self, obj: &PyObject) -> bool {
66 obj.is(&self.ctx.none)
67 }
68 pub fn option_if_none(&self, obj: PyObjectRef) -> Option<PyObjectRef> {
69 if self.is_none(&obj) {
70 None
71 } else {
72 Some(obj)
73 }
74 }
75 pub fn unwrap_or_none(&self, obj: Option<PyObjectRef>) -> PyObjectRef {
76 obj.unwrap_or_else(|| self.ctx.none())
77 }
78
79 pub fn call_get_descriptor_specific(
80 &self,
81 descr: &PyObject,
82 obj: Option<PyObjectRef>,
83 cls: Option<PyObjectRef>,
84 ) -> Option<PyResult> {
85 let descr_get = descr
86 .class()
87 .mro_find_map(|cls| cls.slots.descr_get.load())?;
88 Some(descr_get(descr.to_owned(), obj, cls, self))
89 }
90
91 pub fn call_get_descriptor(&self, descr: &PyObject, obj: PyObjectRef) -> Option<PyResult> {
92 let cls = obj.class().to_owned().into();
93 self.call_get_descriptor_specific(descr, Some(obj), Some(cls))
94 }
95
96 pub fn call_if_get_descriptor(&self, attr: &PyObject, obj: PyObjectRef) -> PyResult {
97 self.call_get_descriptor(attr, obj)
98 .unwrap_or_else(|| Ok(attr.to_owned()))
99 }
100
101 #[inline]
102 pub fn call_method<T>(&self, obj: &PyObject, method_name: &str, args: T) -> PyResult
103 where
104 T: IntoFuncArgs,
105 {
106 flame_guard!(format!("call_method({:?})", method_name));
107
108 let dynamic_name;
109 let name = match self.ctx.interned_str(method_name) {
110 Some(name) => name.as_pystr(&self.ctx),
111 None => {
112 dynamic_name = self.ctx.new_str(method_name);
113 &dynamic_name
114 }
115 };
116 PyMethod::get(obj.to_owned(), name, self)?.invoke(args, self)
117 }
118
119 pub fn dir(&self, obj: Option<PyObjectRef>) -> PyResult<PyList> {
120 let seq = match obj {
121 Some(obj) => self
122 .get_special_method(&obj, identifier!(self, __dir__))?
123 .ok_or_else(|| self.new_type_error("object does not provide __dir__".to_owned()))?
124 .invoke((), self)?,
125 None => self.call_method(
126 self.current_locals()?.as_object(),
127 identifier!(self, keys).as_str(),
128 (),
129 )?,
130 };
131 let items: Vec<_> = seq.try_to_value(self)?;
132 let lst = PyList::from(items);
133 lst.sort(Default::default(), self)?;
134 Ok(lst)
135 }
136
137 #[inline]
138 pub(crate) fn get_special_method(
139 &self,
140 obj: &PyObject,
141 method: &'static PyStrInterned,
142 ) -> PyResult<Option<PyMethod>> {
143 PyMethod::get_special::<false>(obj, method, self)
144 }
145
146 #[doc(hidden)]
148 pub fn call_special_method(
149 &self,
150 obj: &PyObject,
151 method: &'static PyStrInterned,
152 args: impl IntoFuncArgs,
153 ) -> PyResult {
154 self.get_special_method(obj, method)?
155 .ok_or_else(|| self.new_attribute_error(method.as_str().to_owned()))?
156 .invoke(args, self)
157 }
158
159 pub fn print(&self, args: impl IntoFuncArgs) -> PyResult<()> {
163 let ret = self.builtins.get_attr("print", self)?.call(args, self)?;
164 debug_assert!(self.is_none(&ret));
165 Ok(())
166 }
167
168 #[deprecated(note = "in favor of `obj.call(args, vm)`")]
169 pub fn invoke(&self, obj: &impl AsObject, args: impl IntoFuncArgs) -> PyResult {
170 obj.as_object().call(args, self)
171 }
172}