1use crate::{
2 builtins::{PyBoundMethod, PyFunction},
3 function::{FuncArgs, IntoFuncArgs},
4 types::{GenericMethod, VectorCallFunc},
5 {PyObject, PyObjectRef, PyResult, VirtualMachine},
6};
7
8impl PyObject {
9 #[inline]
10 pub fn to_callable(&self) -> Option<PyCallable<'_>> {
11 PyCallable::new(self)
12 }
13
14 #[inline]
15 pub fn is_callable(&self) -> bool {
16 self.to_callable().is_some()
17 }
18
19 #[inline]
21 pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
22 let args = args.into_args(vm);
23 self.call_with_args(args, vm)
24 }
25
26 pub fn call_with_args(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
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 vm_trace!("Invoke: {:?} {:?}", callable, args);
34 callable.invoke(args, vm)
35 }
36
37 #[inline]
40 pub fn vectorcall(
41 &self,
42 args: Vec<PyObjectRef>,
43 nargs: usize,
44 kwnames: Option<&[PyObjectRef]>,
45 vm: &VirtualMachine,
46 ) -> PyResult {
47 let Some(callable) = self.to_callable() else {
48 return Err(
49 vm.new_type_error(format!("'{}' object is not callable", self.class().name()))
50 );
51 };
52 callable.invoke_vectorcall(args, nargs, kwnames, vm)
53 }
54}
55
56#[derive(Debug)]
57pub struct PyCallable<'a> {
58 pub obj: &'a PyObject,
59 pub call: GenericMethod,
60 pub vectorcall: Option<VectorCallFunc>,
61}
62
63impl<'a> PyCallable<'a> {
64 pub fn new(obj: &'a PyObject) -> Option<Self> {
65 let slots = &obj.class().slots;
66 let call = slots.call.load()?;
67 let vectorcall = slots.vectorcall.load();
68 Some(PyCallable {
69 obj,
70 call,
71 vectorcall,
72 })
73 }
74
75 pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
76 let args = args.into_args(vm);
77 if !vm.use_tracing.get() {
78 return (self.call)(self.obj, args, vm);
79 }
80 let is_python_callable = self.obj.downcast_ref::<PyFunction>().is_some()
84 || self.obj.downcast_ref::<PyBoundMethod>().is_some();
85 if is_python_callable {
86 (self.call)(self.obj, args, vm)
87 } else {
88 let callable = self.obj.to_owned();
89 vm.trace_event(TraceEvent::CCall, Some(callable.clone()))?;
90 let result = (self.call)(self.obj, args, vm);
91 if result.is_ok() {
92 vm.trace_event(TraceEvent::CReturn, Some(callable))?;
93 } else {
94 let _ = vm.trace_event(TraceEvent::CException, Some(callable));
95 }
96 result
97 }
98 }
99
100 #[inline]
102 pub fn invoke_vectorcall(
103 &self,
104 args: Vec<PyObjectRef>,
105 nargs: usize,
106 kwnames: Option<&[PyObjectRef]>,
107 vm: &VirtualMachine,
108 ) -> PyResult {
109 if let Some(vc) = self.vectorcall {
110 if !vm.use_tracing.get() {
111 return vc(self.obj, args, nargs, kwnames, vm);
112 }
113 let is_python_callable = self.obj.downcast_ref::<PyFunction>().is_some()
114 || self.obj.downcast_ref::<PyBoundMethod>().is_some();
115 if is_python_callable {
116 vc(self.obj, args, nargs, kwnames, vm)
117 } else {
118 let callable = self.obj.to_owned();
119 vm.trace_event(TraceEvent::CCall, Some(callable.clone()))?;
120 let result = vc(self.obj, args, nargs, kwnames, vm);
121 if result.is_ok() {
122 vm.trace_event(TraceEvent::CReturn, Some(callable))?;
123 } else {
124 let _ = vm.trace_event(TraceEvent::CException, Some(callable));
125 }
126 result
127 }
128 } else {
129 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
131 self.invoke(func_args, vm)
132 }
133 }
134}
135
136pub(crate) enum TraceEvent {
138 Call,
139 Return,
140 Exception,
141 Line,
142 Opcode,
143 CCall,
144 CReturn,
145 CException,
146}
147
148impl TraceEvent {
149 fn is_trace_event(&self) -> bool {
151 matches!(
152 self,
153 Self::Call | Self::Return | Self::Exception | Self::Line | Self::Opcode
154 )
155 }
156
157 fn is_profile_event(&self) -> bool {
161 matches!(
162 self,
163 Self::Call | Self::Return | Self::CCall | Self::CReturn | Self::CException
164 )
165 }
166
167 pub(crate) fn is_opcode_event(&self) -> bool {
169 matches!(self, Self::Opcode)
170 }
171}
172
173impl core::fmt::Display for TraceEvent {
174 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175 use TraceEvent::*;
176 match self {
177 Call => write!(f, "call"),
178 Return => write!(f, "return"),
179 Exception => write!(f, "exception"),
180 Line => write!(f, "line"),
181 Opcode => write!(f, "opcode"),
182 CCall => write!(f, "c_call"),
183 CReturn => write!(f, "c_return"),
184 CException => write!(f, "c_exception"),
185 }
186 }
187}
188
189impl VirtualMachine {
190 #[inline]
200 pub(crate) fn trace_event(
201 &self,
202 event: TraceEvent,
203 arg: Option<PyObjectRef>,
204 ) -> PyResult<Option<PyObjectRef>> {
205 if self.use_tracing.get() {
206 self._trace_event_inner(event, arg)
207 } else {
208 Ok(None)
209 }
210 }
211 fn _trace_event_inner(
212 &self,
213 event: TraceEvent,
214 arg: Option<PyObjectRef>,
215 ) -> PyResult<Option<PyObjectRef>> {
216 let trace_func = self.trace_func.borrow().to_owned();
217 let profile_func = self.profile_func.borrow().to_owned();
218 if self.is_none(&trace_func) && self.is_none(&profile_func) {
219 return Ok(None);
220 }
221
222 let is_trace_event = event.is_trace_event();
223 let is_profile_event = event.is_profile_event();
224 let is_opcode_event = event.is_opcode_event();
225
226 let Some(frame_ref) = self.current_frame() else {
227 return Ok(None);
228 };
229
230 if is_opcode_event && !*frame_ref.trace_opcodes.lock() {
232 return Ok(None);
233 }
234
235 let frame: PyObjectRef = frame_ref.into();
236 let event = self.ctx.new_str(event.to_string()).into();
237 let args = vec![frame, event, arg.unwrap_or_else(|| self.ctx.none())];
238
239 let mut trace_result = None;
240
241 if is_trace_event && !self.is_none(&trace_func) {
244 self.use_tracing.set(false);
245 let res = trace_func.call(args.clone(), self);
246 self.use_tracing.set(true);
247 match res {
248 Ok(result) => {
249 if !self.is_none(&result) {
250 trace_result = Some(result);
251 }
252 }
253 Err(e) => {
254 if let Some(frame_ref) = self.current_frame() {
257 *frame_ref.trace.lock() = self.ctx.none();
258 }
259 return Err(e);
260 }
261 }
262 }
263
264 if is_profile_event && !self.is_none(&profile_func) {
265 self.use_tracing.set(false);
266 let res = profile_func.call(args, self);
267 self.use_tracing.set(true);
268 if res.is_err() {
269 *self.profile_func.borrow_mut() = self.ctx.none();
270 }
271 }
272 Ok(trace_result)
273 }
274}