Skip to main content

rustpython_vm/builtins/
list.rs

1use super::{
2    PositionIterInternal, PyGenericAlias, PyTupleRef, PyType, PyTypeRef,
3    iter::{builtins_iter, builtins_reversed},
4};
5use crate::atomic_func;
6use crate::common::lock::{
7    PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard,
8};
9use crate::object::{Traverse, TraverseFn};
10use crate::{
11    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
12    builtins::PyStr,
13    class::PyClassImpl,
14    convert::ToPyObject,
15    function::{ArgSize, FuncArgs, OptionalArg, PyComparisonValue},
16    iter::PyExactSizeIterator,
17    protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
18    recursion::ReprGuard,
19    sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt},
20    sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
21    types::{
22        AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable,
23        PyComparisonOp, Representable, SelfIter,
24    },
25    vm::VirtualMachine,
26};
27use rustpython_common::wtf8::Wtf8Buf;
28
29use alloc::fmt;
30use core::cell::Cell;
31use core::ops::DerefMut;
32use core::ptr::NonNull;
33use core::sync::atomic::{AtomicU32, Ordering};
34
35#[pyclass(module = false, name = "list", unhashable = true, traverse = "manual")]
36#[derive(Default)]
37pub struct PyList {
38    elements: PyRwLock<Vec<PyObjectRef>>,
39    mutation_counter: AtomicU32,
40}
41
42impl fmt::Debug for PyList {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        // TODO: implement more detailed, non-recursive Debug formatter
45        f.write_str("list")
46    }
47}
48
49impl From<Vec<PyObjectRef>> for PyList {
50    fn from(elements: Vec<PyObjectRef>) -> Self {
51        Self {
52            elements: PyRwLock::new(elements),
53            mutation_counter: AtomicU32::new(0),
54        }
55    }
56}
57
58impl FromIterator<PyObjectRef> for PyList {
59    fn from_iter<T: IntoIterator<Item = PyObjectRef>>(iter: T) -> Self {
60        Vec::from_iter(iter).into()
61    }
62}
63
64// SAFETY: Traverse properly visits all owned PyObjectRefs
65unsafe impl Traverse for PyList {
66    fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
67        self.elements.traverse(traverse_fn);
68    }
69
70    fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
71        // During GC, we use interior mutability to access elements.
72        // This is safe because during GC collection, the object is unreachable
73        // and no other code should be accessing it.
74        if let Some(mut guard) = self.elements.try_write() {
75            out.extend(guard.drain(..));
76        }
77    }
78}
79
80thread_local! {
81    static LIST_FREELIST: Cell<crate::object::FreeList<PyList>> = const { Cell::new(crate::object::FreeList::new()) };
82}
83
84impl PyPayload for PyList {
85    const MAX_FREELIST: usize = 80;
86    const HAS_FREELIST: bool = true;
87
88    #[inline]
89    fn class(ctx: &Context) -> &'static Py<PyType> {
90        ctx.types.list_type
91    }
92
93    #[inline]
94    unsafe fn freelist_push(obj: *mut PyObject) -> bool {
95        LIST_FREELIST
96            .try_with(|fl| {
97                let mut list = fl.take();
98                let stored = if list.len() < Self::MAX_FREELIST {
99                    list.push(obj);
100                    true
101                } else {
102                    false
103                };
104                fl.set(list);
105                stored
106            })
107            .unwrap_or(false)
108    }
109
110    #[inline]
111    unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
112        LIST_FREELIST
113            .try_with(|fl| {
114                let mut list = fl.take();
115                let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
116                fl.set(list);
117                result
118            })
119            .ok()
120            .flatten()
121    }
122}
123
124impl ToPyObject for Vec<PyObjectRef> {
125    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
126        PyList::from(self).into_ref(&vm.ctx).into()
127    }
128}
129
130impl PyList {
131    #[deprecated(note = "use PyList::from(...).into_ref() instead")]
132    pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
133        Self::from(elements).into_ref(ctx)
134    }
135
136    pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> {
137        PyRwLockReadGuard::map(self.elements.read(), |v| &**v)
138    }
139
140    pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
141        let guard = self.elements.write();
142        self.mutation_counter.fetch_add(1, Ordering::Relaxed);
143        guard
144    }
145
146    fn repeat(&self, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
147        let elements = &*self.borrow_vec();
148        let v = elements.mul(vm, n)?;
149        Ok(Self::from(v).into_ref(&vm.ctx))
150    }
151
152    fn irepeat(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
153        zelf.borrow_vec_mut().imul(vm, n)?;
154        Ok(zelf)
155    }
156}
157
158#[derive(FromArgs, Default, Traverse)]
159pub(crate) struct SortOptions {
160    #[pyarg(named, default)]
161    key: Option<PyObjectRef>,
162    #[pytraverse(skip)]
163    #[pyarg(named, default = false)]
164    reverse: bool,
165}
166
167pub type PyListRef = PyRef<PyList>;
168
169#[pyclass(
170    with(
171        Constructor,
172        Initializer,
173        AsMapping,
174        Iterable,
175        Comparable,
176        AsSequence,
177        Representable
178    ),
179    flags(BASETYPE, SEQUENCE, _MATCH_SELF)
180)]
181impl PyList {
182    #[pymethod]
183    pub(crate) fn append(&self, x: PyObjectRef) {
184        self.borrow_vec_mut().push(x);
185    }
186
187    #[pymethod]
188    pub(crate) fn extend(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
189        let mut new_elements = x.try_to_value(vm)?;
190        self.borrow_vec_mut().append(&mut new_elements);
191        Ok(())
192    }
193
194    #[pymethod]
195    pub(crate) fn insert(&self, position: isize, element: PyObjectRef) {
196        let mut elements = self.borrow_vec_mut();
197        let position = elements.saturate_index(position);
198        elements.insert(position, element);
199    }
200
201    fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
202        let other = other.downcast_ref::<Self>().ok_or_else(|| {
203            vm.new_type_error(format!(
204                "Cannot add {} and {}",
205                Self::class(&vm.ctx).name(),
206                other.class().name()
207            ))
208        })?;
209        let mut elements = self.borrow_vec().to_vec();
210        elements.extend(other.borrow_vec().iter().cloned());
211        Ok(Self::from(elements).into_ref(&vm.ctx))
212    }
213
214    fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
215        self.concat(&other, vm)
216    }
217
218    fn inplace_concat(
219        zelf: &Py<Self>,
220        other: &PyObject,
221        vm: &VirtualMachine,
222    ) -> PyResult<PyObjectRef> {
223        let mut seq = extract_cloned(other, Ok, vm)?;
224        zelf.borrow_vec_mut().append(&mut seq);
225        Ok(zelf.to_owned().into())
226    }
227
228    fn __iadd__(
229        zelf: PyRef<Self>,
230        other: PyObjectRef,
231        vm: &VirtualMachine,
232    ) -> PyResult<PyRef<Self>> {
233        let mut seq = extract_cloned(&other, Ok, vm)?;
234        zelf.borrow_vec_mut().append(&mut seq);
235        Ok(zelf)
236    }
237
238    #[pymethod]
239    fn clear(&self) {
240        let _removed = core::mem::take(self.borrow_vec_mut().deref_mut());
241    }
242
243    #[pymethod]
244    fn copy(&self, vm: &VirtualMachine) -> PyRef<Self> {
245        Self::from(self.borrow_vec().to_vec()).into_ref(&vm.ctx)
246    }
247
248    #[allow(clippy::len_without_is_empty)]
249    pub fn __len__(&self) -> usize {
250        self.borrow_vec().len()
251    }
252
253    #[pymethod]
254    fn __sizeof__(&self) -> usize {
255        core::mem::size_of::<Self>()
256            + self.elements.read().capacity() * core::mem::size_of::<PyObjectRef>()
257    }
258
259    #[pymethod]
260    fn reverse(&self) {
261        self.borrow_vec_mut().reverse();
262    }
263
264    #[pymethod]
265    fn __reversed__(zelf: PyRef<Self>) -> PyListReverseIterator {
266        let position = zelf.__len__().saturating_sub(1);
267        PyListReverseIterator {
268            internal: PyMutex::new(PositionIterInternal::new(zelf, position)),
269        }
270    }
271
272    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
273        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
274            SequenceIndex::Int(i) => {
275                let vec = self.borrow_vec();
276                let pos = vec
277                    .wrap_index(i)
278                    .ok_or_else(|| vm.new_index_error("list index out of range"))?;
279                Ok(vec.do_get(pos))
280            }
281            SequenceIndex::Slice(slice) => self
282                .borrow_vec()
283                .getitem_by_slice(vm, slice)
284                .map(|x| vm.ctx.new_list(x).into()),
285        }
286    }
287
288    fn __getitem__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
289        self._getitem(&needle, vm)
290    }
291
292    fn _setitem(&self, needle: &PyObject, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
293        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
294            SequenceIndex::Int(index) => self
295                .borrow_vec_mut()
296                .setitem_by_index(vm, index, value)
297                .map_err(|e| {
298                    if e.class().is(vm.ctx.exceptions.index_error) {
299                        vm.new_index_error("list assignment index out of range".to_owned())
300                    } else {
301                        e
302                    }
303                }),
304            SequenceIndex::Slice(slice) => {
305                let sec = extract_cloned(&value, Ok, vm)?;
306                self.borrow_vec_mut().setitem_by_slice(vm, slice, &sec)
307            }
308        }
309    }
310
311    fn __setitem__(
312        &self,
313        needle: PyObjectRef,
314        value: PyObjectRef,
315        vm: &VirtualMachine,
316    ) -> PyResult<()> {
317        self._setitem(&needle, value, vm)
318    }
319
320    fn __mul__(&self, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
321        self.repeat(n.into(), vm)
322    }
323
324    fn __imul__(zelf: PyRef<Self>, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
325        Self::irepeat(zelf, n.into(), vm)
326    }
327
328    #[pymethod]
329    fn count(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
330        self.mut_count(vm, &needle)
331    }
332
333    pub(crate) fn __contains__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
334        self.mut_contains(vm, &needle)
335    }
336
337    #[pymethod]
338    fn index(
339        &self,
340        needle: PyObjectRef,
341        range: OptionalRangeArgs,
342        vm: &VirtualMachine,
343    ) -> PyResult<usize> {
344        let (start, stop) = range.saturate(self.__len__(), vm)?;
345        let index = self.mut_index_range(vm, &needle, start..stop)?;
346        if let Some(index) = index.into() {
347            Ok(index)
348        } else {
349            Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
350        }
351    }
352
353    #[pymethod]
354    fn pop(&self, i: OptionalArg<isize>, vm: &VirtualMachine) -> PyResult {
355        let mut i = i.into_option().unwrap_or(-1);
356        let mut elements = self.borrow_vec_mut();
357        if i < 0 {
358            i += elements.len() as isize;
359        }
360        if elements.is_empty() {
361            Err(vm.new_index_error("pop from empty list"))
362        } else if i < 0 || i as usize >= elements.len() {
363            Err(vm.new_index_error("pop index out of range"))
364        } else {
365            Ok(elements.remove(i as usize))
366        }
367    }
368
369    #[pymethod]
370    fn remove(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
371        let index = self.mut_index(vm, &needle)?;
372
373        if let Some(index) = index.into() {
374            // defer delete out of borrow
375            let is_inside_range = index < self.borrow_vec().len();
376            Ok(is_inside_range.then(|| self.borrow_vec_mut().remove(index)))
377        } else {
378            Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
379        }
380        .map(drop)
381    }
382
383    fn _delitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
384        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
385            SequenceIndex::Int(i) => self.borrow_vec_mut().delitem_by_index(vm, i),
386            SequenceIndex::Slice(slice) => self.borrow_vec_mut().delitem_by_slice(vm, slice),
387        }
388    }
389
390    fn __delitem__(&self, subscript: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
391        self._delitem(&subscript, vm)
392    }
393
394    #[pymethod]
395    pub(crate) fn sort(&self, options: SortOptions, vm: &VirtualMachine) -> PyResult<()> {
396        // replace list contents with [] for duration of sort.
397        // this prevents keyfunc from messing with the list and makes it easy to
398        // check if it tries to append elements to it.
399        let (mut elements, version_before) = {
400            let mut guard = self.elements.write();
401            let version_before = self.mutation_counter.load(Ordering::Relaxed);
402            (core::mem::take(guard.deref_mut()), version_before)
403        };
404        let res = do_sort(vm, &mut elements, options.key, options.reverse);
405        let mutated = {
406            let mut guard = self.elements.write();
407            let mutated = self.mutation_counter.load(Ordering::Relaxed) != version_before;
408            core::mem::swap(guard.deref_mut(), &mut elements);
409            mutated
410        };
411        res?;
412
413        if mutated {
414            return Err(vm.new_value_error("list modified during sort"));
415        }
416
417        Ok(())
418    }
419
420    #[pyclassmethod]
421    fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
422        PyGenericAlias::from_args(cls, args, vm)
423    }
424}
425
426fn extract_cloned<F, R>(obj: &PyObject, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>>
427where
428    F: FnMut(PyObjectRef) -> PyResult<R>,
429{
430    use crate::builtins::PyTuple;
431    if let Some(tuple) = obj.downcast_ref_if_exact::<PyTuple>(vm) {
432        tuple.iter().map(|x| f(x.clone())).collect()
433    } else if let Some(list) = obj.downcast_ref_if_exact::<PyList>(vm) {
434        list.borrow_vec().iter().map(|x| f(x.clone())).collect()
435    } else {
436        let iter = obj.to_owned().get_iter(vm)?;
437        let iter = iter.iter::<PyObjectRef>(vm)?;
438        let len = obj
439            .sequence_unchecked()
440            .length_opt(vm)
441            .transpose()?
442            .unwrap_or(0);
443        let mut v = Vec::with_capacity(len);
444        for x in iter {
445            v.push(f(x?)?);
446        }
447        v.shrink_to_fit();
448        Ok(v)
449    }
450}
451
452impl MutObjectSequenceOp for PyList {
453    type Inner = [PyObjectRef];
454
455    fn do_get(index: usize, inner: &[PyObjectRef]) -> Option<&PyObject> {
456        inner.get(index).map(|r| r.as_ref())
457    }
458
459    fn do_lock(&self) -> impl core::ops::Deref<Target = [PyObjectRef]> {
460        self.borrow_vec()
461    }
462}
463
464impl Constructor for PyList {
465    type Args = FuncArgs;
466
467    fn py_new(_cls: &Py<PyType>, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<Self> {
468        Ok(Self::default())
469    }
470}
471
472impl Initializer for PyList {
473    type Args = OptionalArg<PyObjectRef>;
474
475    fn init(zelf: PyRef<Self>, iterable: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
476        let mut elements = if let OptionalArg::Present(iterable) = iterable {
477            iterable.try_to_value(vm)?
478        } else {
479            vec![]
480        };
481        core::mem::swap(zelf.borrow_vec_mut().deref_mut(), &mut elements);
482        Ok(())
483    }
484}
485
486impl AsMapping for PyList {
487    fn as_mapping() -> &'static PyMappingMethods {
488        static AS_MAPPING: PyMappingMethods = PyMappingMethods {
489            length: atomic_func!(|mapping, _vm| Ok(PyList::mapping_downcast(mapping).__len__())),
490            subscript: atomic_func!(
491                |mapping, needle, vm| PyList::mapping_downcast(mapping)._getitem(needle, vm)
492            ),
493            ass_subscript: atomic_func!(|mapping, needle, value, vm| {
494                let zelf = PyList::mapping_downcast(mapping);
495                if let Some(value) = value {
496                    zelf._setitem(needle, value, vm)
497                } else {
498                    zelf._delitem(needle, vm)
499                }
500            }),
501        };
502        &AS_MAPPING
503    }
504}
505
506impl AsSequence for PyList {
507    fn as_sequence() -> &'static PySequenceMethods {
508        static AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
509            length: atomic_func!(|seq, _vm| Ok(PyList::sequence_downcast(seq).__len__())),
510            concat: atomic_func!(|seq, other, vm| {
511                PyList::sequence_downcast(seq)
512                    .concat(other, vm)
513                    .map(|x| x.into())
514            }),
515            repeat: atomic_func!(|seq, n, vm| {
516                PyList::sequence_downcast(seq)
517                    .repeat(n, vm)
518                    .map(|x| x.into())
519            }),
520            item: atomic_func!(|seq, i, vm| {
521                let list = PyList::sequence_downcast(seq);
522                let vec = list.borrow_vec();
523                let pos = vec
524                    .wrap_index(i)
525                    .ok_or_else(|| vm.new_index_error("list index out of range"))?;
526                Ok(vec.do_get(pos))
527            }),
528            ass_item: atomic_func!(|seq, i, value, vm| {
529                let zelf = PyList::sequence_downcast(seq);
530                if let Some(value) = value {
531                    zelf.borrow_vec_mut().setitem_by_index(vm, i, value)
532                } else {
533                    zelf.borrow_vec_mut().delitem_by_index(vm, i)
534                }
535                .map_err(|e| {
536                    if e.class().is(vm.ctx.exceptions.index_error) {
537                        vm.new_index_error("list assignment index out of range".to_owned())
538                    } else {
539                        e
540                    }
541                })
542            }),
543            contains: atomic_func!(|seq, target, vm| {
544                let zelf = PyList::sequence_downcast(seq);
545                zelf.mut_contains(vm, target)
546            }),
547            inplace_concat: atomic_func!(|seq, other, vm| {
548                let zelf = PyList::sequence_downcast(seq);
549                PyList::inplace_concat(zelf, other, vm)
550            }),
551            inplace_repeat: atomic_func!(|seq, n, vm| {
552                let zelf = PyList::sequence_downcast(seq);
553                Ok(PyList::irepeat(zelf.to_owned(), n, vm)?.into())
554            }),
555        };
556        &AS_SEQUENCE
557    }
558}
559
560impl Iterable for PyList {
561    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
562        Ok(PyListIterator {
563            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
564        }
565        .into_pyobject(vm))
566    }
567}
568
569impl Comparable for PyList {
570    fn cmp(
571        zelf: &Py<Self>,
572        other: &PyObject,
573        op: PyComparisonOp,
574        vm: &VirtualMachine,
575    ) -> PyResult<PyComparisonValue> {
576        if let Some(res) = op.identical_optimization(zelf, other) {
577            return Ok(res.into());
578        }
579        let other = class_or_notimplemented!(Self, other);
580        let a = &*zelf.borrow_vec();
581        let b = &*other.borrow_vec();
582        a.iter()
583            .richcompare(b.iter(), op, vm)
584            .map(PyComparisonValue::Implemented)
585    }
586}
587
588impl Representable for PyList {
589    #[inline]
590    fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
591        if zelf.__len__() == 0 {
592            return Ok(vm.ctx.intern_str("[]").to_owned());
593        }
594
595        if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
596            // Clone elements before calling repr to release the read lock.
597            // Element repr may mutate the list (e.g., list.clear()), which
598            // needs a write lock and would deadlock if read lock is held.
599            let mut writer = Wtf8Buf::new();
600            writer.push_char('[');
601
602            let mut elements = zelf.borrow_vec().to_vec();
603            let mut size = zelf.__len__();
604            let mut first = true;
605            let mut i = 0;
606            while i < size {
607                if elements.len() != size {
608                    // `repr` mutated the list. refetch it.
609                    elements = zelf.borrow_vec().to_vec();
610                }
611
612                let item = &elements[i];
613
614                if first {
615                    first = false;
616                } else {
617                    writer.push_str(", ");
618                }
619
620                writer.push_wtf8(item.repr(vm)?.as_wtf8());
621
622                size = zelf.__len__(); // Refetch list size as `repr` may mutate the list.
623                i += 1;
624            }
625
626            writer.push_char(']');
627            Ok(vm.ctx.new_str(writer))
628        } else {
629            Ok(vm.ctx.intern_str("[...]").to_owned())
630        }
631    }
632
633    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
634        unreachable!("repr() is overridden directly")
635    }
636}
637
638fn do_sort(
639    vm: &VirtualMachine,
640    values: &mut Vec<PyObjectRef>,
641    key_func: Option<PyObjectRef>,
642    reverse: bool,
643) -> PyResult<()> {
644    // CPython uses __lt__ for all comparisons in sort.
645    // try_sort_by_gt expects is_gt(a, b) = true when a should come AFTER b.
646    let cmp = |a: &PyObjectRef, b: &PyObjectRef| {
647        if reverse {
648            // Descending: a comes after b when a < b
649            a.rich_compare_bool(b, PyComparisonOp::Lt, vm)
650        } else {
651            // Ascending: a comes after b when b < a
652            b.rich_compare_bool(a, PyComparisonOp::Lt, vm)
653        }
654    };
655
656    if let Some(ref key_func) = key_func {
657        let mut items = values
658            .iter()
659            .map(|x| Ok((x.clone(), key_func.call((x.clone(),), vm)?)))
660            .collect::<Result<Vec<_>, _>>()?;
661        timsort::try_sort_by_gt(&mut items, |a, b| cmp(&a.1, &b.1))?;
662        *values = items.into_iter().map(|(val, _)| val).collect();
663    } else {
664        timsort::try_sort_by_gt(values, cmp)?;
665    }
666
667    Ok(())
668}
669
670#[pyclass(module = false, name = "list_iterator", traverse)]
671#[derive(Debug)]
672pub struct PyListIterator {
673    internal: PyMutex<PositionIterInternal<PyListRef>>,
674}
675
676impl PyPayload for PyListIterator {
677    #[inline]
678    fn class(ctx: &Context) -> &'static Py<PyType> {
679        ctx.types.list_iterator_type
680    }
681}
682
683#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
684impl PyListIterator {
685    #[pymethod]
686    fn __length_hint__(&self) -> usize {
687        self.internal.lock().length_hint(|obj| obj.__len__())
688    }
689
690    #[pymethod]
691    fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
692        self.internal
693            .lock()
694            .set_state(state, |obj, pos| pos.min(obj.__len__()), vm)
695    }
696
697    #[pymethod]
698    fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
699        let func = builtins_iter(vm);
700        self.internal.lock().reduce(
701            func,
702            |x| x.clone().into(),
703            |vm| vm.ctx.new_list(Vec::new()).into(),
704            vm,
705        )
706    }
707}
708
709impl PyListIterator {
710    /// Fast path for FOR_ITER specialization.
711    pub(crate) fn fast_next(&self) -> Option<PyObjectRef> {
712        self.internal
713            .lock()
714            .next(|list, pos| {
715                let vec = list.borrow_vec();
716                Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
717            })
718            .ok()
719            .and_then(|r| match r {
720                PyIterReturn::Return(v) => Some(v),
721                PyIterReturn::StopIteration(_) => None,
722            })
723    }
724}
725
726impl SelfIter for PyListIterator {}
727impl IterNext for PyListIterator {
728    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
729        zelf.internal.lock().next(|list, pos| {
730            let vec = list.borrow_vec();
731            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
732        })
733    }
734}
735
736#[pyclass(module = false, name = "list_reverseiterator", traverse)]
737#[derive(Debug)]
738pub struct PyListReverseIterator {
739    internal: PyMutex<PositionIterInternal<PyListRef>>,
740}
741
742impl PyPayload for PyListReverseIterator {
743    #[inline]
744    fn class(ctx: &Context) -> &'static Py<PyType> {
745        ctx.types.list_reverseiterator_type
746    }
747}
748
749#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
750impl PyListReverseIterator {
751    #[pymethod]
752    fn __length_hint__(&self) -> usize {
753        self.internal.lock().rev_length_hint(|obj| obj.__len__())
754    }
755
756    #[pymethod]
757    fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
758        self.internal
759            .lock()
760            .set_state(state, |obj, pos| pos.min(obj.__len__()), vm)
761    }
762
763    #[pymethod]
764    fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
765        let func = builtins_reversed(vm);
766        self.internal.lock().reduce(
767            func,
768            |x| x.clone().into(),
769            |vm| vm.ctx.new_list(Vec::new()).into(),
770            vm,
771        )
772    }
773}
774
775impl SelfIter for PyListReverseIterator {}
776impl IterNext for PyListReverseIterator {
777    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
778        zelf.internal.lock().rev_next(|list, pos| {
779            let vec = list.borrow_vec();
780            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
781        })
782    }
783}
784
785fn vectorcall_list(
786    zelf_obj: &PyObject,
787    args: Vec<PyObjectRef>,
788    nargs: usize,
789    kwnames: Option<&[PyObjectRef]>,
790    vm: &VirtualMachine,
791) -> PyResult {
792    let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
793    let obj = PyList::default().into_ref_with_type(vm, zelf.to_owned())?;
794    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
795    PyList::slot_init(obj.clone().into(), func_args, vm)?;
796    Ok(obj.into())
797}
798
799pub fn init(context: &'static Context) {
800    let list_type = &context.types.list_type;
801    PyList::extend_class(context, list_type);
802    list_type.slots.vectorcall.store(Some(vectorcall_list));
803
804    PyListIterator::extend_class(context, context.types.list_iterator_type);
805    PyListReverseIterator::extend_class(context, context.types.list_reverseiterator_type);
806}