Skip to main content

rustpython_vm/builtins/
list.rs

1use super::{
2    PositionIterInternal, PyGenericAlias, PyTupleRef, PyType, PyTypeRef,
3    iter::{builtins_iter, builtins_reversed},
4    locked_next, locked_rev_next,
5};
6use crate::atomic_func;
7use crate::common::lock::{
8    PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard,
9};
10use crate::object::{Traverse, TraverseFn};
11use crate::{
12    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
13    builtins::{PyFloat, PyInt, PyStr, PyTuple},
14    class::{PyClassDef, PyClassImpl},
15    convert::ToPyObject,
16    function::{Either, FuncArgs, OptionalArg, PyComparisonValue, PySsize},
17    protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
18    recursion::ReprGuard,
19    sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt},
20    sliceable::{SaturatedSlice, SequenceIndex, SliceableSequenceOp},
21    sorting::timsort,
22    types::{
23        AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable,
24        PyComparisonOp, Representable, RichCompareFunc, SelfIter,
25    },
26    vm::VirtualMachine,
27};
28use rustpython_common::wtf8::Wtf8Buf;
29
30use alloc::fmt;
31use core::cell::Cell;
32use core::ops::DerefMut;
33use core::ptr::NonNull;
34use core::sync::atomic::{AtomicU32, Ordering};
35
36#[pyclass(module = false, name = "list", unhashable = true, traverse = "manual")]
37#[derive(Default)]
38pub struct PyList {
39    elements: PyRwLock<Vec<PyObjectRef>>,
40    mutation_counter: AtomicU32,
41}
42
43impl fmt::Debug for PyList {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        // TODO: implement more detailed, non-recursive Debug formatter
46        f.write_str("list")
47    }
48}
49
50impl From<Vec<PyObjectRef>> for PyList {
51    fn from(elements: Vec<PyObjectRef>) -> Self {
52        Self {
53            elements: PyRwLock::new(elements),
54            mutation_counter: AtomicU32::new(0),
55        }
56    }
57}
58
59impl FromIterator<PyObjectRef> for PyList {
60    fn from_iter<T: IntoIterator<Item = PyObjectRef>>(iter: T) -> Self {
61        Vec::from_iter(iter).into()
62    }
63}
64
65// SAFETY: Traverse properly visits all owned PyObjectRefs
66unsafe impl Traverse for PyList {
67    fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
68        self.elements.traverse(traverse_fn);
69    }
70
71    fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
72        // During GC, we use interior mutability to access elements.
73        // This is safe because during GC collection, the object is unreachable
74        // and no other code should be accessing it.
75        if let Some(mut guard) = self.elements.try_write() {
76            if out.is_empty() {
77                // Reuse the allocation so deallocation does not need more memory.
78                *out = core::mem::take(&mut *guard);
79            } else {
80                out.extend(guard.drain(..));
81            }
82        }
83    }
84}
85
86thread_local! {
87    static LIST_FREELIST: Cell<crate::object::FreeList<PyList>> = const { Cell::new(crate::object::FreeList::new()) };
88}
89
90impl PyPayload for PyList {
91    const MAX_FREELIST: usize = 80;
92    const HAS_FREELIST: bool = true;
93
94    #[inline]
95    fn class(ctx: &Context) -> &'static Py<PyType> {
96        ctx.types.list_type
97    }
98
99    #[inline]
100    unsafe fn freelist_push(obj: *mut PyObject) -> bool {
101        LIST_FREELIST
102            .try_with(|fl| {
103                let mut list = fl.take();
104                let stored = if list.len() < Self::MAX_FREELIST {
105                    list.push(obj);
106                    true
107                } else {
108                    false
109                };
110                fl.set(list);
111                stored
112            })
113            .unwrap_or(false)
114    }
115
116    #[inline]
117    unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
118        LIST_FREELIST
119            .try_with(|fl| {
120                let mut list = fl.take();
121                let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
122                fl.set(list);
123                result
124            })
125            .ok()
126            .flatten()
127    }
128}
129
130impl ToPyObject for Vec<PyObjectRef> {
131    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
132        PyList::from(self).into_ref(&vm.ctx).into()
133    }
134}
135
136impl Py<PyList> {
137    #[inline]
138    pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> {
139        self.payload().borrow_vec()
140    }
141
142    #[inline]
143    pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
144        self.payload().borrow_vec_mut()
145    }
146}
147
148impl PyList {
149    #[deprecated(note = "use PyList::from(...).into_ref() instead")]
150    pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
151        Self::from(elements).into_ref(ctx)
152    }
153
154    pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> {
155        PyRwLockReadGuard::map(self.elements.read(), |v| &**v)
156    }
157
158    pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
159        let guard = self.elements.write();
160        self.mutation_counter.fetch_add(1, Ordering::Relaxed);
161        guard
162    }
163
164    fn repeat(&self, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
165        let elements = &*self.borrow_vec();
166        let v = elements.mul(vm, n)?;
167        Ok(Self::from(v).into_ref(&vm.ctx))
168    }
169
170    fn irepeat(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
171        if n <= 0 {
172            Py::<Self>::clear(&zelf);
173        } else {
174            zelf.borrow_vec_mut().imul(vm, n)?;
175        }
176        Ok(zelf)
177    }
178}
179
180#[derive(FromArgs, Default, Traverse)]
181pub(crate) struct SortOptions {
182    #[pyarg(named, optional)]
183    key: Option<PyObjectRef>,
184    #[pytraverse(skip)]
185    #[pyarg(named, default)]
186    reverse: bool,
187}
188
189pub type PyListRef = PyRef<PyList>;
190
191#[derive(FromArgs)]
192struct PopArgs {
193    #[pyarg(positional, default = -1)]
194    index: isize,
195}
196
197impl PyList {
198    pub(crate) fn append(&self, object: PyObjectRef) {
199        self.borrow_vec_mut().push(object);
200    }
201
202    pub(crate) fn sort(&self, options: SortOptions, vm: &VirtualMachine) -> PyResult<()> {
203        // replace list contents with [] for duration of sort.
204        // this prevents keyfunc from messing with the list and makes it easy to
205        // check if it tries to append elements to it.
206        let (mut elements, version_before) = {
207            let mut guard = self.elements.write();
208            let version_before = self.mutation_counter.load(Ordering::Relaxed);
209            (core::mem::take(&mut *guard), version_before)
210        };
211        let res = do_sort(vm, &mut elements, options.key.as_deref(), options.reverse);
212        let mutated = {
213            let mut guard = self.elements.write();
214            let mutated = self.mutation_counter.load(Ordering::Relaxed) != version_before;
215            core::mem::swap(&mut *guard, &mut elements);
216            mutated
217        };
218        res?;
219
220        if mutated {
221            return Err(vm.new_value_error("list modified during sort"));
222        }
223
224        Ok(())
225    }
226
227    fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
228        let other = other.downcast_ref::<Self>().ok_or_else(|| {
229            vm.new_type_error(format!(
230                "Cannot add {} and {}",
231                Self::class(&vm.ctx).name(),
232                other.class().name()
233            ))
234        })?;
235        let mut elements = self.borrow_vec().to_vec();
236        elements.extend(other.borrow_vec().iter().cloned());
237        Ok(Self::from(elements).into_ref(&vm.ctx))
238    }
239
240    fn __add__(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
241        self.concat(other, vm)
242    }
243
244    fn inplace_concat(
245        zelf: &Py<Self>,
246        other: &PyObject,
247        vm: &VirtualMachine,
248    ) -> PyResult<PyObjectRef> {
249        zelf.extend(other.to_owned(), vm)?;
250        Ok(zelf.to_owned().into())
251    }
252
253    fn __iadd__(
254        zelf: PyRef<Self>,
255        other: PyObjectRef,
256        vm: &VirtualMachine,
257    ) -> PyResult<PyRef<Self>> {
258        zelf.extend(other, vm)?;
259        Ok(zelf)
260    }
261
262    pub fn __len__(&self) -> usize {
263        self.borrow_vec().len()
264    }
265
266    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
267        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
268            SequenceIndex::Int(i) => {
269                let vec = self.borrow_vec();
270                let pos = vec
271                    .wrap_index(i)
272                    .ok_or_else(|| vm.new_index_error("list index out of range"))?;
273                Ok(vec.do_get(pos))
274            }
275            SequenceIndex::Slice(slice) => self
276                .borrow_vec()
277                .getitem_by_slice(vm, slice)
278                .map(|x| vm.ctx.new_list(x).into()),
279        }
280    }
281
282    fn assign_item(
283        &self,
284        index: isize,
285        value: Option<PyObjectRef>,
286        vm: &VirtualMachine,
287    ) -> PyResult<()> {
288        let removed = {
289            let mut elements = self.borrow_vec_mut();
290            let index = elements
291                .wrap_index(index)
292                .ok_or_else(|| vm.new_index_error("list assignment index out of range"))?;
293            if let Some(value) = value {
294                core::mem::replace(&mut elements[index], value)
295            } else {
296                elements.remove(index)
297            }
298        };
299        drop(removed);
300        Ok(())
301    }
302
303    fn assign_slice(
304        &self,
305        slice: SaturatedSlice,
306        items: Option<&[PyObjectRef]>,
307        vm: &VirtualMachine,
308    ) -> PyResult<()> {
309        let removed = {
310            let mut elements = self.borrow_vec_mut();
311            let (range, step, slice_len) = slice.adjust_indices(elements.len());
312            if step != 1
313                && let Some(items) = &items
314                && items.len() != slice_len
315            {
316                return Err(vm.new_value_error(format!(
317                    "attempt to assign sequence of size {} to extended slice of size {}",
318                    items.len(),
319                    slice_len
320                )));
321            }
322            let new_len = items.map_or(0, <[PyObjectRef]>::len);
323            if step == 1 && slice_len == elements.len() && new_len == 0 {
324                core::mem::take(&mut *elements)
325            } else {
326                // Keep removed references alive until the complete result is visible
327                // and the write guard is released. Reserve before changing the list.
328                let mut removed = Vec::new();
329                removed
330                    .try_reserve_exact(slice_len)
331                    .map_err(|_| vm.no_memory_error())?;
332                if step == 1 {
333                    elements
334                        .try_reserve(new_len.saturating_sub(slice_len))
335                        .map_err(|_| vm.no_memory_error())?;
336                    removed
337                        .extend(elements.splice(range, items.unwrap_or_default().iter().cloned()));
338                } else if let Some(items) = items {
339                    for (index, item) in slice.iter(elements.len()).zip(items) {
340                        removed.push(core::mem::replace(&mut elements[index], item.clone()));
341                    }
342                } else {
343                    let mut indexes = slice.iter(elements.len()).positive_order().peekable();
344                    let mut index = range.start;
345                    removed.extend(elements.extract_if(range, |_| {
346                        let remove = indexes.peek() == Some(&index);
347                        if remove {
348                            indexes.next();
349                        }
350                        index += 1;
351                        remove
352                    }));
353                }
354                removed
355            }
356        };
357        if slice.step() == 1 {
358            removed.into_iter().rev().for_each(drop);
359        } else {
360            drop(removed);
361        }
362        Ok(())
363    }
364
365    fn _setitem(&self, needle: &PyObject, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
366        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
367            SequenceIndex::Int(index) => self.assign_item(index, Some(value), vm),
368            SequenceIndex::Slice(slice) => {
369                let items = extract_cloned(&value, self, vm)?;
370                let result = self.assign_slice(slice, Some(&items), vm);
371                items.into_iter().rev().for_each(drop);
372                result
373            }
374        }
375    }
376
377    fn __setitem__(
378        &self,
379        needle: &PyObject,
380        value: PyObjectRef,
381        vm: &VirtualMachine,
382    ) -> PyResult<()> {
383        self._setitem(needle, value, vm)
384    }
385
386    fn __mul__(&self, n: PySsize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
387        self.repeat(n, vm)
388    }
389
390    fn __imul__(zelf: PyRef<Self>, n: PySsize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
391        Self::irepeat(zelf, n, vm)
392    }
393
394    pub(crate) fn __contains__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
395        self.mut_contains(vm, needle)
396    }
397
398    fn _delitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
399        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
400            SequenceIndex::Int(i) => self.assign_item(i, None, vm),
401            SequenceIndex::Slice(slice) => self.assign_slice(slice, None, vm),
402        }
403    }
404
405    fn __delitem__(&self, subscript: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
406        self._delitem(subscript, vm)
407    }
408}
409
410#[pyclass(
411    with(
412        Constructor,
413        Initializer,
414        AsMapping,
415        Iterable,
416        Comparable,
417        AsSequence,
418        Representable
419    ),
420    flags(BASETYPE, SEQUENCE, _MATCH_SELF)
421)]
422impl Py<PyList> {
423    #[pymethod]
424    pub(crate) fn append(&self, object: PyObjectRef) {
425        self.payload.append(object)
426    }
427
428    #[pymethod]
429    pub(crate) fn extend(&self, iterable: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
430        if let Some(list) = iterable.downcast_ref::<PyList>()
431            && core::ptr::eq(self, list)
432        {
433            return self.borrow_vec_mut().imul(vm, 2);
434        }
435        if let Some(tuple) = iterable.downcast_ref_if_exact::<PyTuple>(vm) {
436            let mut elements = self.borrow_vec_mut();
437            elements
438                .try_reserve(tuple.len())
439                .map_err(|_| vm.no_memory_error())?;
440            elements.extend_from_slice(tuple.as_slice());
441            return Ok(());
442        }
443        let cls = iterable.class();
444        if cls.is(vm.ctx.types.list_type)
445            || cls.is(vm.ctx.types.set_type)
446            || cls.is(vm.ctx.types.frozenset_type)
447            || cls.is(vm.ctx.types.dict_type)
448            || cls.is(vm.ctx.types.dict_keys_type)
449            || cls.is(vm.ctx.types.dict_values_type)
450            || cls.is(vm.ctx.types.dict_items_type)
451        {
452            // Snapshot mutable native sources before locking the destination.
453            let mut new_elements =
454                vm.extract_elements_sized(&iterable, &|| self.borrow_vec().len(), Ok)?;
455            let mut elements = self.borrow_vec_mut();
456            elements
457                .try_reserve(new_elements.len())
458                .map_err(|_| vm.no_memory_error())?;
459            elements.append(&mut new_elements);
460            return Ok(());
461        }
462
463        let iter = iterable.clone().get_iter(vm)?;
464        // Count the destination after the hint: both calls can mutate it.
465        let hint = vm.length_hint_opt(iterable.clone())?.unwrap_or(8);
466        {
467            let mut elements = self.borrow_vec_mut();
468            if elements.len() <= (isize::MAX as usize) - hint {
469                elements
470                    .try_reserve_exact(hint)
471                    .map_err(|_| vm.no_memory_error())?;
472            }
473        }
474        for item in iter.iter::<PyObjectRef>(vm)? {
475            let item = item?;
476            let mut elements = self.borrow_vec_mut();
477            if elements.len() == elements.capacity() {
478                elements.try_reserve(1).map_err(|_| vm.no_memory_error())?;
479            }
480            elements.push(item);
481        }
482        {
483            // Trim an excessive hint, preserving normal growth slack. Keep the
484            // iterator alive until resizing is done and the lock is released.
485            let mut elements = self.elements.write();
486            if elements.len() < elements.capacity().div_ceil(2) {
487                elements.shrink_to_fit();
488            }
489        }
490        Ok(())
491    }
492
493    #[pymethod]
494    pub(crate) fn insert(&self, index: PySsize, object: PyObjectRef) {
495        let mut elements = self.borrow_vec_mut();
496        let index = elements.saturate_index(index);
497        elements.insert(index, object);
498    }
499
500    #[pymethod]
501    fn clear(&self) {
502        let removed = core::mem::take(self.borrow_vec_mut().deref_mut());
503        removed.into_iter().rev().for_each(drop);
504    }
505
506    #[pymethod]
507    fn copy(&self, vm: &VirtualMachine) -> PyRef<PyList> {
508        PyList::from(self.borrow_vec().to_vec()).into_ref(&vm.ctx)
509    }
510
511    #[pymethod]
512    fn __sizeof__(&self) -> usize {
513        core::mem::size_of::<PyList>()
514            + self.elements.read().capacity() * core::mem::size_of::<PyObjectRef>()
515    }
516
517    #[pymethod]
518    fn reverse(&self) {
519        self.borrow_vec_mut().reverse();
520    }
521
522    #[pymethod]
523    fn __reversed__(zelf: PyRef<PyList>) -> PyListReverseIterator {
524        let position = zelf.__len__().saturating_sub(1);
525        PyListReverseIterator {
526            internal: PyMutex::new(PositionIterInternal::new(zelf, position)),
527        }
528    }
529
530    #[pymethod(coexist)]
531    fn __getitem__(&self, index: PyObjectRef, vm: &VirtualMachine) -> PyResult {
532        self._getitem(&index, vm)
533    }
534
535    #[pymethod]
536    fn count(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
537        self.mut_count(vm, &value)
538    }
539
540    #[pymethod]
541    fn index(
542        &self,
543        value: PyObjectRef,
544        range: OptionalRangeArgs,
545        vm: &VirtualMachine,
546    ) -> PyResult<usize> {
547        let (start, stop) = range.saturate(self.__len__(), vm)?;
548        let index = self.mut_index_range(vm, &value, start..stop)?;
549        if let Some(index) = index.into() {
550            Ok(index)
551        } else {
552            Err(vm.new_value_error(format!("'{}' is not in list", value.str(vm)?)))
553        }
554    }
555
556    #[pymethod]
557    fn pop(&self, args: PopArgs, vm: &VirtualMachine) -> PyResult {
558        let mut index = args.index;
559        let mut elements = self.borrow_vec_mut();
560        if index < 0 {
561            index += elements.len() as isize;
562        }
563        if elements.is_empty() {
564            Err(vm.new_index_error("pop from empty list"))
565        } else if index < 0 || index as usize >= elements.len() {
566            Err(vm.new_index_error("pop index out of range"))
567        } else {
568            Ok(elements.remove(index as usize))
569        }
570    }
571
572    #[pymethod]
573    fn remove(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
574        let index = self.mut_index(vm, &value)?;
575
576        if let Some(index) = index.into() {
577            // defer delete out of borrow
578            let removed = {
579                let mut elements = self.borrow_vec_mut();
580                (index < elements.len()).then(|| elements.remove(index))
581            };
582            drop(removed);
583            Ok(())
584        } else {
585            Err(vm.new_value_error(format!("'{}' is not in list", value.str(vm)?)))
586        }
587    }
588
589    #[pymethod]
590    pub(crate) fn sort(&self, options: SortOptions, vm: &VirtualMachine) -> PyResult<()> {
591        self.payload.sort(options, vm)
592    }
593
594    #[pyclassmethod]
595    fn __class_getitem__(
596        cls: PyTypeRef,
597        object: PyObjectRef,
598        vm: &VirtualMachine,
599    ) -> PyResult<PyGenericAlias> {
600        PyGenericAlias::from_args(cls, object, vm)
601    }
602}
603
604fn extract_cloned(
605    obj: &PyObject,
606    zelf: &PyList,
607    vm: &VirtualMachine,
608) -> PyResult<Vec<PyObjectRef>> {
609    use crate::builtins::PyTuple;
610    let mut v = Vec::new();
611    if let Some(tuple) = obj.downcast_ref_if_exact::<PyTuple>(vm) {
612        v.try_reserve_exact(tuple.len())
613            .map_err(|_| vm.no_memory_error())?;
614        v.extend_from_slice(tuple.as_slice());
615    } else if let Some(list) = obj.downcast_ref::<PyList>()
616        && (obj.class().is(vm.ctx.types.list_type) || core::ptr::eq(zelf, list.payload()))
617    {
618        let elements = list.borrow_vec();
619        v.try_reserve_exact(elements.len())
620            .map_err(|_| vm.no_memory_error())?;
621        v.extend_from_slice(&elements);
622    } else {
623        let iter = obj
624            .to_owned()
625            .get_iter(vm)?
626            .into_iter_sized::<PyObjectRef>(vm)?;
627        let len = iter.size_hint().0;
628        v.try_reserve_exact(len).map_err(|_| vm.no_memory_error())?;
629        let result = (|| {
630            for x in iter {
631                let item = x?;
632                if v.len() == v.capacity() {
633                    v.try_reserve(1).map_err(|_| vm.no_memory_error())?;
634                }
635                v.push(item);
636            }
637            Ok(())
638        })();
639        if let Err(error) = result {
640            v.into_iter().rev().for_each(drop);
641            return Err(error);
642        }
643    }
644    Ok(v)
645}
646
647impl MutObjectSequenceOp for PyList {
648    type Inner = [PyObjectRef];
649
650    fn do_get(index: usize, inner: &[PyObjectRef]) -> Option<&PyObject> {
651        inner.get(index).map(|r| r.as_ref())
652    }
653
654    fn do_lock(&self) -> impl core::ops::Deref<Target = [PyObjectRef]> {
655        self.borrow_vec()
656    }
657}
658
659impl Constructor for PyList {
660    type Args = FuncArgs;
661
662    fn py_new(_cls: &Py<PyType>, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<Self> {
663        Ok(Self::default())
664    }
665}
666
667impl Initializer for PyList {
668    type Args = crate::function::PositionalIterable;
669
670    fn slot_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
671        let list_type = vm.ctx.types.list_type;
672        let cls = zelf.class();
673        let uses_list_new = {
674            let cls_new = cls.slots.new.load().map(crate::types::fn_addr);
675            let list_new = list_type.slots.new.load().map(crate::types::fn_addr);
676            cls_new == list_new
677        };
678        let iterable = if cls.is(list_type) || uses_list_new {
679            args.bind_for(vm, Self::NAME)?
680        } else {
681            match args.args.as_slice() {
682                [] => Self::Args {
683                    iterable: OptionalArg::Missing,
684                },
685                [iterable] => Self::Args {
686                    iterable: OptionalArg::Present(iterable.clone()),
687                },
688                slice => {
689                    return Err(vm.new_arity_type_error(Self::NAME, 0..=1, slice.len()));
690                }
691            }
692        };
693        Self::init(zelf.try_to_ref(vm)?, iterable, vm)
694    }
695
696    fn init(zelf: &Py<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
697        zelf.clear();
698        if let OptionalArg::Present(iterable) = args.iterable {
699            zelf.extend(iterable, vm)?;
700        }
701        Ok(())
702    }
703}
704
705impl AsMapping for PyList {
706    fn as_mapping() -> &'static PyMappingMethods {
707        static AS_MAPPING: PyMappingMethods = PyMappingMethods {
708            length: atomic_func!(|mapping, _vm| Ok(PyList::mapping_downcast(mapping).__len__())),
709            subscript: atomic_func!(
710                |mapping, needle, vm| PyList::mapping_downcast(mapping)._getitem(needle, vm)
711            ),
712            ass_subscript: atomic_func!(|mapping, needle, value, vm| {
713                let zelf = PyList::mapping_downcast(mapping);
714                if let Some(value) = value {
715                    zelf._setitem(needle, value, vm)
716                } else {
717                    zelf._delitem(needle, vm)
718                }
719            }),
720        };
721        &AS_MAPPING
722    }
723}
724
725impl AsSequence for PyList {
726    fn as_sequence() -> &'static PySequenceMethods {
727        static AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
728            length: atomic_func!(|seq, _vm| Ok(PyList::sequence_downcast(seq).__len__())),
729            concat: atomic_func!(|seq, other, vm| {
730                PyList::sequence_downcast(seq)
731                    .concat(other, vm)
732                    .map(|x| x.into())
733            }),
734            repeat: atomic_func!(|seq, n, vm| {
735                PyList::sequence_downcast(seq)
736                    .repeat(n, vm)
737                    .map(|x| x.into())
738            }),
739            item: atomic_func!(|seq, i, vm| {
740                let list = PyList::sequence_downcast(seq);
741                let vec = list.borrow_vec();
742                let pos = vec
743                    .wrap_index(i)
744                    .ok_or_else(|| vm.new_index_error("list index out of range"))?;
745                Ok(vec.do_get(pos))
746            }),
747            ass_item: atomic_func!(|seq, i, value, vm| {
748                PyList::sequence_downcast(seq).assign_item(i, value, vm)
749            }),
750            contains: atomic_func!(|seq, target, vm| {
751                let zelf = PyList::sequence_downcast(seq);
752                zelf.mut_contains(vm, target)
753            }),
754            inplace_concat: atomic_func!(|seq, other, vm| {
755                let zelf = PyList::sequence_downcast(seq);
756                PyList::inplace_concat(zelf, other, vm)
757            }),
758            inplace_repeat: atomic_func!(|seq, n, vm| {
759                let zelf = PyList::sequence_downcast(seq);
760                Ok(PyList::irepeat(zelf.to_owned(), n, vm)?.into())
761            }),
762        };
763        &AS_SEQUENCE
764    }
765}
766
767impl Iterable for PyList {
768    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
769        Ok(PyListIterator {
770            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
771        }
772        .into_pyobject(vm))
773    }
774}
775
776impl Comparable for PyList {
777    fn cmp(
778        zelf: &Py<Self>,
779        other: &PyObject,
780        op: PyComparisonOp,
781        vm: &VirtualMachine,
782    ) -> PyResult<PyComparisonValue> {
783        if let Some(res) = op.identical_optimization(zelf, other) {
784            return Ok(res.into());
785        }
786        let other = class_or_notimplemented!(Self, other);
787        // Item comparison can mutate either list (clear, resize). Holding the
788        // element lock across that callback deadlocks on the write side.
789        crate::iter::richcompare_mutating_seqs(
790            |i| {
791                let a = zelf.borrow_vec();
792                (a.len(), a.get(i).cloned())
793            },
794            |i| {
795                let b = other.borrow_vec();
796                (b.len(), b.get(i).cloned())
797            },
798            op,
799            vm,
800        )
801        .map(PyComparisonValue::Implemented)
802    }
803}
804
805impl Representable for PyList {
806    #[inline]
807    fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
808        if zelf.__len__() == 0 {
809            return Ok(vm.ctx.intern_str("[]").to_owned());
810        }
811
812        if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
813            // Clone each element before calling repr to release the read lock.
814            // Element repr may mutate the list (e.g., list.clear()), which
815            // needs a write lock and would deadlock if read lock is held.
816            let mut writer = Wtf8Buf::new();
817            writer.push_char('[');
818
819            let mut first = true;
820            let mut i = 0;
821            loop {
822                let item = zelf.borrow_vec().get(i).cloned();
823                let Some(item) = item else {
824                    break;
825                };
826
827                if first {
828                    first = false;
829                } else {
830                    writer.push_str(", ");
831                }
832
833                writer.push_wtf8(item.repr(vm)?.as_wtf8());
834
835                i += 1;
836            }
837
838            writer.push_char(']');
839            Ok(vm.ctx.new_str(writer))
840        } else {
841            Ok(vm.ctx.intern_str("[...]").to_owned())
842        }
843    }
844
845    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
846        unreachable!("repr() is overridden directly")
847    }
848}
849
850enum Elem {
851    Str,
852    Int,
853    Float,
854    Object(RichCompareFunc),
855    Generic,
856}
857
858enum PreSort {
859    Str,
860    Int,
861    Float,
862    Object(RichCompareFunc),
863    Tuple(Elem),
864    Generic,
865}
866
867impl From<Elem> for PreSort {
868    fn from(e: Elem) -> Self {
869        match e {
870            Elem::Str => Self::Str,
871            Elem::Int => Self::Int,
872            Elem::Float => Self::Float,
873            Elem::Object(f) => Self::Object(f),
874            Elem::Generic => Self::Generic,
875        }
876    }
877}
878
879fn classify(class: &Py<PyType>, vm: &VirtualMachine) -> Elem {
880    if class.is(vm.ctx.types.str_type) {
881        Elem::Str
882    } else if class.is(vm.ctx.types.int_type) {
883        Elem::Int
884    } else if class.is(vm.ctx.types.float_type) {
885        Elem::Float
886    } else if let Some(f) = class.slots.richcompare.load() {
887        Elem::Object(f)
888    } else {
889        Elem::Generic
890    }
891}
892
893fn pre_sort_check<'a>(
894    mut keys: impl Iterator<Item = &'a PyObject>,
895    vm: &VirtualMachine,
896) -> PreSort {
897    let Some(first) = keys.next() else {
898        return PreSort::Generic;
899    };
900
901    if let Some(t) = first
902        .downcast_ref_if_exact::<PyTuple>(vm)
903        .filter(|t| !t.as_slice().is_empty())
904    {
905        pre_sort_check_tuples(&t.as_slice()[0], keys, vm)
906    } else {
907        let class = first.class();
908        if keys.all(|k| k.class().is(class)) {
909            classify(class, vm).into()
910        } else {
911            PreSort::Generic
912        }
913    }
914}
915
916fn pre_sort_check_tuples<'a>(
917    first_elem: &PyObject,
918    keys: impl Iterator<Item = &'a PyObject>,
919    vm: &VirtualMachine,
920) -> PreSort {
921    let class = first_elem.class();
922    let mut all_same_type = true;
923
924    for k in keys {
925        let Some(t) = k
926            .downcast_ref_if_exact::<PyTuple>(vm)
927            .filter(|t| !t.as_slice().is_empty())
928        else {
929            return PreSort::Generic;
930        };
931        if all_same_type && !t.as_slice()[0].class().is(class) {
932            all_same_type = false;
933        }
934    }
935
936    let elem = if !all_same_type || class.is(vm.ctx.types.tuple_type) {
937        Elem::Generic
938    } else {
939        classify(class, vm)
940    };
941    PreSort::Tuple(elem)
942}
943
944fn str_lt(a: &PyObject, b: &PyObject) -> bool {
945    a.downcast_ref::<PyStr>().unwrap().as_bytes() < b.downcast_ref::<PyStr>().unwrap().as_bytes()
946}
947
948fn int_lt(a: &PyObject, b: &PyObject) -> bool {
949    a.downcast_ref::<PyInt>().unwrap().as_bigint() < b.downcast_ref::<PyInt>().unwrap().as_bigint()
950}
951
952fn float_lt(a: &PyObject, b: &PyObject) -> bool {
953    a.downcast_ref::<PyFloat>().unwrap().to_f64() < b.downcast_ref::<PyFloat>().unwrap().to_f64()
954}
955
956fn object_lt(
957    cmp: RichCompareFunc,
958    a: &PyObject,
959    b: &PyObject,
960    vm: &VirtualMachine,
961) -> PyResult<bool> {
962    #[allow(unpredictable_function_pointer_comparisons)]
963    if a.class().slots().richcompare.load() != Some(cmp) {
964        return a.rich_compare_bool(b, PyComparisonOp::Lt, vm);
965    }
966    match cmp(a, b, PyComparisonOp::Lt, vm)? {
967        Either::B(PyComparisonValue::Implemented(v)) => Ok(v),
968        Either::B(PyComparisonValue::NotImplemented) => {
969            a.rich_compare_bool(b, PyComparisonOp::Lt, vm)
970        }
971        Either::A(obj) => {
972            if obj.is(&vm.ctx.not_implemented) {
973                a.rich_compare_bool(b, PyComparisonOp::Lt, vm)
974            } else {
975                obj.try_to_bool(vm)
976            }
977        }
978    }
979}
980
981fn elem_lt(elem: &Elem, a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
982    match elem {
983        Elem::Str => Ok(str_lt(a, b)),
984        Elem::Int => Ok(int_lt(a, b)),
985        Elem::Float => Ok(float_lt(a, b)),
986        Elem::Object(f) => object_lt(*f, a, b, vm),
987        Elem::Generic => a.rich_compare_bool(b, PyComparisonOp::Lt, vm),
988    }
989}
990
991fn tuple_lt(elem: &Elem, a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
992    let a = a.downcast_ref::<PyTuple>().unwrap().as_slice();
993    let b = b.downcast_ref::<PyTuple>().unwrap().as_slice();
994
995    let mut i = 0;
996    while i < a.len() && i < b.len() {
997        if !a[i].rich_compare_bool(&b[i], PyComparisonOp::Eq, vm)? {
998            break;
999        }
1000        i += 1;
1001    }
1002    if i >= a.len() || i >= b.len() {
1003        return Ok(a.len() < b.len());
1004    }
1005    if i == 0 {
1006        elem_lt(elem, &a[0], &b[0], vm)
1007    } else {
1008        a[i].rich_compare_bool(&b[i], PyComparisonOp::Lt, vm)
1009    }
1010}
1011
1012fn timsort_by<T, K, L>(items: &mut [T], reverse: bool, key: &K, mut lt: L) -> PyResult<()>
1013where
1014    T: Clone,
1015    K: Fn(&T) -> &PyObject,
1016    L: FnMut(&PyObject, &PyObject) -> PyResult<bool>,
1017{
1018    timsort(items, &mut |a, b| {
1019        let (a, b) = if reverse {
1020            (key(b), key(a))
1021        } else {
1022            (key(a), key(b))
1023        };
1024        lt(a, b)
1025    })
1026}
1027
1028fn timsort_specialized<T, K>(
1029    vm: &VirtualMachine,
1030    items: &mut [T],
1031    reverse: bool,
1032    key: K,
1033) -> PyResult<()>
1034where
1035    T: Clone,
1036    K: Fn(&T) -> &PyObject,
1037{
1038    match pre_sort_check(items.iter().map(&key), vm) {
1039        PreSort::Str => timsort_by(items, reverse, &key, |a, b| Ok(str_lt(a, b))),
1040        PreSort::Int => timsort_by(items, reverse, &key, |a, b| Ok(int_lt(a, b))),
1041        PreSort::Float => timsort_by(items, reverse, &key, |a, b| Ok(float_lt(a, b))),
1042        PreSort::Object(cmp) => timsort_by(items, reverse, &key, |a, b| object_lt(cmp, a, b, vm)),
1043        PreSort::Tuple(elem) => timsort_by(items, reverse, &key, |a, b| tuple_lt(&elem, a, b, vm)),
1044        PreSort::Generic => timsort_by(items, reverse, &key, |a, b| {
1045            a.rich_compare_bool(b, PyComparisonOp::Lt, vm)
1046        }),
1047    }
1048}
1049
1050fn do_sort(
1051    vm: &VirtualMachine,
1052    values: &mut Vec<PyObjectRef>,
1053    key_func: Option<&PyObject>,
1054    reverse: bool,
1055) -> PyResult<()> {
1056    if let Some(key_func) = key_func {
1057        let mut items = values
1058            .iter()
1059            .map(|x| Ok((x.clone(), key_func.call((x.clone(),), vm)?)))
1060            .collect::<Result<Vec<_>, _>>()?;
1061        timsort_specialized(
1062            vm,
1063            &mut items,
1064            reverse,
1065            |item: &(PyObjectRef, PyObjectRef)| &*item.1,
1066        )?;
1067        *values = items.into_iter().map(|(val, _)| val).collect();
1068    } else {
1069        timsort_specialized(vm, values, reverse, |x| x)?
1070    }
1071
1072    Ok(())
1073}
1074
1075#[pyclass(module = false, name = "list_iterator", traverse)]
1076#[derive(Debug)]
1077pub(crate) struct PyListIterator {
1078    internal: PyMutex<PositionIterInternal<PyListRef>>,
1079}
1080
1081impl PyPayload for PyListIterator {
1082    #[inline]
1083    fn class(ctx: &Context) -> &'static Py<PyType> {
1084        ctx.types.list_iterator_type
1085    }
1086}
1087
1088#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1089impl Py<PyListIterator> {
1090    #[pymethod]
1091    fn __length_hint__(&self) -> usize {
1092        self.internal.lock().length_hint(|obj| obj.__len__())
1093    }
1094
1095    #[pymethod]
1096    fn __setstate__(&self, object: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1097        self.internal
1098            .lock()
1099            .set_state(&object, |obj, pos| pos.min(obj.__len__()), vm)
1100    }
1101
1102    #[pymethod]
1103    fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
1104        let func = builtins_iter(vm)?;
1105        Ok(self.internal.lock().reduce(
1106            func,
1107            |x| x.clone().into(),
1108            |vm| vm.ctx.new_list(Vec::new()).into(),
1109            vm,
1110        ))
1111    }
1112}
1113
1114impl PyListIterator {
1115    /// Fast path for FOR_ITER specialization.
1116    pub(crate) fn fast_next(&self) -> Option<PyObjectRef> {
1117        locked_next(&self.internal, |list, pos| {
1118            let vec = list.borrow_vec();
1119            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
1120        })
1121        .ok()
1122        .and_then(|r| match r {
1123            PyIterReturn::Return(v) => Some(v),
1124            PyIterReturn::StopIteration(_) => None,
1125        })
1126    }
1127}
1128
1129impl SelfIter for PyListIterator {}
1130impl IterNext for PyListIterator {
1131    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1132        locked_next(&zelf.internal, |list, pos| {
1133            let vec = list.borrow_vec();
1134            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
1135        })
1136    }
1137}
1138
1139#[pyclass(module = false, name = "list_reverseiterator", traverse)]
1140#[derive(Debug)]
1141pub(crate) struct PyListReverseIterator {
1142    internal: PyMutex<PositionIterInternal<PyListRef>>,
1143}
1144
1145impl PyPayload for PyListReverseIterator {
1146    #[inline]
1147    fn class(ctx: &Context) -> &'static Py<PyType> {
1148        ctx.types.list_reverseiterator_type
1149    }
1150}
1151
1152#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
1153impl Py<PyListReverseIterator> {
1154    #[pymethod]
1155    fn __length_hint__(&self) -> usize {
1156        self.internal.lock().rev_length_hint(|obj| obj.__len__())
1157    }
1158
1159    #[pymethod]
1160    fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1161        self.internal
1162            .lock()
1163            .set_state(&state, |obj, pos| pos.min(obj.__len__()), vm)
1164    }
1165
1166    #[pymethod]
1167    fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
1168        let func = builtins_reversed(vm)?;
1169        Ok(self.internal.lock().reduce(
1170            func,
1171            |x| x.clone().into(),
1172            |vm| vm.ctx.new_list(Vec::new()).into(),
1173            vm,
1174        ))
1175    }
1176}
1177
1178impl SelfIter for PyListReverseIterator {}
1179impl IterNext for PyListReverseIterator {
1180    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
1181        locked_rev_next(&zelf.internal, |list, pos| {
1182            let vec = list.borrow_vec();
1183            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
1184        })
1185    }
1186}
1187
1188fn vectorcall_list(
1189    zelf_obj: &PyObject,
1190    args: Vec<PyObjectRef>,
1191    nargs: usize,
1192    kwnames: Option<&[PyObjectRef]>,
1193    vm: &VirtualMachine,
1194) -> PyResult {
1195    let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1196    let obj = PyList::default().into_ref_with_type(vm, zelf.to_owned())?;
1197    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1198    PyList::slot_init(obj.as_object(), func_args, vm)?;
1199    Ok(obj.into())
1200}
1201
1202pub(crate) fn init(context: &'static Context) {
1203    let list_type = &context.types.list_type;
1204    PyList::extend_class(context, list_type);
1205    list_type.slots.vectorcall.store(Some(vectorcall_list));
1206
1207    PyListIterator::extend_class(context, context.types.list_iterator_type);
1208    PyListReverseIterator::extend_class(context, context.types.list_reverseiterator_type);
1209}