rustpython_vm/protocol/
callable.rs1use 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 #[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 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
57enum 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 #[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 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}