1#[cfg(feature = "jit")]
2mod jit;
3
4use super::{
5 PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyList, PyModule, PyStr, PyStrRef,
6 PyTuple, PyTupleRef, PyType, object,
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::{FrameObject, FrameObjectRef},
18 function::{Either, 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
37 let last = if missing.len() > 1 {
38 missing.pop()
39 } else {
40 None
41 };
42
43 let (and, right): (&str, String) = if let Some(last) = last {
44 (
45 if missing.len() == 1 {
46 "' and '"
47 } else {
48 "', and '"
49 },
50 last.to_string(),
51 )
52 } else {
53 ("", String::new())
54 };
55
56 format!(
57 "{qualname}() missing {count} required {kind} argument{}: '{}{}{right}'",
58 if count == 1 { "" } else { "s" },
59 missing.iter().join("', '"),
60 and,
61 )
62}
63
64#[pyclass(module = false, name = "function", traverse = "manual")]
65#[derive(Debug)]
66pub struct PyFunction {
67 pub(crate) code: PyAtomicRef<PyCode>,
68 #[pymember(name = "__globals__")]
69 pub(crate) globals: PyDictRef,
70 #[pymember(name = "__builtins__")]
71 pub(crate) builtins: PyObjectRef,
72 #[pymember(name = "__closure__")]
73 pub(crate) closure: Option<PyRef<PyTuple<PyCellRef>>>,
74 defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
75 name: PyMutex<PyStrRef>,
76 qualname: PyMutex<PyStrRef>,
77 type_params: PyMutex<PyTupleRef>,
78 annotations: PyMutex<Option<PyDictRef>>,
79 annotate: PyMutex<Option<PyObjectRef>>,
80 #[pymember(name = "__module__", writable)]
81 module: PyAtomicRef<Option<PyObject>>,
82 #[pymember(name = "__doc__", writable)]
83 doc: PyAtomicRef<Option<PyObject>>,
84 func_version: AtomicU32,
85 #[cfg(feature = "jit")]
86 jitted_code: PyMutex<Option<CompiledCode>>,
87}
88
89static FUNC_VERSION_COUNTER: AtomicU32 = AtomicU32::new(1);
90
91fn next_func_version() -> u32 {
94 FUNC_VERSION_COUNTER
95 .try_update(Relaxed, Relaxed, |v| (v != 0).then(|| v.wrapping_add(1)))
96 .unwrap_or(0)
97}
98
99unsafe impl Traverse for PyFunction {
100 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
101 self.globals.traverse(tracer_fn);
102 if let Some(closure) = self.closure.as_ref() {
103 tracer_fn(closure.as_untyped().as_object());
109 }
110 self.defaults_and_kwdefaults.traverse(tracer_fn);
111 self.type_params.lock().traverse(tracer_fn);
113 self.annotations.lock().traverse(tracer_fn);
114 self.annotate.lock().traverse(tracer_fn);
115 self.module.traverse(tracer_fn);
116 self.doc.traverse(tracer_fn);
117 self.name.lock().traverse(tracer_fn);
118 self.qualname.lock().traverse(tracer_fn);
119 }
120
121 fn clear(&mut self, out: &mut Vec<crate::PyObjectRef>) {
122 if let Some(closure) = self.closure.take() {
124 out.push(closure.into());
125 }
126
127 if let Some(mut guard) = self.defaults_and_kwdefaults.try_lock() {
129 if let Some(defaults) = guard.0.take() {
130 out.push(defaults.into());
131 }
132 if let Some(kwdefaults) = guard.1.take() {
133 out.push(kwdefaults.into());
134 }
135 }
136
137 if let Some(mut guard) = self.annotations.try_lock()
139 && let Some(annotations) = guard.take()
140 {
141 out.push(annotations.into());
142 }
143 if let Some(mut guard) = self.annotate.try_lock()
144 && let Some(annotate) = guard.take()
145 {
146 out.push(annotate);
147 }
148
149 if let Some(old_module) = self.module.store(Some(Context::genesis().none())) {
151 out.push(old_module);
152 }
153 if let Some(old_doc) = self.doc.store(Some(Context::genesis().none())) {
154 out.push(old_doc);
155 }
156 if let Some(mut guard) = self.type_params.try_lock() {
157 let old_type_params =
158 core::mem::replace(&mut *guard, Context::genesis().empty_tuple.to_owned());
159 out.push(old_type_params.into());
160 }
161
162 if let Some(mut guard) = self.name.try_lock() {
165 let old_name = core::mem::replace(&mut *guard, Context::genesis().empty_str.to_owned());
166 out.push(old_name.into());
167 }
168 if let Some(mut guard) = self.qualname.try_lock() {
169 let old_qualname =
170 core::mem::replace(&mut *guard, Context::genesis().empty_str.to_owned());
171 out.push(old_qualname.into());
172 }
173
174 }
176}
177
178impl PyFunction {
179 #[inline]
180 pub(crate) fn new(
181 code: PyRef<PyCode>,
182 globals: PyDictRef,
183 vm: &VirtualMachine,
184 ) -> PyResult<Self> {
185 let name = PyMutex::new(code.obj_name.to_owned());
186 let module = vm.unwrap_or_none(globals.get_item_opt(identifier!(vm, __name__), vm)?);
187 let builtins = globals.get_item("__builtins__", vm).unwrap_or_else(|_| {
188 crate::frame::current_builtins().unwrap_or_else(|| vm.builtins.dict().into())
190 });
191 let builtins = if let Some(module) = builtins.downcast_ref::<PyModule>() {
193 module.dict().into()
194 } else {
195 builtins
196 };
197
198 let doc = if code.code.flags.contains(bytecode::CodeFlags::HAS_DOCSTRING) {
200 code.code
201 .constants
202 .first()
203 .map_or_else(|| vm.ctx.none(), |c| c.as_object().to_owned())
204 } else {
205 vm.ctx.none()
206 };
207
208 let qualname = vm.ctx.new_str(code.qualname.as_str());
209 let func = Self {
210 code: PyAtomicRef::from(code),
211 globals,
212 builtins,
213 closure: None,
214 defaults_and_kwdefaults: PyMutex::new((None, None)),
215 name,
216 qualname: PyMutex::new(qualname),
217 type_params: PyMutex::new(vm.ctx.empty_tuple.clone()),
218 annotations: PyMutex::new(None),
219 annotate: PyMutex::new(None),
220 module: PyAtomicRef::from(Some(module)),
221 doc: PyAtomicRef::from(Some(doc)),
222 func_version: AtomicU32::new(next_func_version()),
223 #[cfg(feature = "jit")]
224 jitted_code: PyMutex::new(None),
225 };
226 Ok(func)
227 }
228
229 fn fill_locals_from_args(
230 &self,
231 frame: &FrameObject,
232 func_args: FuncArgs,
233 vm: &VirtualMachine,
234 ) -> PyResult<()> {
235 let fastlocals = unsafe { frame.fastlocals_mut() };
237 self.fill_locals_from_args_inner(fastlocals, func_args, vm)
238 }
239
240 fn fill_locals_from_args_iframe(
241 &self,
242 iframe: &mut crate::frame::InterpreterFrame,
243 func_args: FuncArgs,
244 vm: &VirtualMachine,
245 ) -> PyResult<()> {
246 let fastlocals = iframe.localsplus.fastlocals_mut();
247 self.fill_locals_from_args_inner(fastlocals, func_args, vm)
248 }
249
250 fn fill_locals_from_args_inner(
251 &self,
252 fastlocals: &mut [Option<PyObjectRef>],
253 func_args: FuncArgs,
254 vm: &VirtualMachine,
255 ) -> PyResult<()> {
256 let code: &Py<PyCode> = &self.code;
257 let nargs = func_args.args.len();
258 let n_expected_args = code.arg_count as usize;
259 let total_args = code.arg_count as usize + code.kwonlyarg_count as usize;
260
261 let mut args_iter = func_args.args.into_iter();
262
263 for (local, arg) in Iterator::zip(
267 fastlocals.iter_mut().take(n_expected_args),
268 args_iter.by_ref().take(nargs),
269 ) {
270 *local = Some(arg);
271 }
272
273 let mut vararg_offset = total_args;
274 let too_many_positional = if code.flags.contains(bytecode::CodeFlags::VARARGS) {
276 let vararg_value = vm.ctx.new_tuple(args_iter.collect());
277 fastlocals[vararg_offset] = Some(vararg_value.into());
278 vararg_offset += 1;
279 false
280 } else {
281 nargs > n_expected_args
282 };
283
284 let kw_only_given = if too_many_positional && code.kwonlyarg_count > 0 {
288 let start = code.arg_count as usize;
289 let end = start + code.kwonlyarg_count as usize;
290 code.varnames[start..end]
291 .iter()
292 .filter(|name| func_args.kwargs.contains_key(name.as_str()))
293 .count()
294 } else {
295 0
296 };
297
298 let kwargs = if code.flags.contains(bytecode::CodeFlags::VARKEYWORDS) {
300 let d = vm.ctx.new_dict();
301 fastlocals[vararg_offset] = Some(d.clone().into());
302 Some(d)
303 } else {
304 None
305 };
306
307 let arg_pos = |range: core::ops::Range<_>, name: &str| {
308 code.varnames
309 .iter()
310 .enumerate()
311 .skip(range.start)
312 .take(range.end - range.start)
313 .find(|(_, s)| s.as_str() == name)
314 .map(|(p, _)| p)
315 };
316
317 let mut kwargs_iter = func_args.kwargs.into_iter();
319 while let Some((name, value)) = kwargs_iter.next() {
320 let name_str = name.as_str().ok();
323 if let Some(pos) =
325 name_str.and_then(|s| arg_pos(code.posonlyarg_count as usize..total_args, s))
326 {
327 let slot = &mut fastlocals[pos];
328 if slot.is_some() {
329 return Err(vm.new_type_error(format!(
330 "{}() got multiple values for argument '{}'",
331 self.qualname.lock().clone(),
332 name
333 )));
334 }
335 *slot = Some(value);
336 } else if let Some(kwargs) = kwargs.as_ref() {
337 kwargs.set_item(&name, value, vm)?;
338 } else {
339 let is_posonly = |name: &Wtf8Buf| {
343 name.as_str()
344 .is_ok_and(|s| arg_pos(0..code.posonlyarg_count as usize, s).is_some())
345 };
346 let mut posonly: Vec<_> = is_posonly(&name)
347 .then(|| name.clone())
348 .into_iter()
349 .collect();
350 posonly.extend(kwargs_iter.map(|(name, _)| name).filter(is_posonly));
351 if !posonly.is_empty() {
352 return Err(vm.new_type_error(format!(
353 "{}() got some positional-only arguments passed as keyword arguments: '{}'",
354 self.qualname.lock().clone(),
355 posonly.into_iter().format(", "),
356 )));
357 }
358 return Err(vm.new_type_error(format!(
359 "{}() got an unexpected keyword argument '{}'",
360 self.qualname.lock().clone(),
361 name
362 )));
363 }
364 }
365 if too_many_positional {
368 let n_defaults = self
369 .defaults_and_kwdefaults
370 .lock()
371 .0
372 .as_ref()
373 .map_or(0, |d| d.as_slice().len());
374 let n_required = n_expected_args - n_defaults;
375 let (takes_msg, plural) = if n_defaults > 0 {
376 (format!("from {n_required} to {n_expected_args}"), true)
377 } else {
378 (n_expected_args.to_string(), n_expected_args != 1)
379 };
380
381 let given_msg = if kw_only_given > 0 {
382 format!(
383 "{} positional argument{} (and {} keyword-only argument{}) were",
384 nargs,
385 if nargs == 1 { "" } else { "s" },
386 kw_only_given,
387 if kw_only_given == 1 { "" } else { "s" },
388 )
389 } else {
390 format!("{} {}", nargs, if nargs == 1 { "was" } else { "were" })
391 };
392
393 return Err(vm.new_type_error(format!(
394 "{}() takes {} positional argument{} but {} given",
395 self.qualname.lock().clone(),
396 takes_msg,
397 if plural { "s" } else { "" },
398 given_msg,
399 )));
400 }
401
402 let mut defaults_and_kwdefaults = None;
403 macro_rules! get_defaults {
405 () => {{
406 defaults_and_kwdefaults
407 .get_or_insert_with(|| self.defaults_and_kwdefaults.lock().clone())
408 }};
409 }
410
411 if nargs < n_expected_args {
414 let defaults = get_defaults!().0.as_ref().map(|tup| tup.as_slice());
415 let n_defs = defaults.map_or(0, |d| d.len());
416
417 let n_required = code.arg_count as usize - n_defs;
418
419 let mut missing: Vec<_> = (nargs..n_required)
422 .filter_map(|i| {
423 if fastlocals[i].is_none() {
424 Some(&code.varnames[i])
425 } else {
426 None
427 }
428 })
429 .collect();
430
431 if !missing.is_empty() {
432 return Err(vm.new_type_error(format_missing_args(
433 self.qualname.lock().clone(),
434 "positional",
435 &mut missing,
436 )));
437 }
438
439 if let Some(defaults) = defaults {
440 let n = core::cmp::min(nargs, n_expected_args);
441 let i = n.saturating_sub(n_required);
442
443 for i in i..defaults.len() {
446 let slot = &mut fastlocals[n_required + i];
447 if slot.is_none() {
448 *slot = Some(defaults[i].clone());
449 }
450 }
451 }
452 };
453
454 if code.kwonlyarg_count > 0 {
455 let mut missing = Vec::new();
456 for (slot, kwarg) in fastlocals
458 .iter_mut()
459 .zip(&*code.varnames)
460 .skip(code.arg_count as usize)
461 .take(code.kwonlyarg_count as usize)
462 .filter(|(slot, _)| slot.is_none())
463 {
464 if let Some(defaults) = &get_defaults!().1
465 && let Some(default) = defaults.get_item_opt(&**kwarg, vm)?
466 {
467 *slot = Some(default);
468 continue;
469 }
470
471 missing.push(kwarg);
473 }
474
475 if !missing.is_empty() {
476 return Err(vm.new_type_error(format_missing_args(
477 self.qualname.lock().clone(),
478 "keyword-only",
479 &mut missing,
480 )));
481 }
482 }
483
484 Ok(())
485 }
486
487 pub(crate) fn set_function_attribute(
489 &mut self,
490 attr: bytecode::MakeFunctionFlag,
491 attr_value: PyObjectRef,
492 vm: &VirtualMachine,
493 ) -> PyResult<()> {
494 use crate::builtins::PyDict;
495 match attr {
496 bytecode::MakeFunctionFlag::Defaults => {
497 let defaults = match attr_value.downcast::<PyTuple>() {
498 Ok(tuple) => tuple,
499 Err(obj) => {
500 return Err(vm.new_type_error(format!(
501 "__defaults__ must be a tuple, not {}",
502 obj.class().name()
503 )));
504 }
505 };
506 self.defaults_and_kwdefaults.lock().0 = Some(defaults);
507 }
508 bytecode::MakeFunctionFlag::KwOnlyDefaults => {
509 let kwdefaults = match attr_value.downcast::<PyDict>() {
510 Ok(dict) => dict,
511 Err(obj) => {
512 return Err(vm.new_type_error(format!(
513 "__kwdefaults__ must be a dict, not {}",
514 obj.class().name()
515 )));
516 }
517 };
518 self.defaults_and_kwdefaults.lock().1 = Some(kwdefaults);
519 }
520 bytecode::MakeFunctionFlag::Annotations => {
521 let annotations = match attr_value.downcast::<PyDict>() {
522 Ok(dict) => dict,
523 Err(obj) => {
524 return Err(vm.new_type_error(format!(
525 "__annotations__ must be a dict, not {}",
526 obj.class().name()
527 )));
528 }
529 };
530 *self.annotations.lock() = Some(annotations);
531 }
532 bytecode::MakeFunctionFlag::Closure => {
533 let closure_tuple = attr_value
534 .downcast_exact::<PyTuple>(vm)
535 .map_err(|obj| {
536 vm.new_type_error(format!(
537 "closure must be a tuple, not {}",
538 obj.class().name()
539 ))
540 })?
541 .into_pyref();
542
543 let typed = closure_tuple.try_into_typed::<PyCell>(vm)?;
544 self.closure = Some(typed);
545 }
546 bytecode::MakeFunctionFlag::TypeParams => {
547 let type_params = attr_value.clone().downcast::<PyTuple>().map_err(|_| {
548 vm.new_type_error(format!(
549 "__type_params__ must be a tuple, not {}",
550 attr_value.class().name()
551 ))
552 })?;
553 *self.type_params.lock() = type_params;
554 }
555 bytecode::MakeFunctionFlag::Annotate => {
556 if !attr_value.is_callable() {
557 return Err(vm.new_type_error("__annotate__ must be callable"));
558 }
559 *self.annotate.lock() = Some(attr_value);
560 }
561 }
562 Ok(())
563 }
564}
565
566impl Py<PyFunction> {
567 pub(crate) fn is_optimized_for_call_specialization(&self) -> bool {
568 self.code.flags.contains(bytecode::CodeFlags::OPTIMIZED)
569 }
570
571 #[inline]
574 pub(crate) fn is_jitted(&self) -> bool {
575 #[cfg(feature = "jit")]
576 {
577 self.jitted_code.lock().is_some()
578 }
579 #[cfg(not(feature = "jit"))]
580 {
581 false
582 }
583 }
584
585 pub fn invoke_with_locals(
586 &self,
587 func_args: FuncArgs,
588 locals: Option<ArgMapping>,
589 vm: &VirtualMachine,
590 ) -> PyResult {
591 #[cfg(feature = "jit")]
592 if let Some(jitted_code) = self.jitted_code.lock().as_ref() {
593 use crate::convert::ToPyObject;
594 match jit::get_jit_args(self, &func_args, jitted_code, vm) {
595 Ok(args) => {
596 return Ok(args.invoke().to_pyobject(vm));
597 }
598 Err(err) => info!(
599 "jit: function `{}` is falling back to being interpreted because of the \
600 error: {}",
601 self.code.obj_name, err
602 ),
603 }
604 }
605
606 let code = &*self.code;
607
608 let is_gen = code.flags.contains(bytecode::CodeFlags::GENERATOR);
609 let is_coro = code.flags.contains(bytecode::CodeFlags::COROUTINE);
610 let is_async_gen = code.flags.contains(bytecode::CodeFlags::ASYNC_GENERATOR);
611
612 let needs_heap_frame = is_gen || is_coro || is_async_gen || vm.use_tracing.get();
613
614 if needs_heap_frame {
615 let code_owned: PyRef<PyCode> = code.to_owned();
619 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
620 None
621 } else if let Some(locals) = locals {
622 Some(locals)
623 } else {
624 Some(ArgMapping::from_dict_exact(self.globals.clone()))
625 };
626 let use_datastack = !is_gen && !is_coro && !is_async_gen;
627 let frame = FrameObject::new_ref(
628 code_owned,
629 Scope::new(locals, self.globals.clone()),
630 self.builtins.clone(),
631 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
632 Some(self.to_owned().into()),
633 use_datastack,
634 vm,
635 );
636 self.fill_locals_from_args(&frame, func_args, vm)?;
637 if is_gen || is_coro || is_async_gen {
638 return Ok(self.make_generator_or_coro(frame, vm));
639 }
640 let result = vm.run_frame(frame.clone());
642 unsafe {
643 if let Some(base) = frame.iframe_mut().localsplus.release_datastack() {
644 vm.datastack_pop(base);
645 }
646 }
647 return result;
648 }
649
650 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
653 crate::frame::FrameLocals::lazy()
654 } else if let Some(locals) = locals {
655 crate::frame::FrameLocals::with_locals(locals)
656 } else {
657 crate::frame::FrameLocals::with_locals(crate::function::ArgMapping::from_dict_exact(
658 self.globals.clone(),
659 ))
660 };
661
662 let iframe = unsafe {
665 crate::frame::InterpreterFrame::new_on_datastack(
668 &self.code,
669 &self.globals,
670 &self.builtins,
671 Some(self.as_object()),
672 locals,
673 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
674 vm,
675 )
676 };
677 let result = self
678 .fill_locals_from_args_iframe(iframe, func_args, vm)
679 .and_then(|()| vm.run_frame_fast(iframe));
680 unsafe {
682 if let Some((base, size)) = iframe.release_datastack_frame() {
683 vm.datastack_pop_frame(base, size);
684 }
685 }
686 result
687 }
688
689 fn make_generator_or_coro(&self, frame: FrameObjectRef, vm: &VirtualMachine) -> PyObjectRef {
691 let code = frame.iframe().code();
692 let is_async_gen = code.flags.contains(bytecode::CodeFlags::ASYNC_GENERATOR);
693 let is_gen = code.flags.contains(bytecode::CodeFlags::GENERATOR);
694
695 let obj = if is_async_gen {
696 PyAsyncGen::new(frame.clone(), self.__name__(), self.__qualname__()).into_pyobject(vm)
697 } else if is_gen {
698 PyGenerator::new(frame.clone(), self.__name__(), self.__qualname__()).into_pyobject(vm)
699 } else {
700 let origin = crate::coroutine::compute_cr_origin(vm);
701 PyCoroutine::new(frame.clone(), self.__name__(), self.__qualname__(), origin)
702 .into_pyobject(vm)
703 };
704 debug_assert!(
705 !frame.localsplus_is_datastack_backed(),
706 "generator frame is data-stack-backed"
707 );
708 unsafe {
712 crate::gc_state::track_new_pair(
713 core::ptr::NonNull::from(obj.as_object()),
714 core::ptr::NonNull::from(frame.as_object()),
715 );
716 }
717 frame.set_generator(&obj);
718 obj
719 }
720
721 #[inline(always)]
722 pub fn invoke(&self, func_args: FuncArgs, vm: &VirtualMachine) -> PyResult {
723 self.invoke_with_locals(func_args, None, vm)
724 }
725
726 #[inline]
728 pub fn func_version(&self) -> u32 {
729 self.func_version.load(Relaxed)
730 }
731
732 pub fn get_version_for_current_state(&self) -> u32 {
736 let v = self.func_version.load(Relaxed);
737 if v != 0 {
738 return v;
739 }
740 let new_v = next_func_version();
741 if new_v == 0 {
742 return 0;
743 }
744 self.func_version.store(new_v, Relaxed);
745 new_v
746 }
747
748 pub(crate) fn can_specialize_call(&self, effective_nargs: u32) -> bool {
752 let code: &Py<PyCode> = &self.code;
753 let flags = code.flags;
754 flags.contains(bytecode::CodeFlags::OPTIMIZED)
755 && !flags.intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
756 && code.kwonlyarg_count == 0
757 && code.arg_count == effective_nargs
758 }
759
760 #[inline]
762 pub(crate) fn is_generator_like(&self) -> bool {
763 self.code.flags.intersects(
764 bytecode::CodeFlags::GENERATOR
765 | bytecode::CodeFlags::COROUTINE
766 | bytecode::CodeFlags::ASYNC_GENERATOR,
767 )
768 }
769
770 #[inline]
773 pub(crate) fn has_exact_argcount(&self, effective_nargs: u32) -> bool {
774 self.code.arg_count == effective_nargs
775 }
776
777 pub(crate) fn datastack_frame_size_bytes(&self) -> Option<usize> {
781 datastack_frame_size_bytes_for_code(&self.code)
782 }
783
784 pub(crate) fn prepare_exact_args_frame(
785 &self,
786 args: impl ExactSizeIterator<Item = PyObjectRef>,
787 vm: &VirtualMachine,
788 ) -> FrameObjectRef {
789 let code: PyRef<PyCode> = (*self.code).to_owned();
790
791 debug_assert_eq!(args.len(), code.arg_count as usize);
792 debug_assert!(code.flags.contains(bytecode::CodeFlags::OPTIMIZED));
793 debug_assert!(
794 !code
795 .flags
796 .intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
797 );
798 debug_assert_eq!(code.kwonlyarg_count, 0);
799 debug_assert!(!code.flags.intersects(
800 bytecode::CodeFlags::GENERATOR
801 | bytecode::CodeFlags::COROUTINE
802 | bytecode::CodeFlags::ASYNC_GENERATOR,
803 ));
804
805 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
806 None
807 } else {
808 Some(ArgMapping::from_dict_exact(self.globals.clone()))
809 };
810
811 let frame = FrameObject::new_ref(
812 code,
813 Scope::new(locals, self.globals.clone()),
814 self.builtins.clone(),
815 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
816 Some(self.to_owned().into()),
817 true, vm,
819 );
820
821 {
822 let fastlocals = unsafe { frame.fastlocals_mut() };
823 for (slot, arg) in fastlocals.iter_mut().zip(args) {
824 *slot = Some(arg);
825 }
826 }
827
828 frame
829 }
830
831 pub(crate) fn make_generator_exact_args(
842 &self,
843 args: impl ExactSizeIterator<Item = PyObjectRef>,
844 vm: &VirtualMachine,
845 ) -> PyObjectRef {
846 let code: PyRef<PyCode> = (*self.code).to_owned();
847
848 debug_assert_eq!(args.len(), code.arg_count as usize);
849 debug_assert!(code.flags.contains(bytecode::CodeFlags::OPTIMIZED));
850 debug_assert!(
851 !code
852 .flags
853 .intersects(bytecode::CodeFlags::VARARGS | bytecode::CodeFlags::VARKEYWORDS)
854 );
855 debug_assert_eq!(code.kwonlyarg_count, 0);
856 debug_assert!(code.flags.intersects(
857 bytecode::CodeFlags::GENERATOR
858 | bytecode::CodeFlags::COROUTINE
859 | bytecode::CodeFlags::ASYNC_GENERATOR,
860 ));
861
862 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
863 None
864 } else {
865 Some(ArgMapping::from_dict_exact(self.globals.clone()))
866 };
867
868 let frame = FrameObject::new_ref(
870 code,
871 Scope::new(locals, self.globals.clone()),
872 self.builtins.clone(),
873 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
874 Some(self.to_owned().into()),
875 false,
876 vm,
877 );
878
879 {
880 let fastlocals = unsafe { frame.fastlocals_mut() };
882 for (slot, arg) in fastlocals.iter_mut().zip(args) {
883 *slot = Some(arg);
884 }
885 }
886
887 self.make_generator_or_coro(frame, vm)
888 }
889
890 pub(crate) fn invoke_prepared_exact_args(
891 &self,
892 args: impl ExactSizeIterator<Item = PyObjectRef>,
893 vm: &VirtualMachine,
894 ) -> PyResult {
895 let code = &*self.code;
896
897 let locals = if code.flags.contains(bytecode::CodeFlags::NEWLOCALS) {
898 crate::frame::FrameLocals::lazy()
899 } else {
900 crate::frame::FrameLocals::with_locals(ArgMapping::from_dict_exact(
901 self.globals.clone(),
902 ))
903 };
904
905 let iframe = unsafe {
906 crate::frame::InterpreterFrame::new_on_datastack(
909 code,
910 &self.globals,
911 &self.builtins,
912 Some(self.as_object()),
913 locals,
914 self.closure.as_ref().map_or(&[], |c| c.as_slice()),
915 vm,
916 )
917 };
918
919 {
921 let fastlocals = iframe.localsplus.fastlocals_mut();
922 for (slot, arg) in fastlocals.iter_mut().zip(args) {
923 *slot = Some(arg);
924 }
925 }
926
927 let result = vm.run_frame_fast(iframe);
928 unsafe {
929 if let Some((base, size)) = iframe.release_datastack_frame() {
930 vm.datastack_pop_frame(base, size);
931 }
932 }
933 result
934 }
935
936 pub fn invoke_exact_args(&self, args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
941 debug_assert_eq!(args.len(), self.code.arg_count as usize);
942
943 if self.is_generator_like() {
947 return Ok(self.make_generator_exact_args(args.into_iter(), vm));
948 }
949 self.invoke_prepared_exact_args(args.into_iter(), vm)
950 }
951
952 pub(crate) fn invoke_exact_args_slots(
956 &self,
957 args: &mut [Option<PyObjectRef>],
958 vm: &VirtualMachine,
959 ) -> PyResult {
960 debug_assert_eq!(args.len(), self.code.arg_count as usize);
961
962 let taken = args
963 .iter_mut()
964 .map(|slot| slot.take().expect("arg slot must be filled"));
965 if self.is_generator_like() {
969 return Ok(self.make_generator_exact_args(taken, vm));
970 }
971 self.invoke_prepared_exact_args(taken, vm)
972 }
973}
974
975pub(crate) fn datastack_frame_size_bytes_for_code(code: &Py<PyCode>) -> Option<usize> {
976 if code.flags.intersects(
977 bytecode::CodeFlags::GENERATOR
978 | bytecode::CodeFlags::COROUTINE
979 | bytecode::CodeFlags::ASYNC_GENERATOR,
980 ) {
981 return None;
982 }
983 let nlocalsplus = code.localspluskinds.len();
984 Some(crate::frame::datastack_iframe_total_bytes(
985 nlocalsplus,
986 code.max_stackdepth as usize,
987 ))
988}
989
990impl PyPayload for PyFunction {
991 #[inline]
992 fn class(ctx: &Context) -> &'static Py<PyType> {
993 ctx.types.function_type
994 }
995}
996
997#[pyclass(
998 with(GetDescriptor, Callable, Representable, Constructor),
999 flags(HAS_DICT, HAS_WEAKREF, METHOD_DESCRIPTOR)
1000)]
1001impl Py<PyFunction> {
1002 #[pygetset]
1003 fn __code__(&self) -> PyRef<PyCode> {
1004 (*self.code).to_owned()
1005 }
1006
1007 #[pygetset(setter)]
1008 fn set___code__(&self, code: PyRef<PyCode>, vm: &VirtualMachine) -> PyResult<()> {
1009 let n_free = code.freevars.len();
1010 let n_closure = self.closure.as_ref().map_or(0, |c| c.as_slice().len());
1011 if n_closure != n_free {
1012 return Err(vm.new_value_error(format!(
1013 "{}() requires a code object with {} free vars, not {}",
1014 self.qualname.lock(),
1015 n_closure,
1016 n_free,
1017 )));
1018 }
1019 #[cfg(feature = "jit")]
1020 let mut jit_guard = self.jitted_code.lock();
1021 self.code.swap_to_temporary_refs(code, vm);
1022 #[cfg(feature = "jit")]
1023 {
1024 *jit_guard = None;
1025 }
1026 self.func_version.store(0, Relaxed);
1027 crate::stdlib::_testinternalcapi::note_func_modification();
1028 Ok(())
1029 }
1030
1031 #[pygetset]
1032 pub(crate) fn __defaults__(&self) -> Option<PyTupleRef> {
1033 self.defaults_and_kwdefaults.lock().0.clone()
1034 }
1035 #[pygetset(setter)]
1036 fn set___defaults__(&self, defaults: PySetterValue<Option<PyTupleRef>>) {
1037 self.defaults_and_kwdefaults.lock().0 = match defaults {
1038 PySetterValue::Assign(d) => d,
1039 PySetterValue::Delete => None,
1040 };
1041 self.func_version.store(0, Relaxed);
1042 crate::stdlib::_testinternalcapi::note_func_modification();
1043 }
1044
1045 #[pygetset]
1046 pub(crate) fn __kwdefaults__(&self) -> Option<PyDictRef> {
1047 self.defaults_and_kwdefaults.lock().1.clone()
1048 }
1049 #[pygetset(setter)]
1050 fn set___kwdefaults__(&self, kwdefaults: PySetterValue<Option<PyDictRef>>) {
1051 self.defaults_and_kwdefaults.lock().1 = match kwdefaults {
1052 PySetterValue::Assign(d) => d,
1053 PySetterValue::Delete => None,
1054 };
1055 self.func_version.store(0, Relaxed);
1056 crate::stdlib::_testinternalcapi::note_func_modification();
1057 }
1058
1059 #[pygetset]
1060 fn __name__(&self) -> PyStrRef {
1061 self.name.lock().clone()
1062 }
1063
1064 #[pygetset(setter)]
1065 fn set___name__(&self, name: PyStrRef) {
1066 *self.name.lock() = name;
1067 }
1068
1069 #[pygetset]
1070 fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
1071 {
1073 let annotations = self.annotations.lock();
1074 if let Some(ref ann) = *annotations {
1075 return Ok(ann.clone());
1076 }
1077 }
1078
1079 let annotate_fn = {
1081 let annotate = self.annotate.lock();
1082 if let Some(ref func) = *annotate
1083 && func.is_callable()
1084 {
1085 Some(func.clone())
1086 } else {
1087 None
1088 }
1089 };
1090
1091 if let Some(annotate_fn) = annotate_fn {
1093 let one = vm.ctx.new_int(1);
1094 let ann_dict = annotate_fn.call((one,), vm)?;
1095 let ann_dict = ann_dict
1096 .downcast::<crate::builtins::PyDict>()
1097 .map_err(|obj| {
1098 vm.new_type_error(format!(
1099 "__annotate__ returned non-dict of type '{}'",
1100 obj.class().name()
1101 ))
1102 })?;
1103
1104 *self.annotations.lock() = Some(ann_dict.clone());
1106 return Ok(ann_dict);
1107 }
1108
1109 let new_dict = vm.ctx.new_dict();
1111 *self.annotations.lock() = Some(new_dict.clone());
1112 Ok(new_dict)
1113 }
1114
1115 #[pygetset(setter)]
1116 fn set___annotations__(
1117 &self,
1118 value: PySetterValue<Option<PyObjectRef>>,
1119 vm: &VirtualMachine,
1120 ) -> PyResult<()> {
1121 match value {
1122 PySetterValue::Assign(Some(value)) => {
1123 let annotations = value.downcast::<crate::builtins::PyDict>().map_err(|_| {
1124 vm.new_type_error("__annotations__ must be set to a dict object")
1125 })?;
1126 *self.annotations.lock() = Some(annotations);
1127 *self.annotate.lock() = None;
1128 }
1129 PySetterValue::Assign(None) => {
1130 *self.annotations.lock() = None;
1131 *self.annotate.lock() = None;
1132 }
1133 PySetterValue::Delete => {
1134 *self.annotations.lock() = None;
1136 }
1137 }
1138 Ok(())
1139 }
1140
1141 #[pygetset]
1142 fn __dict__(zelf: &Self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
1143 object::object_get_dict(zelf.as_object().to_owned(), vm)
1144 }
1145
1146 #[pygetset(setter)]
1147 fn set___dict__(zelf: &Self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
1148 object::object_generic_set_dict(zelf.as_object().to_owned(), value, vm)
1149 }
1150
1151 #[pygetset]
1152 fn __annotate__(&self, vm: &VirtualMachine) -> PyObjectRef {
1153 self.annotate
1154 .lock()
1155 .clone()
1156 .unwrap_or_else(|| vm.ctx.none())
1157 }
1158
1159 #[pygetset(setter)]
1160 fn set___annotate__(
1161 &self,
1162 value: PySetterValue<Option<PyObjectRef>>,
1163 vm: &VirtualMachine,
1164 ) -> PyResult<()> {
1165 let annotate = match value {
1166 PySetterValue::Assign(Some(value)) => {
1167 if !value.is_callable() {
1168 return Err(vm.new_type_error("__annotate__ must be callable or None"));
1169 }
1170 *self.annotations.lock() = None;
1172 Some(value)
1173 }
1174 PySetterValue::Assign(None) => None,
1175 PySetterValue::Delete => {
1176 return Err(vm.new_type_error("__annotate__ cannot be deleted"));
1177 }
1178 };
1179 *self.annotate.lock() = annotate;
1180 Ok(())
1181 }
1182
1183 #[pygetset]
1184 fn __qualname__(&self) -> PyStrRef {
1185 self.qualname.lock().clone()
1186 }
1187
1188 #[pygetset(setter)]
1189 fn set___qualname__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
1190 match value {
1191 PySetterValue::Assign(value) => {
1192 let Ok(qualname) = value.downcast::<PyStr>() else {
1193 return Err(vm.new_type_error("__qualname__ must be set to a string object"));
1194 };
1195 *self.qualname.lock() = qualname;
1196 }
1197 PySetterValue::Delete => {
1198 return Err(vm.new_type_error("__qualname__ must be set to a string object"));
1199 }
1200 }
1201 Ok(())
1202 }
1203
1204 #[pygetset]
1205 fn __type_params__(&self) -> PyTupleRef {
1206 self.type_params.lock().clone()
1207 }
1208
1209 #[pygetset(setter)]
1210 fn set___type_params__(
1211 &self,
1212 value: PySetterValue<PyTupleRef>,
1213 vm: &VirtualMachine,
1214 ) -> PyResult<()> {
1215 match value {
1216 PySetterValue::Assign(value) => {
1217 *self.type_params.lock() = value;
1218 }
1219 PySetterValue::Delete => {
1220 return Err(vm.new_type_error("__type_params__ must be set to a tuple object"));
1221 }
1222 }
1223 Ok(())
1224 }
1225
1226 #[cfg(feature = "jit")]
1227 #[pymethod]
1228 fn __jit__(zelf: PyRef<PyFunction>, vm: &VirtualMachine) -> PyResult<()> {
1229 let mut jit_guard = zelf.jitted_code.lock();
1230 if jit_guard.is_some() {
1231 return Ok(());
1232 }
1233 let arg_types = jit::get_jit_arg_types(&zelf, vm)?;
1234 let ret_type = jit::jit_ret_type(&zelf, vm)?;
1235 let code: &Py<PyCode> = &zelf.code;
1236 let compiled = rustpython_jit::compile(&code.code, &arg_types, ret_type)
1237 .map_err(|err| jit::new_jit_error(err.to_string(), vm))?;
1238 *jit_guard = Some(compiled);
1239 Ok(())
1240 }
1241}
1242
1243impl GetDescriptor for PyFunction {
1244 fn descr_get(
1245 zelf: &PyObject,
1246 obj: Option<&PyObject>,
1247 cls: Option<&PyObject>,
1248 vm: &VirtualMachine,
1249 ) -> PyResult {
1250 let (_zelf, obj) = Self::_unwrap(zelf, obj, vm)?;
1251 Ok(if vm.is_none(obj) && !Self::_cls_is(&cls, obj.class()) {
1252 zelf.to_owned()
1253 } else {
1254 PyBoundMethod::new(obj.to_owned(), zelf.to_owned())
1255 .into_ref(&vm.ctx)
1256 .into()
1257 })
1258 }
1259}
1260
1261impl Callable for PyFunction {
1262 type Args = FuncArgs;
1263 #[inline]
1264 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1265 zelf.invoke(args, vm)
1266 }
1267}
1268
1269impl Representable for PyFunction {
1270 #[inline]
1271 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1272 Ok(format!(
1273 "<function {} at {:#x}>",
1274 zelf.__qualname__(),
1275 zelf.get_id()
1276 ))
1277 }
1278}
1279
1280#[derive(FromArgs)]
1281pub struct PyFunctionNewArgs {
1282 #[pyarg(positional)]
1283 code: PyRef<PyCode>,
1284 #[pyarg(positional)]
1285 globals: PyDictRef,
1286 #[pyarg(any, optional, error_msg = "arg 3 (name) must be None or string")]
1287 name: OptionalArg<PyStrRef>,
1288 #[pyarg(any, optional, error_msg = "arg 4 (defaults) must be None or tuple")]
1289 argdefs: Option<PyTupleRef>,
1290 #[pyarg(any, optional, error_msg = "arg 5 (closure) must be None or tuple")]
1291 closure: Option<PyTupleRef>,
1292 #[pyarg(any, optional, error_msg = "arg 6 (kwdefaults) must be None or dict")]
1293 kwdefaults: Option<PyDictRef>,
1294}
1295
1296impl Constructor for PyFunction {
1297 type Args = PyFunctionNewArgs;
1298
1299 fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
1300 let closure = if let Some(closure_tuple) = args.closure {
1302 if closure_tuple.as_slice().len() != args.code.freevars.len() {
1304 return Err(vm.new_value_error(format!(
1305 "{} requires closure of length {}, not {}",
1306 args.code.obj_name,
1307 args.code.freevars.len(),
1308 closure_tuple.as_slice().len()
1309 )));
1310 }
1311
1312 let typed_closure = closure_tuple.try_into_typed::<PyCell>(vm)?;
1314 Some(typed_closure)
1315 } else if !args.code.freevars.is_empty() {
1316 return Err(vm.new_type_error("arg 5 (closure) must be tuple"));
1317 } else {
1318 None
1319 };
1320
1321 let mut func = Self::new(args.code.clone(), args.globals.clone(), vm)?;
1322 if let Some(name) = args.name.into_option() {
1324 *func.name.lock() = name.clone();
1325 *func.qualname.lock() = name;
1327 }
1328 if let Some(closure_tuple) = closure {
1330 func.closure = Some(closure_tuple);
1331 }
1332 if let Some(argdefs) = args.argdefs {
1333 func.defaults_and_kwdefaults.lock().0 = Some(argdefs);
1334 }
1335 if let Some(kwdefaults) = args.kwdefaults {
1336 func.defaults_and_kwdefaults.lock().1 = Some(kwdefaults);
1337 }
1338
1339 Ok(func)
1340 }
1341}
1342
1343#[pyclass(module = false, name = "method", traverse)]
1344#[derive(Debug)]
1345pub struct PyBoundMethod {
1346 #[pymember(name = "__self__")]
1347 object: PyObjectRef,
1348 #[pymember(name = "__func__")]
1349 function: PyObjectRef,
1350}
1351
1352impl Callable for PyBoundMethod {
1353 type Args = FuncArgs;
1354 #[inline]
1355 fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1356 args.prepend_arg(zelf.object.clone());
1357 zelf.function.call(args, vm)
1358 }
1359}
1360
1361impl Comparable for PyBoundMethod {
1362 fn cmp(
1363 zelf: &Py<Self>,
1364 other: &PyObject,
1365 op: PyComparisonOp,
1366 _vm: &VirtualMachine,
1367 ) -> PyResult<PyComparisonValue> {
1368 op.eq_only(|| {
1369 let other = class_or_notimplemented!(Self, other);
1370 Ok(PyComparisonValue::Implemented(
1371 zelf.function.is(&other.function) && zelf.object.is(&other.object),
1372 ))
1373 })
1374 }
1375}
1376
1377impl Hashable for PyBoundMethod {
1378 fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
1379 let self_hash = crate::common::hash::hash_object_id_raw(zelf.object.get_id());
1380 let func_hash = zelf.function.hash(vm)?;
1381 Ok(crate::common::hash::fix_sentinel(self_hash ^ func_hash))
1382 }
1383}
1384
1385impl GetAttr for PyBoundMethod {
1386 fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
1387 let class_attr = vm
1388 .ctx
1389 .interned_str(name)
1390 .and_then(|attr_name| zelf.get_class_attr(attr_name));
1391 if let Some(obj) = class_attr {
1392 return vm.call_if_get_descriptor(&obj, zelf.to_owned().into());
1393 }
1394 zelf.function.get_attr(name, vm)
1395 }
1396}
1397
1398impl GetDescriptor for PyBoundMethod {
1399 fn descr_get(
1400 zelf: &PyObject,
1401 _obj: Option<&PyObject>,
1402 _cls: Option<&PyObject>,
1403 _vm: &VirtualMachine,
1404 ) -> PyResult {
1405 Ok(zelf.to_owned())
1406 }
1407}
1408
1409#[derive(FromArgs)]
1410pub struct PyBoundMethodNewArgs {
1411 #[pyarg(positional)]
1412 function: PyObjectRef,
1413 #[pyarg(positional)]
1414 object: PyObjectRef,
1415}
1416
1417impl Constructor for PyBoundMethod {
1418 type Args = PyBoundMethodNewArgs;
1419
1420 fn py_new(
1421 _cls: &Py<PyType>,
1422 Self::Args { function, object }: Self::Args,
1423 vm: &VirtualMachine,
1424 ) -> PyResult<Self> {
1425 if !function.is_callable() {
1426 return Err(vm.new_type_error("first argument must be callable"));
1427 }
1428 if vm.is_none(&object) {
1429 return Err(vm.new_type_error("instance must not be None"));
1430 }
1431 Ok(Self::new(object, function))
1432 }
1433}
1434
1435impl PyBoundMethod {
1436 #[must_use]
1437 pub const fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
1438 Self { object, function }
1439 }
1440
1441 #[inline]
1442 pub(crate) fn function_obj(&self) -> &PyObject {
1443 &self.function
1444 }
1445
1446 #[inline]
1447 pub(crate) fn self_obj(&self) -> &PyObject {
1448 &self.object
1449 }
1450
1451 #[deprecated(note = "Use `Self::new(object, function).into_ref(ctx)` instead")]
1452 pub fn new_ref(object: PyObjectRef, function: PyObjectRef, ctx: &Context) -> PyRef<Self> {
1453 Self::new(object, function).into_ref(ctx)
1454 }
1455}
1456
1457#[pyclass(
1458 with(
1459 Callable,
1460 Comparable,
1461 Hashable,
1462 GetAttr,
1463 GetDescriptor,
1464 Constructor,
1465 Representable
1466 ),
1467 flags(IMMUTABLETYPE, HAS_WEAKREF)
1468)]
1469impl Py<PyBoundMethod> {
1470 #[pymethod]
1471 fn __reduce__(
1472 &self,
1473 vm: &VirtualMachine,
1474 ) -> PyResult<(PyObjectRef, (PyObjectRef, PyObjectRef))> {
1475 let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
1476 let func_self = self.object.clone();
1477 let func_name = self.function.get_attr("__name__", vm)?;
1478 Ok((builtins_getattr, (func_self, func_name)))
1479 }
1480
1481 #[pygetset]
1482 fn __doc__(&self, vm: &VirtualMachine) -> PyResult {
1483 self.function.get_attr("__doc__", vm)
1484 }
1485
1486 #[pygetset]
1487 fn __module__(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
1488 self.function.get_attr("__module__", vm).ok()
1489 }
1490
1491 #[pymethod]
1492 fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyList> {
1493 let func_dir = vm.dir(Some(self.function.clone()))?;
1494
1495 let bound_only = [
1496 "__self__",
1497 "__func__",
1498 "__doc__",
1499 "__module__",
1500 "__call__",
1501 "__get__",
1502 "__repr__",
1503 ];
1504
1505 let mut seen = std::collections::HashSet::new();
1506 let mut result: Vec<PyObjectRef> = Vec::new();
1507
1508 for item in func_dir.borrow_vec().iter() {
1509 if let Ok(s) = item.clone().downcast::<PyStr>() {
1510 seen.insert(s.as_wtf8().to_string());
1511 }
1512 result.push(item.clone());
1513 }
1514
1515 for name in bound_only {
1516 if seen.insert(name.to_owned()) {
1517 result.push(vm.ctx.new_str(name).into());
1518 }
1519 }
1520
1521 Ok(PyList::from(result))
1522 }
1523}
1524
1525impl PyPayload for PyBoundMethod {
1526 #[inline]
1527 fn class(ctx: &Context) -> &'static Py<PyType> {
1528 ctx.types.bound_method_type
1529 }
1530}
1531
1532impl Representable for PyBoundMethod {
1533 #[inline]
1534 fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
1535 let func_name = if let Some(qname) =
1536 vm.get_attribute_opt(&zelf.function, identifier!(vm, __qualname__))?
1537 {
1538 Some(qname)
1539 } else {
1540 vm.get_attribute_opt(&zelf.function, identifier!(vm, __name__))?
1541 };
1542 let func_name: Option<PyStrRef> = func_name.and_then(|o| o.downcast().ok());
1543 let object_repr = zelf.object.repr(vm)?;
1544 let name = func_name
1545 .as_ref()
1546 .map_or_else(|| "?".as_ref(), |s| s.as_wtf8());
1547 Ok(wtf8_concat!(
1548 "<bound method ",
1549 name,
1550 " of ",
1551 object_repr.as_wtf8(),
1552 ">"
1553 ))
1554 }
1555}
1556
1557#[pyclass(module = false, name = "cell", unhashable = true, traverse)]
1558#[derive(Debug, Default)]
1559pub(crate) struct PyCell {
1560 contents: PyMutex<Option<PyObjectRef>>,
1561}
1562
1563pub(crate) type PyCellRef = PyRef<PyCell>;
1564
1565impl PyPayload for PyCell {
1566 #[inline]
1567 fn class(ctx: &Context) -> &'static Py<PyType> {
1568 ctx.types.cell_type
1569 }
1570}
1571
1572impl Constructor for PyCell {
1573 type Args = OptionalArg;
1574
1575 fn py_new(_cls: &Py<PyType>, value: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
1576 Ok(Self::new(value.into_option()))
1577 }
1578}
1579
1580impl PyCell {
1581 pub(crate) const fn new(contents: Option<PyObjectRef>) -> Self {
1582 Self {
1583 contents: PyMutex::new(contents),
1584 }
1585 }
1586
1587 pub(crate) fn get(&self) -> Option<PyObjectRef> {
1588 self.contents.lock().clone()
1589 }
1590
1591 pub(crate) fn set(&self, x: Option<PyObjectRef>) {
1592 let replaced = core::mem::replace(&mut *self.contents.lock(), x);
1596 drop(replaced);
1597 }
1598}
1599
1600#[pyclass(with(Constructor, Representable))]
1601impl Py<PyCell> {
1602 #[pyslot]
1603 fn slot_richcompare(
1604 zelf: &PyObject,
1605 other: &PyObject,
1606 op: PyComparisonOp,
1607 vm: &VirtualMachine,
1608 ) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
1609 let (Some(zelf), Some(other)) = (
1610 zelf.downcast_ref::<PyCell>(),
1611 other.downcast_ref::<PyCell>(),
1612 ) else {
1613 return Ok(Either::B(PyComparisonValue::NotImplemented));
1614 };
1615 match (zelf.get(), other.get()) {
1617 (Some(a), Some(b)) => a.rich_compare(b, op, vm).map(Either::A),
1618 (a, b) => Ok(Either::B(op.eval_ord(b.is_none().cmp(&a.is_none())).into())),
1619 }
1620 }
1621
1622 #[pygetset]
1623 fn cell_contents(&self, vm: &VirtualMachine) -> PyResult {
1624 self.get()
1625 .ok_or_else(|| vm.new_value_error("Cell is empty"))
1626 }
1627
1628 #[pygetset(setter)]
1629 fn set_cell_contents(&self, x: PySetterValue) {
1630 match x {
1631 PySetterValue::Assign(value) => self.set(Some(value)),
1632 PySetterValue::Delete => self.set(None),
1633 }
1634 }
1635}
1636
1637impl Representable for PyCell {
1638 #[inline]
1639 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1640 let id = zelf.get_id();
1641 Ok(match zelf.get() {
1642 Some(value) => {
1643 let type_name = value.class().slot_name();
1644 let mut end = type_name.len().min(80);
1647 while !type_name.is_char_boundary(end) {
1648 end -= 1;
1649 }
1650 format!(
1651 "<cell at {id:#x}: {} object at {:#x}>",
1652 &type_name[..end],
1653 value.get_id()
1654 )
1655 }
1656 None => format!("<cell at {id:#x}: empty>"),
1657 })
1658 }
1659}
1660
1661const MAX_INLINE_KW: usize = 16;
1665
1666fn try_reorder_simple_kwargs(
1686 code: &Py<PyCode>,
1687 mut args: Vec<PyObjectRef>,
1688 nargs: usize,
1689 kwnames: &[PyObjectRef],
1690) -> Result<Vec<PyObjectRef>, Vec<PyObjectRef>> {
1691 let arg_count = code.arg_count as usize;
1692 let posonly = code.posonlyarg_count as usize;
1693 let kw_count = kwnames.len();
1694 if kw_count > MAX_INLINE_KW {
1695 return Err(args);
1696 }
1697
1698 let mut rel_positions = [0usize; MAX_INLINE_KW];
1701 for (i, name_obj) in kwnames.iter().enumerate() {
1702 let Some(name_str) = name_obj.downcast_ref::<PyStr>().and_then(|s| s.to_str()) else {
1703 return Err(args);
1704 };
1705 let Some(pos) = code.varnames[posonly..arg_count]
1706 .iter()
1707 .position(|v| v.as_str() == name_str)
1708 .map(|p| p + posonly)
1709 else {
1710 return Err(args);
1712 };
1713 let rel = match pos.checked_sub(nargs) {
1714 None => return Err(args),
1717 Some(rel) => rel,
1718 };
1719 if rel_positions[..i].contains(&rel) {
1720 return Err(args);
1722 }
1723 rel_positions[i] = rel;
1724 }
1725
1726 let mut buf: [Option<PyObjectRef>; MAX_INLINE_KW] = [const { None }; MAX_INLINE_KW];
1730 for (i, value) in args.drain(nargs..nargs + kw_count).enumerate() {
1731 buf[rel_positions[i]] = Some(value);
1732 }
1733 for slot in buf.iter_mut().take(kw_count) {
1734 args.push(slot.take().unwrap());
1735 }
1736 Ok(args)
1737}
1738
1739pub(crate) fn vectorcall_function(
1742 zelf_obj: &PyObject,
1743 mut args: Vec<PyObjectRef>,
1744 nargs: usize,
1745 kwnames: Option<&[PyObjectRef]>,
1746 vm: &VirtualMachine,
1747) -> PyResult {
1748 let zelf: &Py<PyFunction> = zelf_obj.downcast_ref().unwrap();
1749 let code: &Py<PyCode> = &zelf.code;
1750
1751 let has_kwargs = kwnames.is_some_and(|kw| !kw.is_empty());
1752 if zelf.is_jitted() {
1753 let func_args = if has_kwargs {
1754 FuncArgs::from_vectorcall_owned(args, nargs, kwnames)
1755 } else {
1756 args.truncate(nargs);
1757 FuncArgs::from(args)
1758 };
1759 return zelf.invoke(func_args, vm);
1760 }
1761
1762 let positional_only = code.flags.contains(bytecode::CodeFlags::OPTIMIZED)
1765 && !code.flags.contains(bytecode::CodeFlags::VARARGS)
1766 && !code.flags.contains(bytecode::CodeFlags::VARKEYWORDS)
1767 && code.kwonlyarg_count == 0;
1768 let is_generator_like = code.flags.intersects(
1769 bytecode::CodeFlags::GENERATOR
1770 | bytecode::CodeFlags::COROUTINE
1771 | bytecode::CodeFlags::ASYNC_GENERATOR,
1772 );
1773 let base_simple = positional_only && !is_generator_like;
1774
1775 if !has_kwargs && positional_only && is_generator_like && nargs == code.arg_count as usize {
1776 args.truncate(nargs);
1782 return Ok(zelf.make_generator_exact_args(args.into_iter(), vm));
1783 }
1784
1785 if !has_kwargs && base_simple && nargs == code.arg_count as usize {
1786 args.truncate(nargs);
1789 let frame = zelf.prepare_exact_args_frame(args.into_iter(), vm);
1790
1791 let result = vm.run_frame(frame.clone());
1792 crate::frame::release_datastack_frame(&frame, vm);
1793 return result;
1794 }
1795
1796 if has_kwargs
1797 && base_simple
1798 && let Some(kwnames) = kwnames
1799 && nargs + kwnames.len() == code.arg_count as usize
1800 {
1801 match try_reorder_simple_kwargs(code, args, nargs, kwnames) {
1806 Ok(ordered) => {
1807 let frame = zelf.prepare_exact_args_frame(ordered.into_iter(), vm);
1808 let result = vm.run_frame(frame.clone());
1809 crate::frame::release_datastack_frame(&frame, vm);
1810 return result;
1811 }
1812 Err(restored) => {
1813 args = restored;
1814 }
1815 }
1816 }
1817
1818 let func_args = if has_kwargs {
1820 FuncArgs::from_vectorcall_owned(args, nargs, kwnames)
1821 } else {
1822 args.truncate(nargs);
1823 FuncArgs::from(args)
1824 };
1825
1826 zelf.invoke(func_args, vm)
1827}
1828
1829fn vectorcall_bound_method(
1831 zelf_obj: &PyObject,
1832 mut args: Vec<PyObjectRef>,
1833 nargs: usize,
1834 kwnames: Option<&[PyObjectRef]>,
1835 vm: &VirtualMachine,
1836) -> PyResult {
1837 let zelf: &Py<PyBoundMethod> = zelf_obj.downcast_ref().unwrap();
1838
1839 args.insert(0, zelf.object.clone());
1842 let new_nargs = nargs + 1;
1843 zelf.function.vectorcall(args, new_nargs, kwnames, vm)
1844}
1845
1846pub(crate) fn init(context: &'static Context) {
1847 PyFunction::extend_class(context, context.types.function_type);
1848 context
1849 .types
1850 .function_type
1851 .slots
1852 .vectorcall
1853 .store(Some(vectorcall_function));
1854
1855 PyBoundMethod::extend_class(context, context.types.bound_method_type);
1856 context
1857 .types
1858 .bound_method_type
1859 .slots
1860 .vectorcall
1861 .store(Some(vectorcall_bound_method));
1862
1863 PyCell::extend_class(context, context.types.cell_type);
1864}