1#[cfg(feature = "jit")]
2mod jit;
3
4use super::{
5 PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyModule, PyStr, PyStrRef, PyTuple,
6 PyTupleRef, PyType,
7};
8use crate::common::hash::PyHash;
9use crate::common::lock::PyMutex;
10use crate::function::ArgMapping;
11use crate::object::{PyAtomicRef, Traverse, TraverseFn};
12use crate::{
13 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
14 bytecode,
15 class::PyClassImpl,
16 common::wtf8::{Wtf8Buf, wtf8_concat},
17 frame::{Frame, FrameRef},
18 function::{FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
19 scope::Scope,
20 types::{
21 Callable, Comparable, Constructor, GetAttr, GetDescriptor, Hashable, PyComparisonOp,
22 Representable,
23 },
24};
25use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
26use itertools::Itertools;
27#[cfg(feature = "jit")]
28use rustpython_jit::CompiledCode;
29
30fn format_missing_args(
31 qualname: impl core::fmt::Display,
32 kind: &str,
33 missing: &mut Vec<impl core::fmt::Display>,
34) -> String {
35 let count = missing.len();
36 let last = if missing.len() > 1 {
37 missing.pop()
38 } else {
39 None
40 };
41 let (and, right): (&str, String) = if let Some(last) = last {
42 (
43 if missing.len() == 1 {
44 "' and '"
45 } else {
46 "', and '"
47 },
48 format!("{last}"),
49 )
50 } else {
51 ("", String::new())
52 };
53 format!(
54 "{qualname}() missing {count} required {kind} argument{}: '{}{}{right}'",
55 if count == 1 { "" } else { "s" },
56 missing.iter().join("', '"),
57 and,
58 )
59}
60
61#[pyclass(module = false, name = "function", traverse = "manual")]
62#[derive(Debug)]
63pub struct PyFunction {
64 code: PyAtomicRef<PyCode>,
65 globals: PyDictRef,
66 builtins: PyObjectRef,
67 pub(crate) closure: Option<PyRef<PyTuple<PyCellRef>>>,
68 defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
69 name: PyMutex<PyStrRef>,
70 qualname: PyMutex<PyStrRef>,
71 type_params: PyMutex<PyTupleRef>,
72 annotations: PyMutex<Option<PyDictRef>>,
73 annotate: PyMutex<Option<PyObjectRef>>,
74 module: PyMutex<PyObjectRef>,
75 doc: PyMutex<PyObjectRef>,
76 func_version: AtomicU32,
77 #[cfg(feature = "jit")]
78 jitted_code: PyMutex<Option<CompiledCode>>,
79}
80
81static FUNC_VERSION_COUNTER: AtomicU32 = AtomicU32::new(1);
82
83fn next_func_version() -> u32 {
86 FUNC_VERSION_COUNTER
87 .fetch_update(Relaxed, Relaxed, |v| (v != 0).then(|| v.wrapping_add(1)))
88 .unwrap_or(0)
89}
90
91unsafe impl Traverse for PyFunction {
92 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
93 self.globals.traverse(tracer_fn);
94 if let Some(closure) = self.closure.as_ref() {
95 closure.as_untyped().traverse(tracer_fn);
96 }
97 self.defaults_and_kwdefaults.traverse(tracer_fn);
98 self.type_params.lock().traverse(tracer_fn);
100 self.annotations.lock().traverse(tracer_fn);
101 self.module.lock().traverse(tracer_fn);
102 self.doc.lock().traverse(tracer_fn);
103 }
104
105 fn clear(&mut self, out: &mut Vec<crate::PyObjectRef>) {
106 if let Some(closure) = self.closure.take() {
108 out.push(closure.into());
109 }
110
111 if let Some(mut guard) = self.defaults_and_kwdefaults.try_lock() {
113 if let Some(defaults) = guard.0.take() {
114 out.push(defaults.into());
115 }
116 if let Some(kwdefaults) = guard.1.take() {
117 out.push(kwdefaults.into());
118 }
119 }
120
121 if let Some(mut guard) = self.annotations.try_lock()
123 && let Some(annotations) = guard.take()
124 {
125 out.push(annotations.into());
126 }
127 if let Some(mut guard) = self.annotate.try_lock()
128 && let Some(annotate) = guard.take()
129 {
130 out.push(annotate);
131 }
132
133 if let Some(mut guard) = self.module.try_lock() {
135 let old_module =
136 core::mem::replace(&mut *guard, Context::genesis().none.to_owned().into());
137 out.push(old_module);
138 }
139 if let Some(mut guard) = self.doc.try_lock() {
140 let old_doc =
141 core::mem::replace(&mut *guard, Context::genesis().none.to_owned().into());
142 out.push(old_doc);
143 }
144 if let Some(mut guard) = self.type_params.try_lock() {
145 let old_type_params =
146 core::mem::replace(&mut *guard, Context::genesis().empty_tuple.to_owned());
147 out.push(old_type_params.into());
148 }
149
150 if let Some(mut guard) = self.name.try_lock() {
153 let old_name = core::mem::replace(&mut *guard, Context::genesis().empty_str.to_owned());
154 out.push(old_name.into());
155 }
156 if let Some(mut guard) = self.qualname.try_lock() {
157 let old_qualname =
158 core::mem::replace(&mut *guard, Context::genesis().empty_str.to_owned());
159 out.push(old_qualname.into());
160 }
161
162 }
164}
165
166impl PyFunction {
167 #[inline]
168 pub(crate) fn new(
169 code: PyRef<PyCode>,
170 globals: PyDictRef,
171 vm: &VirtualMachine,
172 ) -> PyResult<Self> {
173 let name = PyMutex::new(code.obj_name.to_owned());
174 let module = vm.unwrap_or_none(globals.get_item_opt(identifier!(vm, __name__), vm)?);
175 let builtins = globals.get_item("__builtins__", vm).unwrap_or_else(|_| {
176 if let Some(frame) = vm.current_frame() {
178 frame.builtins.clone()
179 } else {
180 vm.builtins.dict().into()
181 }
182 });
183 let builtins = if let Some(module) = builtins.downcast_ref::<PyModule>() {
185 module.dict().into()
186 } else {
187 builtins
188 };
189
190 let doc = if code.code.flags.contains(bytecode::CodeFlags::HAS_DOCSTRING) {
192 code.code
193 .constants
194 .first()
195 .map(|c| c.as_object().to_owned())
196 .unwrap_or_else(|| vm.ctx.none())
197 } else {
198 vm.ctx.none()
199 };
200
201 let qualname = vm.ctx.new_str(code.qualname.as_str());
202 let func = Self {
203 code: PyAtomicRef::from(code.clone()),
204 globals,
205 builtins,
206 closure: None,
207 defaults_and_kwdefaults: PyMutex::new((None, None)),
208 name,
209 qualname: PyMutex::new(qualname),
210 type_params: PyMutex::new(vm.ctx.empty_tuple.clone()),
211 annotations: PyMutex::new(None),
212 annotate: PyMutex::new(None),
213 module: PyMutex::new(module),
214 doc: PyMutex::new(doc),
215 func_version: AtomicU32::new(next_func_version()),
216 #[cfg(feature = "jit")]
217 jitted_code: PyMutex::new(None),
218 };
219 Ok(func)
220 }
221
222 fn fill_locals_from_args(
223 &self,
224 frame: &Frame,
225 func_args: FuncArgs,
226 vm: &VirtualMachine,
227 ) -> PyResult<()> {
228 let code: &Py<PyCode> = &self.code;
229 let nargs = func_args.args.len();
230 let n_expected_args = code.arg_count as usize;
231 let total_args = code.arg_count as usize + code.kwonlyarg_count as usize;
232 let fastlocals = unsafe { frame.fastlocals_mut() };
242
243 let mut args_iter = func_args.args.into_iter();
244
245 for (local, arg) in Iterator::zip(
249 fastlocals.iter_mut().take(n_expected_args),
250 args_iter.by_ref().take(nargs),
251 ) {
252 *local = Some(arg);
253 }
254
255 let mut vararg_offset = total_args;
256 if code.flags.contains(bytecode::CodeFlags::VARARGS) {
258 let vararg_value = vm.ctx.new_tuple(args_iter.collect());
259 fastlocals[vararg_offset] = Some(vararg_value.into());
260 vararg_offset += 1;
261 } else {
262 if nargs > n_expected_args {
264 let n_defaults = self
265 .defaults_and_kwdefaults
266 .lock()
267 .0
268 .as_ref()
269 .map_or(0, |d| d.len());
270 let n_required = n_expected_args - n_defaults;
271 let takes_msg = if n_defaults > 0 {
272 format!("from {} to {}", n_required, n_expected_args)
273 } else {
274 n_expected_args.to_string()
275 };
276
277 let kw_only_given = if code.kwonlyarg_count > 0 {
279 let start = code.arg_count as usize;
280 let end = start + code.kwonlyarg_count as usize;
281 code.varnames[start..end]
282 .iter()
283 .filter(|name| func_args.kwargs.contains_key(name.as_str()))
284 .count()
285 } else {
286 0
287 };
288
289 let given_msg = if kw_only_given > 0 {
290 format!(
291 "{} positional argument{} (and {} keyword-only argument{}) were",
292 nargs,
293 if nargs == 1 { "" } else { "s" },
294 kw_only_given,
295 if kw_only_given == 1 { "" } else { "s" },
296 )
297 } else {
298 format!("{} {}", nargs, if nargs == 1 { "was" } else { "were" })
299 };
300
301 return Err(vm.new_type_error(format!(
302 "{}() takes {} positional argument{} but {} given",
303 self.__qualname__(),
304 takes_msg,
305 if n_expected_args == 1 { "" } else { "s" },
306 given_msg,
307 )));
308 }
309 }
310
311 let kwargs = if code.flags.contains(bytecode::CodeFlags::VARKEYWORDS) {
313 let d = vm.ctx.new_dict();
314 fastlocals[vararg_offset] = Some(d.clone().into());
315 Some(d)
316 } else {
317 None
318 };
319
320 let arg_pos = |range: core::ops::Range<_>, name: &str| {
321 code.varnames
322 .iter()
323 .enumerate()
324 .skip(range.start)
325 .take(range.end - range.start)
326 .find(|(_, s)| s.as_str() == name)
327 .map(|(p, _)| p)
328 };
329
330 let mut posonly_passed_as_kwarg = Vec::new();
331 for (name, value) in func_args.kwargs {
333 if let Some(pos) = arg_pos(code.posonlyarg_count as usize..total_args, &name) {
335 let slot = &mut fastlocals[pos];
336 if slot.is_some() {
337 return Err(vm.new_type_error(format!(
338 "{}() got multiple values for argument '{}'",
339 self.__qualname__(),
340 name
341 )));
342 }
343 *slot = Some(value);
344 } else if let Some(kwargs) = kwargs.as_ref() {
345 kwargs.set_item(&name, value, vm)?;
346 } else if arg_pos(0..code.posonlyarg_count as usize, &name).is_some() {
347 posonly_passed_as_kwarg.push(name);
348 } else {
349 return Err(vm.new_type_error(format!(
350 "{}() got an unexpected keyword argument '{}'",
351 self.__qualname__(),
352 name
353 )));
354 }
355 }
356 if !posonly_passed_as_kwarg.is_empty() {
357 return Err(vm.new_type_error(format!(
358 "{}() got some positional-only arguments passed as keyword arguments: '{}'",
359 self.__qualname__(),
360 posonly_passed_as_kwarg.into_iter().format(", "),
361 )));
362 }
363
364 let mut defaults_and_kwdefaults = None;
365 macro_rules! get_defaults {
367 () => {{
368 defaults_and_kwdefaults
369 .get_or_insert_with(|| self.defaults_and_kwdefaults.lock().clone())
370 }};
371 }
372
373 if nargs < n_expected_args {
376 let defaults = get_defaults!().0.as_ref().map(|tup| tup.as_slice());
377 let n_defs = defaults.map_or(0, |d| d.len());
378
379 let n_required = code.arg_count as usize - n_defs;
380
381 let mut missing: Vec<_> = (nargs..n_required)
384 .filter_map(|i| {
385 if fastlocals[i].is_none() {
386 Some(&code.varnames[i])
387 } else {
388 None
389 }
390 })
391 .collect();
392
393 if !missing.is_empty() {
394 return Err(vm.new_type_error(format_missing_args(
395 self.__qualname__(),
396 "positional",
397 &mut missing,
398 )));
399 }
400
401 if let Some(defaults) = defaults {
402 let n = core::cmp::min(nargs, n_expected_args);
403 let i = n.saturating_sub(n_required);
404
405 for i in i..defaults.len() {
408 let slot = &mut fastlocals[n_required + i];
409 if slot.is_none() {
410 *slot = Some(defaults[i].clone());
411 }
412 }
413 }
414 };
415
416 if code.kwonlyarg_count > 0 {
417 let mut missing = Vec::new();
418 for (slot, kwarg) in fastlocals
420 .iter_mut()
421 .zip(&*code.varnames)
422 .skip(code.arg_count as usize)
423 .take(code.kwonlyarg_count as usize)
424 .filter(|(slot, _)| slot.is_none())
425 {
426 if let Some(defaults) = &get_defaults!().1
427 && let Some(default) = defaults.get_item_opt(&**kwarg, vm)?
428 {
429 *slot = Some(default);
430 continue;
431 }
432
433 missing.push(kwarg);
435 }
436
437 if !missing.is_empty() {
438 return Err(vm.new_type_error(format_missing_args(
439 self.__qualname__(),
440 "keyword-only",
441 &mut missing,
442 )));
443 }
444 }
445
446 Ok(())
447 }
448
449 pub(crate) fn set_function_attribute(
451 &mut self,
452 attr: bytecode::MakeFunctionFlag,
453 attr_value: PyObjectRef,
454 vm: &VirtualMachine,
455 ) -> PyResult<()> {
456 use crate::builtins::PyDict;
457 match attr {
458 bytecode::MakeFunctionFlag::Defaults => {
459 let defaults = match attr_value.downcast::<PyTuple>() {
460 Ok(tuple) => tuple,
461 Err(obj) => {
462 return Err(vm.new_type_error(format!(
463 "__defaults__ must be a tuple, not {}",
464 obj.class().name()
465 )));
466 }
467 };
468 self.defaults_and_kwdefaults.lock().0 = Some(defaults);
469 }
470 bytecode::MakeFunctionFlag::KwOnlyDefaults => {
471 let kwdefaults = match attr_value.downcast::<PyDict>() {
472 Ok(dict) => dict,
473 Err(obj) => {
474 return Err(vm.new_type_error(format!(
475 "__kwdefaults__ must be a dict, not {}",
476 obj.class().name()
477 )));
478 }
479 };
480 self.defaults_and_kwdefaults.lock().1 = Some(kwdefaults);
481 }
482 bytecode::MakeFunctionFlag::Annotations => {
483 let annotations = match attr_value.downcast::<PyDict>() {
484 Ok(dict) => dict,
485 Err(obj) => {
486 return Err(vm.new_type_error(format!(
487 "__annotations__ must be a dict, not {}",
488 obj.class().name()
489 )));
490 }
491 };
492 *self.annotations.lock() = Some(annotations);
493 }
494 bytecode::MakeFunctionFlag::Closure => {
495 let closure_tuple = attr_value
496 .clone()
497 .downcast_exact::<PyTuple>(vm)
498 .map_err(|obj| {
499 vm.new_type_error(format!(
500 "closure must be a tuple, not {}",
501 obj.class().name()
502 ))
503 })?
504 .into_pyref();
505
506 self.closure = Some(closure_tuple.try_into_typed::<PyCell>(vm)?);
507 }
508 bytecode::MakeFunctionFlag::TypeParams => {
509 let type_params = attr_value.clone().downcast::<PyTuple>().map_err(|_| {
510 vm.new_type_error(format!(
511 "__type_params__ must be a tuple, not {}",
512 attr_value.class().name()
513 ))
514 })?;
515 *self.type_params.lock() = type_params;
516 }
517 bytecode::MakeFunctionFlag::Annotate => {
518 if !attr_value.is_callable() {
519 return Err(vm.new_type_error("__annotate__ must be callable"));
520 }
521 *self.annotate.lock() = Some(attr_value);
522 }
523 }
524 Ok(())
525 }
526}
527
528impl Py<PyFunction> {
529 pub(crate) fn is_optimized_for_call_specialization(&self) -> bool {
530 self.code.flags.contains(bytecode::CodeFlags::OPTIMIZED)
531 }
532
533 pub fn invoke_with_locals(
534 &self,
535 func_args: FuncArgs,
536 locals: Option<ArgMapping>,
537 vm: &VirtualMachine,
538 ) -> PyResult {
539 #[cfg(feature = "jit")]
540 if let Some(jitted_code) = self.jitted_code.lock().as_ref() {
541 use crate::convert::ToPyObject;
542 match jit::get_jit_args(self, &func_args, jitted_code, vm) {
543 Ok(args) => {
544 return Ok(args.invoke().to_pyobject(vm));
545 }
546 Err(err) => info!(
547 "jit: function `{}` is falling back to being interpreted because of the \
548 error: {}",
549 self.code.obj_name, err
550 ),
551 }
552 }
553
554 let code: PyRef<PyCode> = (*self.code).to_owned();
555
556 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
557 None
558 } else if let Some(locals) = locals {
559 Some(locals)
560 } else {
561 Some(ArgMapping::from_dict_exact(self.globals.clone()))
562 };
563
564 let is_gen = code.flags.contains(bytecode::CodeFlags::GENERATOR);
565 let is_coro = code.flags.contains(bytecode::CodeFlags::COROUTINE);
566 let use_datastack = !(is_gen || is_coro);
567
568 let frame = Frame::new(
570 code,
571 Scope::new(locals, self.globals.clone()),
572 self.builtins.clone(),
573 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
574 Some(self.to_owned().into()),
575 use_datastack,
576 vm,
577 )
578 .into_ref(&vm.ctx);
579
580 self.fill_locals_from_args(&frame, func_args, vm)?;
581 match (is_gen, is_coro) {
582 (true, false) => {
583 let obj = PyGenerator::new(frame.clone(), self.__name__(), self.__qualname__())
584 .into_pyobject(vm);
585 frame.set_generator(&obj);
586 Ok(obj)
587 }
588 (false, true) => {
589 let obj = PyCoroutine::new(frame.clone(), self.__name__(), self.__qualname__())
590 .into_pyobject(vm);
591 frame.set_generator(&obj);
592 Ok(obj)
593 }
594 (true, true) => {
595 let obj = PyAsyncGen::new(frame.clone(), self.__name__(), self.__qualname__())
596 .into_pyobject(vm);
597 frame.set_generator(&obj);
598 Ok(obj)
599 }
600 (false, false) => {
601 let result = vm.run_frame(frame.clone());
602 unsafe {
604 if let Some(base) = frame.materialize_localsplus() {
605 vm.datastack_pop(base);
606 }
607 }
608 result
609 }
610 }
611 }
612
613 #[inline(always)]
614 pub fn invoke(&self, func_args: FuncArgs, vm: &VirtualMachine) -> PyResult {
615 self.invoke_with_locals(func_args, None, vm)
616 }
617
618 #[inline]
620 pub fn func_version(&self) -> u32 {
621 self.func_version.load(Relaxed)
622 }
623
624 pub fn get_version_for_current_state(&self) -> u32 {
628 let v = self.func_version.load(Relaxed);
629 if v != 0 {
630 return v;
631 }
632 let new_v = next_func_version();
633 if new_v == 0 {
634 return 0;
635 }
636 self.func_version.store(new_v, Relaxed);
637 new_v
638 }
639
640 pub(crate) fn is_simple_for_call_specialization(&self) -> bool {
643 let code: &Py<PyCode> = &self.code;
644 let flags = code.flags;
645 flags.contains(bytecode::CodeFlags::OPTIMIZED)
646 && !flags.intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
647 && code.kwonlyarg_count == 0
648 }
649
650 pub(crate) fn can_specialize_call(&self, effective_nargs: u32) -> bool {
654 let code: &Py<PyCode> = &self.code;
655 let flags = code.flags;
656 flags.contains(bytecode::CodeFlags::OPTIMIZED)
657 && !flags.intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
658 && code.kwonlyarg_count == 0
659 && code.arg_count == effective_nargs
660 }
661
662 #[inline]
665 pub(crate) fn has_exact_argcount(&self, effective_nargs: u32) -> bool {
666 self.code.arg_count == effective_nargs
667 }
668
669 pub(crate) fn datastack_frame_size_bytes(&self) -> Option<usize> {
673 datastack_frame_size_bytes_for_code(&self.code)
674 }
675
676 pub(crate) fn prepare_exact_args_frame(
677 &self,
678 mut args: Vec<PyObjectRef>,
679 vm: &VirtualMachine,
680 ) -> FrameRef {
681 let code: PyRef<PyCode> = (*self.code).to_owned();
682
683 debug_assert_eq!(args.len(), code.arg_count as usize);
684 debug_assert!(code.flags.contains(bytecode::CodeFlags::OPTIMIZED));
685 debug_assert!(
686 !code
687 .flags
688 .intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
689 );
690 debug_assert_eq!(code.kwonlyarg_count, 0);
691 debug_assert!(
692 !code
693 .flags
694 .intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE)
695 );
696
697 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
698 None
699 } else {
700 Some(ArgMapping::from_dict_exact(self.globals.clone()))
701 };
702
703 let frame = Frame::new(
704 code.clone(),
705 Scope::new(locals, self.globals.clone()),
706 self.builtins.clone(),
707 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
708 Some(self.to_owned().into()),
709 true, vm,
711 )
712 .into_ref(&vm.ctx);
713
714 {
715 let fastlocals = unsafe { frame.fastlocals_mut() };
716 for (slot, arg) in fastlocals.iter_mut().zip(args.drain(..)) {
717 *slot = Some(arg);
718 }
719 }
720
721 frame
722 }
723
724 pub fn invoke_exact_args(&self, args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
729 let code: PyRef<PyCode> = (*self.code).to_owned();
730
731 debug_assert_eq!(args.len(), code.arg_count as usize);
732 debug_assert!(code.flags.contains(bytecode::CodeFlags::OPTIMIZED));
733 debug_assert!(
734 !code
735 .flags
736 .intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
737 );
738 debug_assert_eq!(code.kwonlyarg_count, 0);
739
740 if code
744 .flags
745 .intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE)
746 {
747 return self.invoke(FuncArgs::from(args), vm);
748 }
749 let frame = self.prepare_exact_args_frame(args, vm);
750
751 let result = vm.run_frame(frame.clone());
752 unsafe {
753 if let Some(base) = frame.materialize_localsplus() {
754 vm.datastack_pop(base);
755 }
756 }
757 result
758 }
759}
760
761pub(crate) fn datastack_frame_size_bytes_for_code(code: &Py<PyCode>) -> Option<usize> {
762 if code
763 .flags
764 .intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE)
765 {
766 return None;
767 }
768 let nlocalsplus = code.localspluskinds.len();
769 let capacity = nlocalsplus.checked_add(code.max_stackdepth as usize)?;
770 capacity.checked_mul(core::mem::size_of::<usize>())
771}
772
773impl PyPayload for PyFunction {
774 #[inline]
775 fn class(ctx: &Context) -> &'static Py<PyType> {
776 ctx.types.function_type
777 }
778}
779
780#[pyclass(
781 with(GetDescriptor, Callable, Representable, Constructor),
782 flags(HAS_DICT, HAS_WEAKREF, METHOD_DESCRIPTOR)
783)]
784impl PyFunction {
785 #[pygetset]
786 fn __code__(&self) -> PyRef<PyCode> {
787 (*self.code).to_owned()
788 }
789
790 #[pygetset(setter)]
791 fn set___code__(&self, code: PyRef<PyCode>, vm: &VirtualMachine) {
792 #[cfg(feature = "jit")]
793 let mut jit_guard = self.jitted_code.lock();
794 self.code.swap_to_temporary_refs(code, vm);
795 #[cfg(feature = "jit")]
796 {
797 *jit_guard = None;
798 }
799 self.func_version.store(0, Relaxed);
800 }
801
802 #[pygetset]
803 fn __defaults__(&self) -> Option<PyTupleRef> {
804 self.defaults_and_kwdefaults.lock().0.clone()
805 }
806 #[pygetset(setter)]
807 fn set___defaults__(&self, defaults: Option<PyTupleRef>) {
808 self.defaults_and_kwdefaults.lock().0 = defaults;
809 self.func_version.store(0, Relaxed);
810 }
811
812 #[pygetset]
813 fn __kwdefaults__(&self) -> Option<PyDictRef> {
814 self.defaults_and_kwdefaults.lock().1.clone()
815 }
816 #[pygetset(setter)]
817 fn set___kwdefaults__(&self, kwdefaults: Option<PyDictRef>) {
818 self.defaults_and_kwdefaults.lock().1 = kwdefaults;
819 self.func_version.store(0, Relaxed);
820 }
821
822 #[pymember]
828 fn __globals__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
829 let zelf = Self::_as_pyref(&zelf, vm)?;
830 Ok(zelf.globals.clone().into())
831 }
832
833 #[pymember]
834 fn __closure__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
835 let zelf = Self::_as_pyref(&zelf, vm)?;
836 Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.into())))
837 }
838
839 #[pymember]
840 fn __builtins__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
841 let zelf = Self::_as_pyref(&zelf, vm)?;
842 Ok(zelf.builtins.clone())
843 }
844
845 #[pygetset]
846 fn __name__(&self) -> PyStrRef {
847 self.name.lock().clone()
848 }
849
850 #[pygetset(setter)]
851 fn set___name__(&self, name: PyStrRef) {
852 *self.name.lock() = name;
853 }
854
855 #[pymember]
856 fn __doc__(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult {
857 if let Ok(func) = obj.downcast::<Self>() {
859 let doc = func.doc.lock();
860 Ok(doc.clone())
861 } else {
862 Ok(vm.ctx.none())
864 }
865 }
866
867 #[pymember(setter)]
868 fn set___doc__(vm: &VirtualMachine, zelf: PyObjectRef, value: PySetterValue) -> PyResult<()> {
869 let zelf: PyRef<Self> = zelf.downcast().unwrap_or_else(|_| unreachable!());
870 let value = value.unwrap_or_none(vm);
871 *zelf.doc.lock() = value;
872 Ok(())
873 }
874
875 #[pygetset]
876 fn __module__(&self) -> PyObjectRef {
877 self.module.lock().clone()
878 }
879
880 #[pygetset(setter)]
881 fn set___module__(&self, module: PySetterValue<PyObjectRef>, vm: &VirtualMachine) {
882 *self.module.lock() = module.unwrap_or_none(vm);
883 }
884
885 #[pygetset]
886 fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
887 {
889 let annotations = self.annotations.lock();
890 if let Some(ref ann) = *annotations {
891 return Ok(ann.clone());
892 }
893 }
894
895 let annotate_fn = {
897 let annotate = self.annotate.lock();
898 if let Some(ref func) = *annotate
899 && func.is_callable()
900 {
901 Some(func.clone())
902 } else {
903 None
904 }
905 };
906
907 if let Some(annotate_fn) = annotate_fn {
909 let one = vm.ctx.new_int(1);
910 let ann_dict = annotate_fn.call((one,), vm)?;
911 let ann_dict = ann_dict
912 .downcast::<crate::builtins::PyDict>()
913 .map_err(|obj| {
914 vm.new_type_error(format!(
915 "__annotate__ returned non-dict of type '{}'",
916 obj.class().name()
917 ))
918 })?;
919
920 *self.annotations.lock() = Some(ann_dict.clone());
922 return Ok(ann_dict);
923 }
924
925 let new_dict = vm.ctx.new_dict();
927 *self.annotations.lock() = Some(new_dict.clone());
928 Ok(new_dict)
929 }
930
931 #[pygetset(setter)]
932 fn set___annotations__(
933 &self,
934 value: PySetterValue<Option<PyObjectRef>>,
935 vm: &VirtualMachine,
936 ) -> PyResult<()> {
937 match value {
938 PySetterValue::Assign(Some(value)) => {
939 let annotations = value.downcast::<crate::builtins::PyDict>().map_err(|_| {
940 vm.new_type_error("__annotations__ must be set to a dict object")
941 })?;
942 *self.annotations.lock() = Some(annotations);
943 *self.annotate.lock() = None;
944 }
945 PySetterValue::Assign(None) => {
946 *self.annotations.lock() = None;
947 *self.annotate.lock() = None;
948 }
949 PySetterValue::Delete => {
950 *self.annotations.lock() = None;
952 }
953 }
954 Ok(())
955 }
956
957 #[pygetset]
958 fn __annotate__(&self, vm: &VirtualMachine) -> PyObjectRef {
959 self.annotate
960 .lock()
961 .clone()
962 .unwrap_or_else(|| vm.ctx.none())
963 }
964
965 #[pygetset(setter)]
966 fn set___annotate__(
967 &self,
968 value: PySetterValue<Option<PyObjectRef>>,
969 vm: &VirtualMachine,
970 ) -> PyResult<()> {
971 let annotate = match value {
972 PySetterValue::Assign(Some(value)) => {
973 if !value.is_callable() {
974 return Err(vm.new_type_error("__annotate__ must be callable or None"));
975 }
976 *self.annotations.lock() = None;
978 Some(value)
979 }
980 PySetterValue::Assign(None) => None,
981 PySetterValue::Delete => {
982 return Err(vm.new_type_error("__annotate__ cannot be deleted"));
983 }
984 };
985 *self.annotate.lock() = annotate;
986 Ok(())
987 }
988
989 #[pygetset]
990 fn __qualname__(&self) -> PyStrRef {
991 self.qualname.lock().clone()
992 }
993
994 #[pygetset(setter)]
995 fn set___qualname__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
996 match value {
997 PySetterValue::Assign(value) => {
998 let Ok(qualname) = value.downcast::<PyStr>() else {
999 return Err(vm.new_type_error("__qualname__ must be set to a string object"));
1000 };
1001 *self.qualname.lock() = qualname;
1002 }
1003 PySetterValue::Delete => {
1004 return Err(vm.new_type_error("__qualname__ must be set to a string object"));
1005 }
1006 }
1007 Ok(())
1008 }
1009
1010 #[pygetset]
1011 fn __type_params__(&self) -> PyTupleRef {
1012 self.type_params.lock().clone()
1013 }
1014
1015 #[pygetset(setter)]
1016 fn set___type_params__(
1017 &self,
1018 value: PySetterValue<PyTupleRef>,
1019 vm: &VirtualMachine,
1020 ) -> PyResult<()> {
1021 match value {
1022 PySetterValue::Assign(value) => {
1023 *self.type_params.lock() = value;
1024 }
1025 PySetterValue::Delete => {
1026 return Err(vm.new_type_error("__type_params__ must be set to a tuple object"));
1027 }
1028 }
1029 Ok(())
1030 }
1031
1032 #[cfg(feature = "jit")]
1033 #[pymethod]
1034 fn __jit__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<()> {
1035 let mut jit_guard = zelf.jitted_code.lock();
1036 if jit_guard.is_some() {
1037 return Ok(());
1038 }
1039 let arg_types = jit::get_jit_arg_types(&zelf, vm)?;
1040 let ret_type = jit::jit_ret_type(&zelf, vm)?;
1041 let code: &Py<PyCode> = &zelf.code;
1042 let compiled = rustpython_jit::compile(&code.code, &arg_types, ret_type)
1043 .map_err(|err| jit::new_jit_error(err.to_string(), vm))?;
1044 *jit_guard = Some(compiled);
1045 Ok(())
1046 }
1047}
1048
1049impl GetDescriptor for PyFunction {
1050 fn descr_get(
1051 zelf: PyObjectRef,
1052 obj: Option<PyObjectRef>,
1053 cls: Option<PyObjectRef>,
1054 vm: &VirtualMachine,
1055 ) -> PyResult {
1056 let (_zelf, obj) = Self::_unwrap(&zelf, obj, vm)?;
1057 Ok(if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) {
1058 zelf
1059 } else {
1060 PyBoundMethod::new(obj, zelf).into_ref(&vm.ctx).into()
1061 })
1062 }
1063}
1064
1065impl Callable for PyFunction {
1066 type Args = FuncArgs;
1067 #[inline]
1068 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1069 zelf.invoke(args, vm)
1070 }
1071}
1072
1073impl Representable for PyFunction {
1074 #[inline]
1075 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1076 Ok(format!(
1077 "<function {} at {:#x}>",
1078 zelf.__qualname__(),
1079 zelf.get_id()
1080 ))
1081 }
1082}
1083
1084#[derive(FromArgs)]
1085pub struct PyFunctionNewArgs {
1086 #[pyarg(positional)]
1087 code: PyRef<PyCode>,
1088 #[pyarg(positional)]
1089 globals: PyDictRef,
1090 #[pyarg(any, optional, error_msg = "arg 3 (name) must be None or string")]
1091 name: OptionalArg<PyStrRef>,
1092 #[pyarg(any, optional, error_msg = "arg 4 (defaults) must be None or tuple")]
1093 argdefs: Option<PyTupleRef>,
1094 #[pyarg(any, optional, error_msg = "arg 5 (closure) must be None or tuple")]
1095 closure: Option<PyTupleRef>,
1096 #[pyarg(any, optional, error_msg = "arg 6 (kwdefaults) must be None or dict")]
1097 kwdefaults: Option<PyDictRef>,
1098}
1099
1100impl Constructor for PyFunction {
1101 type Args = PyFunctionNewArgs;
1102
1103 fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
1104 let closure = if let Some(closure_tuple) = args.closure {
1106 if closure_tuple.len() != args.code.freevars.len() {
1108 return Err(vm.new_value_error(format!(
1109 "{} requires closure of length {}, not {}",
1110 args.code.obj_name,
1111 args.code.freevars.len(),
1112 closure_tuple.len()
1113 )));
1114 }
1115
1116 let typed_closure = closure_tuple.try_into_typed::<PyCell>(vm)?;
1118 Some(typed_closure)
1119 } else if !args.code.freevars.is_empty() {
1120 return Err(vm.new_type_error("arg 5 (closure) must be tuple"));
1121 } else {
1122 None
1123 };
1124
1125 let mut func = Self::new(args.code.clone(), args.globals.clone(), vm)?;
1126 if let Some(name) = args.name.into_option() {
1128 *func.name.lock() = name.clone();
1129 *func.qualname.lock() = name;
1131 }
1132 if let Some(closure_tuple) = closure {
1134 func.closure = Some(closure_tuple);
1135 }
1136 if let Some(argdefs) = args.argdefs {
1137 func.defaults_and_kwdefaults.lock().0 = Some(argdefs);
1138 }
1139 if let Some(kwdefaults) = args.kwdefaults {
1140 func.defaults_and_kwdefaults.lock().1 = Some(kwdefaults);
1141 }
1142
1143 Ok(func)
1144 }
1145}
1146
1147#[pyclass(module = false, name = "method", traverse)]
1148#[derive(Debug)]
1149pub struct PyBoundMethod {
1150 object: PyObjectRef,
1151 function: PyObjectRef,
1152}
1153
1154impl Callable for PyBoundMethod {
1155 type Args = FuncArgs;
1156 #[inline]
1157 fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1158 args.prepend_arg(zelf.object.clone());
1159 zelf.function.call(args, vm)
1160 }
1161}
1162
1163impl Comparable for PyBoundMethod {
1164 fn cmp(
1165 zelf: &Py<Self>,
1166 other: &PyObject,
1167 op: PyComparisonOp,
1168 _vm: &VirtualMachine,
1169 ) -> PyResult<PyComparisonValue> {
1170 op.eq_only(|| {
1171 let other = class_or_notimplemented!(Self, other);
1172 Ok(PyComparisonValue::Implemented(
1173 zelf.function.is(&other.function) && zelf.object.is(&other.object),
1174 ))
1175 })
1176 }
1177}
1178
1179impl Hashable for PyBoundMethod {
1180 fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
1181 let self_hash = crate::common::hash::hash_object_id_raw(zelf.object.get_id());
1182 let func_hash = zelf.function.hash(vm)?;
1183 Ok(crate::common::hash::fix_sentinel(self_hash ^ func_hash))
1184 }
1185}
1186
1187impl GetAttr for PyBoundMethod {
1188 fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
1189 let class_attr = vm
1190 .ctx
1191 .interned_str(name)
1192 .and_then(|attr_name| zelf.get_class_attr(attr_name));
1193 if let Some(obj) = class_attr {
1194 return vm.call_if_get_descriptor(&obj, zelf.to_owned().into());
1195 }
1196 zelf.function.get_attr(name, vm)
1197 }
1198}
1199
1200impl GetDescriptor for PyBoundMethod {
1201 fn descr_get(
1202 zelf: PyObjectRef,
1203 _obj: Option<PyObjectRef>,
1204 _cls: Option<PyObjectRef>,
1205 _vm: &VirtualMachine,
1206 ) -> PyResult {
1207 Ok(zelf)
1208 }
1209}
1210
1211#[derive(FromArgs)]
1212pub struct PyBoundMethodNewArgs {
1213 #[pyarg(positional)]
1214 function: PyObjectRef,
1215 #[pyarg(positional)]
1216 object: PyObjectRef,
1217}
1218
1219impl Constructor for PyBoundMethod {
1220 type Args = PyBoundMethodNewArgs;
1221
1222 fn py_new(
1223 _cls: &Py<PyType>,
1224 Self::Args { function, object }: Self::Args,
1225 vm: &VirtualMachine,
1226 ) -> PyResult<Self> {
1227 if !function.is_callable() {
1228 return Err(vm.new_type_error("first argument must be callable".to_owned()));
1229 }
1230 if vm.is_none(&object) {
1231 return Err(vm.new_type_error("instance must not be None".to_owned()));
1232 }
1233 Ok(Self::new(object, function))
1234 }
1235}
1236
1237impl PyBoundMethod {
1238 pub const fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
1239 Self { object, function }
1240 }
1241
1242 #[inline]
1243 pub(crate) fn function_obj(&self) -> &PyObjectRef {
1244 &self.function
1245 }
1246
1247 #[inline]
1248 pub(crate) fn self_obj(&self) -> &PyObjectRef {
1249 &self.object
1250 }
1251
1252 #[deprecated(note = "Use `Self::new(object, function).into_ref(ctx)` instead")]
1253 pub fn new_ref(object: PyObjectRef, function: PyObjectRef, ctx: &Context) -> PyRef<Self> {
1254 Self::new(object, function).into_ref(ctx)
1255 }
1256}
1257
1258#[pyclass(
1259 with(
1260 Callable,
1261 Comparable,
1262 Hashable,
1263 GetAttr,
1264 GetDescriptor,
1265 Constructor,
1266 Representable
1267 ),
1268 flags(IMMUTABLETYPE, HAS_WEAKREF)
1269)]
1270impl PyBoundMethod {
1271 #[pymethod]
1272 fn __reduce__(
1273 &self,
1274 vm: &VirtualMachine,
1275 ) -> PyResult<(PyObjectRef, (PyObjectRef, PyObjectRef))> {
1276 let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
1277 let func_self = self.object.clone();
1278 let func_name = self.function.get_attr("__name__", vm)?;
1279 Ok((builtins_getattr, (func_self, func_name)))
1280 }
1281
1282 #[pygetset]
1283 fn __doc__(&self, vm: &VirtualMachine) -> PyResult {
1284 self.function.get_attr("__doc__", vm)
1285 }
1286
1287 #[pygetset]
1288 fn __func__(&self) -> PyObjectRef {
1289 self.function.clone()
1290 }
1291
1292 #[pygetset(name = "__self__")]
1293 fn get_self(&self) -> PyObjectRef {
1294 self.object.clone()
1295 }
1296
1297 #[pygetset]
1298 fn __module__(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
1299 self.function.get_attr("__module__", vm).ok()
1300 }
1301}
1302
1303impl PyPayload for PyBoundMethod {
1304 #[inline]
1305 fn class(ctx: &Context) -> &'static Py<PyType> {
1306 ctx.types.bound_method_type
1307 }
1308}
1309
1310impl Representable for PyBoundMethod {
1311 #[inline]
1312 fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
1313 let func_name = if let Some(qname) =
1314 vm.get_attribute_opt(zelf.function.clone(), identifier!(vm, __qualname__))?
1315 {
1316 Some(qname)
1317 } else {
1318 vm.get_attribute_opt(zelf.function.clone(), identifier!(vm, __name__))?
1319 };
1320 let func_name: Option<PyStrRef> = func_name.and_then(|o| o.downcast().ok());
1321 let object_repr = zelf.object.repr(vm)?;
1322 let name = func_name.as_ref().map_or("?".as_ref(), |s| s.as_wtf8());
1323 Ok(wtf8_concat!(
1324 "<bound method ",
1325 name,
1326 " of ",
1327 object_repr.as_wtf8(),
1328 ">"
1329 ))
1330 }
1331}
1332
1333#[pyclass(module = false, name = "cell", traverse)]
1334#[derive(Debug, Default)]
1335pub(crate) struct PyCell {
1336 contents: PyMutex<Option<PyObjectRef>>,
1337}
1338pub(crate) type PyCellRef = PyRef<PyCell>;
1339
1340impl PyPayload for PyCell {
1341 #[inline]
1342 fn class(ctx: &Context) -> &'static Py<PyType> {
1343 ctx.types.cell_type
1344 }
1345}
1346
1347impl Constructor for PyCell {
1348 type Args = OptionalArg;
1349
1350 fn py_new(_cls: &Py<PyType>, value: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
1351 Ok(Self::new(value.into_option()))
1352 }
1353}
1354
1355#[pyclass(with(Constructor))]
1356impl PyCell {
1357 pub const fn new(contents: Option<PyObjectRef>) -> Self {
1358 Self {
1359 contents: PyMutex::new(contents),
1360 }
1361 }
1362
1363 pub fn get(&self) -> Option<PyObjectRef> {
1364 self.contents.lock().clone()
1365 }
1366 pub fn set(&self, x: Option<PyObjectRef>) {
1367 *self.contents.lock() = x;
1368 }
1369
1370 #[pygetset]
1371 fn cell_contents(&self, vm: &VirtualMachine) -> PyResult {
1372 self.get()
1373 .ok_or_else(|| vm.new_value_error("Cell is empty"))
1374 }
1375 #[pygetset(setter)]
1376 fn set_cell_contents(&self, x: PySetterValue) {
1377 match x {
1378 PySetterValue::Assign(value) => self.set(Some(value)),
1379 PySetterValue::Delete => self.set(None),
1380 }
1381 }
1382}
1383
1384pub(crate) fn vectorcall_function(
1387 zelf_obj: &PyObject,
1388 mut args: Vec<PyObjectRef>,
1389 nargs: usize,
1390 kwnames: Option<&[PyObjectRef]>,
1391 vm: &VirtualMachine,
1392) -> PyResult {
1393 let zelf: &Py<PyFunction> = zelf_obj.downcast_ref().unwrap();
1394 let code: &Py<PyCode> = &zelf.code;
1395
1396 let has_kwargs = kwnames.is_some_and(|kw| !kw.is_empty());
1397 let is_simple = !has_kwargs
1398 && code.flags.contains(bytecode::CodeFlags::OPTIMIZED)
1399 && !code.flags.contains(bytecode::CodeFlags::VARARGS)
1400 && !code.flags.contains(bytecode::CodeFlags::VARKEYWORDS)
1401 && code.kwonlyarg_count == 0
1402 && !code
1403 .flags
1404 .intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE);
1405
1406 if is_simple && nargs == code.arg_count as usize {
1407 args.truncate(nargs);
1410 let frame = zelf.prepare_exact_args_frame(args, vm);
1411
1412 let result = vm.run_frame(frame.clone());
1413 unsafe {
1414 if let Some(base) = frame.materialize_localsplus() {
1415 vm.datastack_pop(base);
1416 }
1417 }
1418 return result;
1419 }
1420
1421 let func_args = if has_kwargs {
1423 FuncArgs::from_vectorcall(&args, nargs, kwnames)
1424 } else {
1425 args.truncate(nargs);
1426 FuncArgs::from(args)
1427 };
1428 zelf.invoke(func_args, vm)
1429}
1430
1431fn vectorcall_bound_method(
1433 zelf_obj: &PyObject,
1434 mut args: Vec<PyObjectRef>,
1435 nargs: usize,
1436 kwnames: Option<&[PyObjectRef]>,
1437 vm: &VirtualMachine,
1438) -> PyResult {
1439 let zelf: &Py<PyBoundMethod> = zelf_obj.downcast_ref().unwrap();
1440
1441 args.insert(0, zelf.object.clone());
1444 let new_nargs = nargs + 1;
1445 zelf.function.vectorcall(args, new_nargs, kwnames, vm)
1446}
1447
1448pub fn init(context: &'static Context) {
1449 PyFunction::extend_class(context, context.types.function_type);
1450 context
1451 .types
1452 .function_type
1453 .slots
1454 .vectorcall
1455 .store(Some(vectorcall_function));
1456
1457 PyBoundMethod::extend_class(context, context.types.bound_method_type);
1458 context
1459 .types
1460 .bound_method_type
1461 .slots
1462 .vectorcall
1463 .store(Some(vectorcall_bound_method));
1464
1465 PyCell::extend_class(context, context.types.cell_type);
1466}