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 #[must_use]
11 pub fn to_callable(&self) -> Option<PyCallable<'_>> {
12 PyCallable::new(self)
13 }
14
15 #[inline]
16 #[must_use]
17 pub fn is_callable(&self) -> bool {
18 self.to_callable().is_some()
19 }
20
21 #[inline]
23 pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
24 let args = args.into_args(vm);
25 self.call_with_args(args, vm)
26 }
27
28 pub fn call_with_args(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
30 let Some(callable) = self.to_callable() else {
31 return Err(vm.new_type_error(format!(
32 "'{}' object is not callable",
33 self.class().slot_name()
34 )));
35 };
36 vm_trace!("Invoke: {:?} {:?}", callable, args);
37 callable.invoke(args, vm)
38 }
39
40 #[inline]
43 pub fn vectorcall(
44 &self,
45 args: Vec<PyObjectRef>,
46 nargs: usize,
47 kwnames: Option<&[PyObjectRef]>,
48 vm: &VirtualMachine,
49 ) -> PyResult {
50 let Some(callable) = self.to_callable() else {
51 return Err(vm.new_type_error(format!(
52 "'{}' object is not callable",
53 self.class().slot_name()
54 )));
55 };
56 callable.invoke_vectorcall(args, nargs, kwnames, vm)
57 }
58}
59
60#[derive(Debug)]
61pub struct PyCallable<'a> {
62 pub obj: &'a PyObject,
63 pub call: GenericMethod,
64 pub vectorcall: Option<VectorCallFunc>,
65}
66
67impl<'a> PyCallable<'a> {
68 pub fn new(obj: &'a PyObject) -> Option<Self> {
69 let slots = obj.class().slots();
70 let call = slots.call.load()?;
71 let vectorcall = slots.vectorcall.load();
72 Some(PyCallable {
73 obj,
74 call,
75 vectorcall,
76 })
77 }
78
79 pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
80 let args = args.into_args(vm);
81 if !vm.use_tracing.get() {
82 return (self.call)(self.obj, args, vm);
83 }
84 let is_python_callable = self.obj.downcast_ref::<PyFunction>().is_some()
88 || self.obj.downcast_ref::<PyBoundMethod>().is_some();
89 if is_python_callable {
90 (self.call)(self.obj, args, vm)
91 } else {
92 let callable = self.obj.to_owned();
93 vm.trace_event(TraceEvent::CCall, Some(callable.clone()))?;
94 let result = (self.call)(self.obj, args, vm);
95 if result.is_ok() {
96 vm.trace_event(TraceEvent::CReturn, Some(callable))?;
97 } else {
98 let _ = vm.trace_event(TraceEvent::CException, Some(callable));
99 }
100 result
101 }
102 }
103
104 #[inline]
106 pub fn invoke_vectorcall(
107 &self,
108 args: Vec<PyObjectRef>,
109 nargs: usize,
110 kwnames: Option<&[PyObjectRef]>,
111 vm: &VirtualMachine,
112 ) -> PyResult {
113 if let Some(vc) = self.vectorcall {
114 if !vm.use_tracing.get() {
115 return vc(self.obj, args, nargs, kwnames, vm);
116 }
117 let is_python_callable = self.obj.downcast_ref::<PyFunction>().is_some()
118 || self.obj.downcast_ref::<PyBoundMethod>().is_some();
119 if is_python_callable {
120 vc(self.obj, args, nargs, kwnames, vm)
121 } else {
122 let callable = self.obj.to_owned();
123 vm.trace_event(TraceEvent::CCall, Some(callable.clone()))?;
124 let result = vc(self.obj, args, nargs, kwnames, vm);
125 if result.is_ok() {
126 vm.trace_event(TraceEvent::CReturn, Some(callable))?;
127 } else {
128 let _ = vm.trace_event(TraceEvent::CException, Some(callable));
129 }
130 result
131 }
132 } else {
133 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
135 self.invoke(func_args, vm)
136 }
137 }
138}
139
140#[derive(Clone, Copy, Eq, PartialEq)]
142pub(crate) enum TraceEvent {
143 Call,
144 Return,
145 Exception,
146 Line,
147 Opcode,
148 CCall,
149 CReturn,
150 CException,
151}
152
153impl TraceEvent {
154 #[must_use]
156 const fn is_trace_event(self) -> bool {
157 matches!(
158 self,
159 Self::Call | Self::Return | Self::Exception | Self::Line | Self::Opcode
160 )
161 }
162
163 #[must_use]
167 const fn is_profile_event(self) -> bool {
168 matches!(
169 self,
170 Self::Call | Self::Return | Self::CCall | Self::CReturn | Self::CException
171 )
172 }
173
174 #[must_use]
176 pub(crate) const fn is_opcode_event(self) -> bool {
177 matches!(self, Self::Opcode)
178 }
179
180 #[must_use]
182 const fn default_what(self) -> crate::stdlib::sys::monitoring::MonitoringEvent {
183 use crate::stdlib::sys::monitoring::MonitoringEvent as Ev;
184 match self {
185 Self::Call => Ev::PyStart,
186 Self::Return => Ev::PyReturn,
187 Self::Exception => Ev::Raise,
188 Self::Line => Ev::Line,
189 Self::Opcode => Ev::Instruction,
190 Self::CCall => Ev::Call,
191 Self::CReturn => Ev::CReturn,
192 Self::CException => Ev::CRaise,
193 }
194 }
195}
196
197impl core::fmt::Display for TraceEvent {
198 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 match self {
200 Self::Call => write!(f, "call"),
201 Self::Return => write!(f, "return"),
202 Self::Exception => write!(f, "exception"),
203 Self::Line => write!(f, "line"),
204 Self::Opcode => write!(f, "opcode"),
205 Self::CCall => write!(f, "c_call"),
206 Self::CReturn => write!(f, "c_return"),
207 Self::CException => write!(f, "c_exception"),
208 }
209 }
210}
211
212impl VirtualMachine {
213 #[inline]
223 pub(crate) fn trace_event(
224 &self,
225 event: TraceEvent,
226 arg: Option<PyObjectRef>,
227 ) -> PyResult<Option<PyObjectRef>> {
228 self.trace_event_what(event, event.default_what(), arg)
229 }
230
231 #[inline]
235 pub(crate) fn trace_event_what(
236 &self,
237 event: TraceEvent,
238 what: crate::stdlib::sys::monitoring::MonitoringEvent,
239 arg: Option<PyObjectRef>,
240 ) -> PyResult<Option<PyObjectRef>> {
241 if self.use_tracing.get() && !self.tracing_is_suppressed() {
242 let old = self.what_event.replace(Some(what));
243 let result = self._trace_event_inner(event, arg);
244 self.what_event.set(old);
245 result
246 } else {
247 Ok(None)
248 }
249 }
250 fn _trace_event_inner(
251 &self,
252 event: TraceEvent,
253 arg: Option<PyObjectRef>,
254 ) -> PyResult<Option<PyObjectRef>> {
255 let trace_func = self.trace_func.borrow().to_owned();
256 let profile_func = self.profile_func.borrow().to_owned();
257 if self.is_none(&trace_func) && self.is_none(&profile_func) {
258 return Ok(None);
259 }
260
261 let is_trace_event = event.is_trace_event();
262 let is_profile_event = event.is_profile_event();
263 let is_opcode_event = event.is_opcode_event();
264
265 let Some(frame_ref) = crate::frame::current_thread_frame_materialize(self) else {
266 return Ok(None);
267 };
268
269 if is_opcode_event
271 && !frame_ref
272 .iframe()
273 .cold_opt()
274 .is_some_and(|c| *c.trace_opcodes.lock())
275 {
276 return Ok(None);
277 }
278
279 let callback = if event == TraceEvent::Call {
282 if self.is_none(&trace_func) {
283 None
284 } else {
285 Some(trace_func)
286 }
287 } else {
288 frame_ref
289 .iframe()
290 .cold_opt()
291 .and_then(|c| c.trace.lock().clone())
292 };
293
294 let frame: PyObjectRef = frame_ref.to_owned().into();
295 let event_str: PyObjectRef = self.ctx.new_str(event.to_string()).into();
296 let args = vec![frame, event_str, arg.unwrap_or_else(|| self.ctx.none())];
297
298 let mut trace_result = None;
299
300 if is_trace_event && let Some(callback) = callback {
303 self.use_tracing.set(false);
304 self.enter_tracing();
305 let res = callback.call(args.clone(), self);
306 self.leave_tracing();
307 self.use_tracing.set(true);
308 match res {
309 Ok(result) => {
310 if !self.is_none(&result) {
311 *frame_ref.iframe().cold().trace.lock() = Some(result.clone());
312 trace_result = Some(result);
313 }
314 }
315 Err(e) => {
316 *self.trace_func.borrow_mut() = self.ctx.none();
319 *frame_ref.iframe().cold().trace.lock() = None;
320 let profile_is_none = self.is_none(&self.profile_func.borrow());
321 self.use_tracing.set(!profile_is_none);
322 return Err(e);
323 }
324 }
325 }
326
327 if is_profile_event && !self.is_none(&profile_func) {
328 self.use_tracing.set(false);
329 self.enter_tracing();
330 let res = profile_func.call(args, self);
331 self.leave_tracing();
332 self.use_tracing.set(true);
333 if res.is_err() {
334 *self.profile_func.borrow_mut() = self.ctx.none();
335 }
336 }
337 Ok(trace_result)
338 }
339}