rustpython_vm/protocol/
callable.rs

1use crate::{
2    function::{FuncArgs, IntoFuncArgs},
3    types::GenericMethod,
4    {AsObject, PyObject, PyResult, VirtualMachine},
5};
6
7impl PyObject {
8    #[inline]
9    pub fn to_callable(&self) -> Option<PyCallable<'_>> {
10        PyCallable::new(self)
11    }
12
13    #[inline]
14    pub fn is_callable(&self) -> bool {
15        self.to_callable().is_some()
16    }
17
18    /// PyObject_Call*Arg* series
19    #[inline]
20    pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
21        let args = args.into_args(vm);
22        self.call_with_args(args, vm)
23    }
24
25    /// PyObject_Call
26    pub fn call_with_args(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
27        vm_trace!("Invoke: {:?} {:?}", callable, args);
28        let Some(callable) = self.to_callable() else {
29            return Err(
30                vm.new_type_error(format!("'{}' object is not callable", self.class().name()))
31            );
32        };
33        callable.invoke(args, vm)
34    }
35}
36
37pub struct PyCallable<'a> {
38    pub obj: &'a PyObject,
39    pub call: GenericMethod,
40}
41
42impl<'a> PyCallable<'a> {
43    pub fn new(obj: &'a PyObject) -> Option<Self> {
44        let call = obj.class().mro_find_map(|cls| cls.slots.call.load())?;
45        Some(PyCallable { obj, call })
46    }
47
48    pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
49        let args = args.into_args(vm);
50        vm.trace_event(TraceEvent::Call)?;
51        let result = (self.call)(self.obj, args, vm);
52        vm.trace_event(TraceEvent::Return)?;
53        result
54    }
55}
56
57/// Trace events for sys.settrace and sys.setprofile.
58enum TraceEvent {
59    Call,
60    Return,
61}
62
63impl std::fmt::Display for TraceEvent {
64    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
65        use TraceEvent::*;
66        match self {
67            Call => write!(f, "call"),
68            Return => write!(f, "return"),
69        }
70    }
71}
72
73impl VirtualMachine {
74    /// Call registered trace function.
75    #[inline]
76    fn trace_event(&self, event: TraceEvent) -> PyResult<()> {
77        if self.use_tracing.get() {
78            self._trace_event_inner(event)
79        } else {
80            Ok(())
81        }
82    }
83    fn _trace_event_inner(&self, event: TraceEvent) -> PyResult<()> {
84        let trace_func = self.trace_func.borrow().to_owned();
85        let profile_func = self.profile_func.borrow().to_owned();
86        if self.is_none(&trace_func) && self.is_none(&profile_func) {
87            return Ok(());
88        }
89
90        let frame_ref = self.current_frame();
91        if frame_ref.is_none() {
92            return Ok(());
93        }
94
95        let frame = frame_ref.unwrap().as_object().to_owned();
96        let event = self.ctx.new_str(event.to_string()).into();
97        let args = vec![frame, event, self.ctx.none()];
98
99        // temporarily disable tracing, during the call to the
100        // tracing function itself.
101        if !self.is_none(&trace_func) {
102            self.use_tracing.set(false);
103            let res = trace_func.call(args.clone(), self);
104            self.use_tracing.set(true);
105            if res.is_err() {
106                *self.trace_func.borrow_mut() = self.ctx.none();
107            }
108        }
109
110        if !self.is_none(&profile_func) {
111            self.use_tracing.set(false);
112            let res = profile_func.call(args, self);
113            self.use_tracing.set(true);
114            if res.is_err() {
115                *self.profile_func.borrow_mut() = self.ctx.none();
116            }
117        }
118        Ok(())
119    }
120}