rustpython_vm/builtins/
list.rs

1use super::{PositionIterInternal, PyGenericAlias, PyTupleRef, PyType, PyTypeRef};
2use crate::atomic_func;
3use crate::common::lock::{
4    PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard,
5};
6use crate::{
7    class::PyClassImpl,
8    convert::ToPyObject,
9    function::{ArgSize, FuncArgs, OptionalArg, PyComparisonValue},
10    iter::PyExactSizeIterator,
11    protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
12    recursion::ReprGuard,
13    sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt},
14    sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
15    types::{
16        AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable,
17        PyComparisonOp, Representable, SelfIter, Unconstructible,
18    },
19    utils::collection_repr,
20    vm::VirtualMachine,
21    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
22};
23use std::{fmt, ops::DerefMut};
24
25#[pyclass(module = false, name = "list", unhashable = true, traverse)]
26#[derive(Default)]
27pub struct PyList {
28    elements: PyRwLock<Vec<PyObjectRef>>,
29}
30
31impl fmt::Debug for PyList {
32    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33        // TODO: implement more detailed, non-recursive Debug formatter
34        f.write_str("list")
35    }
36}
37
38impl From<Vec<PyObjectRef>> for PyList {
39    fn from(elements: Vec<PyObjectRef>) -> Self {
40        PyList {
41            elements: PyRwLock::new(elements),
42        }
43    }
44}
45
46impl FromIterator<PyObjectRef> for PyList {
47    fn from_iter<T: IntoIterator<Item = PyObjectRef>>(iter: T) -> Self {
48        Vec::from_iter(iter).into()
49    }
50}
51
52impl PyPayload for PyList {
53    fn class(ctx: &Context) -> &'static Py<PyType> {
54        ctx.types.list_type
55    }
56}
57
58impl ToPyObject for Vec<PyObjectRef> {
59    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
60        PyList::new_ref(self, &vm.ctx).into()
61    }
62}
63
64impl PyList {
65    pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
66        PyRef::new_ref(Self::from(elements), ctx.types.list_type.to_owned(), None)
67    }
68
69    pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> {
70        PyRwLockReadGuard::map(self.elements.read(), |v| &**v)
71    }
72
73    pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
74        self.elements.write()
75    }
76
77    fn repeat(&self, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
78        let elements = &*self.borrow_vec();
79        let v = elements.mul(vm, n)?;
80        Ok(Self::new_ref(v, &vm.ctx))
81    }
82
83    fn irepeat(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
84        zelf.borrow_vec_mut().imul(vm, n)?;
85        Ok(zelf)
86    }
87}
88
89#[derive(FromArgs, Default, Traverse)]
90pub(crate) struct SortOptions {
91    #[pyarg(named, default)]
92    key: Option<PyObjectRef>,
93    #[pytraverse(skip)]
94    #[pyarg(named, default = "false")]
95    reverse: bool,
96}
97
98pub type PyListRef = PyRef<PyList>;
99
100#[pyclass(
101    with(
102        Constructor,
103        Initializer,
104        AsMapping,
105        Iterable,
106        Comparable,
107        AsSequence,
108        Representable
109    ),
110    flags(BASETYPE)
111)]
112impl PyList {
113    #[pymethod]
114    pub(crate) fn append(&self, x: PyObjectRef) {
115        self.borrow_vec_mut().push(x);
116    }
117
118    #[pymethod]
119    pub(crate) fn extend(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
120        let mut new_elements = x.try_to_value(vm)?;
121        self.borrow_vec_mut().append(&mut new_elements);
122        Ok(())
123    }
124
125    #[pymethod]
126    pub(crate) fn insert(&self, position: isize, element: PyObjectRef) {
127        let mut elements = self.borrow_vec_mut();
128        let position = elements.saturate_index(position);
129        elements.insert(position, element);
130    }
131
132    fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
133        let other = other.payload_if_subclass::<PyList>(vm).ok_or_else(|| {
134            vm.new_type_error(format!(
135                "Cannot add {} and {}",
136                Self::class(&vm.ctx).name(),
137                other.class().name()
138            ))
139        })?;
140        let mut elements = self.borrow_vec().to_vec();
141        elements.extend(other.borrow_vec().iter().cloned());
142        Ok(Self::new_ref(elements, &vm.ctx))
143    }
144
145    #[pymethod(magic)]
146    fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
147        self.concat(&other, vm)
148    }
149
150    fn inplace_concat(
151        zelf: &Py<Self>,
152        other: &PyObject,
153        vm: &VirtualMachine,
154    ) -> PyResult<PyObjectRef> {
155        let mut seq = extract_cloned(other, Ok, vm)?;
156        zelf.borrow_vec_mut().append(&mut seq);
157        Ok(zelf.to_owned().into())
158    }
159
160    #[pymethod(magic)]
161    fn iadd(zelf: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
162        let mut seq = extract_cloned(&other, Ok, vm)?;
163        zelf.borrow_vec_mut().append(&mut seq);
164        Ok(zelf)
165    }
166
167    #[pymethod]
168    fn clear(&self) {
169        let _removed = std::mem::take(self.borrow_vec_mut().deref_mut());
170    }
171
172    #[pymethod]
173    fn copy(&self, vm: &VirtualMachine) -> PyRef<Self> {
174        Self::new_ref(self.borrow_vec().to_vec(), &vm.ctx)
175    }
176
177    #[allow(clippy::len_without_is_empty)]
178    #[pymethod(magic)]
179    pub fn len(&self) -> usize {
180        self.borrow_vec().len()
181    }
182
183    #[pymethod(magic)]
184    fn sizeof(&self) -> usize {
185        std::mem::size_of::<Self>()
186            + self.elements.read().capacity() * std::mem::size_of::<PyObjectRef>()
187    }
188
189    #[pymethod]
190    fn reverse(&self) {
191        self.borrow_vec_mut().reverse();
192    }
193
194    #[pymethod(magic)]
195    fn reversed(zelf: PyRef<Self>) -> PyListReverseIterator {
196        let position = zelf.len().saturating_sub(1);
197        PyListReverseIterator {
198            internal: PyMutex::new(PositionIterInternal::new(zelf, position)),
199        }
200    }
201
202    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
203        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
204            SequenceIndex::Int(i) => self.borrow_vec().getitem_by_index(vm, i),
205            SequenceIndex::Slice(slice) => self
206                .borrow_vec()
207                .getitem_by_slice(vm, slice)
208                .map(|x| vm.ctx.new_list(x).into()),
209        }
210    }
211
212    #[pymethod(magic)]
213    fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
214        self._getitem(&needle, vm)
215    }
216
217    fn _setitem(&self, needle: &PyObject, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
218        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
219            SequenceIndex::Int(index) => self.borrow_vec_mut().setitem_by_index(vm, index, value),
220            SequenceIndex::Slice(slice) => {
221                let sec = extract_cloned(&value, Ok, vm)?;
222                self.borrow_vec_mut().setitem_by_slice(vm, slice, &sec)
223            }
224        }
225    }
226
227    #[pymethod(magic)]
228    fn setitem(
229        &self,
230        needle: PyObjectRef,
231        value: PyObjectRef,
232        vm: &VirtualMachine,
233    ) -> PyResult<()> {
234        self._setitem(&needle, value, vm)
235    }
236
237    #[pymethod(magic)]
238    #[pymethod(name = "__rmul__")]
239    fn mul(&self, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
240        self.repeat(n.into(), vm)
241    }
242
243    #[pymethod(magic)]
244    fn imul(zelf: PyRef<Self>, n: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
245        Self::irepeat(zelf, n.into(), vm)
246    }
247
248    #[pymethod]
249    fn count(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
250        self.mut_count(vm, &needle)
251    }
252
253    #[pymethod(magic)]
254    pub(crate) fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
255        self.mut_contains(vm, &needle)
256    }
257
258    #[pymethod]
259    fn index(
260        &self,
261        needle: PyObjectRef,
262        range: OptionalRangeArgs,
263        vm: &VirtualMachine,
264    ) -> PyResult<usize> {
265        let (start, stop) = range.saturate(self.len(), vm)?;
266        let index = self.mut_index_range(vm, &needle, start..stop)?;
267        if let Some(index) = index.into() {
268            Ok(index)
269        } else {
270            Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
271        }
272    }
273
274    #[pymethod]
275    fn pop(&self, i: OptionalArg<isize>, vm: &VirtualMachine) -> PyResult {
276        let mut i = i.into_option().unwrap_or(-1);
277        let mut elements = self.borrow_vec_mut();
278        if i < 0 {
279            i += elements.len() as isize;
280        }
281        if elements.is_empty() {
282            Err(vm.new_index_error("pop from empty list".to_owned()))
283        } else if i < 0 || i as usize >= elements.len() {
284            Err(vm.new_index_error("pop index out of range".to_owned()))
285        } else {
286            Ok(elements.remove(i as usize))
287        }
288    }
289
290    #[pymethod]
291    fn remove(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
292        let index = self.mut_index(vm, &needle)?;
293
294        if let Some(index) = index.into() {
295            // defer delete out of borrow
296            let is_inside_range = index < self.borrow_vec().len();
297            Ok(is_inside_range.then(|| self.borrow_vec_mut().remove(index)))
298        } else {
299            Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
300        }
301        .map(drop)
302    }
303
304    fn _delitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
305        match SequenceIndex::try_from_borrowed_object(vm, needle, "list")? {
306            SequenceIndex::Int(i) => self.borrow_vec_mut().delitem_by_index(vm, i),
307            SequenceIndex::Slice(slice) => self.borrow_vec_mut().delitem_by_slice(vm, slice),
308        }
309    }
310
311    #[pymethod(magic)]
312    fn delitem(&self, subscript: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
313        self._delitem(&subscript, vm)
314    }
315
316    #[pymethod]
317    pub(crate) fn sort(&self, options: SortOptions, vm: &VirtualMachine) -> PyResult<()> {
318        // replace list contents with [] for duration of sort.
319        // this prevents keyfunc from messing with the list and makes it easy to
320        // check if it tries to append elements to it.
321        let mut elements = std::mem::take(self.borrow_vec_mut().deref_mut());
322        let res = do_sort(vm, &mut elements, options.key, options.reverse);
323        std::mem::swap(self.borrow_vec_mut().deref_mut(), &mut elements);
324        res?;
325
326        if !elements.is_empty() {
327            return Err(vm.new_value_error("list modified during sort".to_owned()));
328        }
329
330        Ok(())
331    }
332
333    #[pyclassmethod(magic)]
334    fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
335        PyGenericAlias::new(cls, args, vm)
336    }
337}
338
339fn extract_cloned<F, R>(obj: &PyObject, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>>
340where
341    F: FnMut(PyObjectRef) -> PyResult<R>,
342{
343    use crate::builtins::PyTuple;
344    if let Some(tuple) = obj.payload_if_exact::<PyTuple>(vm) {
345        tuple.iter().map(|x| f(x.clone())).collect()
346    } else if let Some(list) = obj.payload_if_exact::<PyList>(vm) {
347        list.borrow_vec().iter().map(|x| f(x.clone())).collect()
348    } else {
349        let iter = obj.to_owned().get_iter(vm)?;
350        let iter = iter.iter::<PyObjectRef>(vm)?;
351        let len = obj.to_sequence().length_opt(vm).transpose()?.unwrap_or(0);
352        let mut v = Vec::with_capacity(len);
353        for x in iter {
354            v.push(f(x?)?);
355        }
356        v.shrink_to_fit();
357        Ok(v)
358    }
359}
360
361impl MutObjectSequenceOp for PyList {
362    type Inner = [PyObjectRef];
363
364    fn do_get(index: usize, inner: &[PyObjectRef]) -> Option<&PyObjectRef> {
365        inner.get(index)
366    }
367
368    fn do_lock(&self) -> impl std::ops::Deref<Target = [PyObjectRef]> {
369        self.borrow_vec()
370    }
371}
372
373impl Constructor for PyList {
374    type Args = FuncArgs;
375
376    fn py_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
377        PyList::default()
378            .into_ref_with_type(vm, cls)
379            .map(Into::into)
380    }
381}
382
383impl Initializer for PyList {
384    type Args = OptionalArg<PyObjectRef>;
385
386    fn init(zelf: PyRef<Self>, iterable: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
387        let mut elements = if let OptionalArg::Present(iterable) = iterable {
388            iterable.try_to_value(vm)?
389        } else {
390            vec![]
391        };
392        std::mem::swap(zelf.borrow_vec_mut().deref_mut(), &mut elements);
393        Ok(())
394    }
395}
396
397impl AsMapping for PyList {
398    fn as_mapping() -> &'static PyMappingMethods {
399        static AS_MAPPING: PyMappingMethods = PyMappingMethods {
400            length: atomic_func!(|mapping, _vm| Ok(PyList::mapping_downcast(mapping).len())),
401            subscript: atomic_func!(
402                |mapping, needle, vm| PyList::mapping_downcast(mapping)._getitem(needle, vm)
403            ),
404            ass_subscript: atomic_func!(|mapping, needle, value, vm| {
405                let zelf = PyList::mapping_downcast(mapping);
406                if let Some(value) = value {
407                    zelf._setitem(needle, value, vm)
408                } else {
409                    zelf._delitem(needle, vm)
410                }
411            }),
412        };
413        &AS_MAPPING
414    }
415}
416
417impl AsSequence for PyList {
418    fn as_sequence() -> &'static PySequenceMethods {
419        static AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
420            length: atomic_func!(|seq, _vm| Ok(PyList::sequence_downcast(seq).len())),
421            concat: atomic_func!(|seq, other, vm| {
422                PyList::sequence_downcast(seq)
423                    .concat(other, vm)
424                    .map(|x| x.into())
425            }),
426            repeat: atomic_func!(|seq, n, vm| {
427                PyList::sequence_downcast(seq)
428                    .repeat(n, vm)
429                    .map(|x| x.into())
430            }),
431            item: atomic_func!(|seq, i, vm| {
432                PyList::sequence_downcast(seq)
433                    .borrow_vec()
434                    .getitem_by_index(vm, i)
435            }),
436            ass_item: atomic_func!(|seq, i, value, vm| {
437                let zelf = PyList::sequence_downcast(seq);
438                if let Some(value) = value {
439                    zelf.borrow_vec_mut().setitem_by_index(vm, i, value)
440                } else {
441                    zelf.borrow_vec_mut().delitem_by_index(vm, i)
442                }
443            }),
444            contains: atomic_func!(|seq, target, vm| {
445                let zelf = PyList::sequence_downcast(seq);
446                zelf.mut_contains(vm, target)
447            }),
448            inplace_concat: atomic_func!(|seq, other, vm| {
449                let zelf = PyList::sequence_downcast(seq);
450                PyList::inplace_concat(zelf, other, vm)
451            }),
452            inplace_repeat: atomic_func!(|seq, n, vm| {
453                let zelf = PyList::sequence_downcast(seq);
454                Ok(PyList::irepeat(zelf.to_owned(), n, vm)?.into())
455            }),
456        };
457        &AS_SEQUENCE
458    }
459}
460
461impl Iterable for PyList {
462    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
463        Ok(PyListIterator {
464            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
465        }
466        .into_pyobject(vm))
467    }
468}
469
470impl Comparable for PyList {
471    fn cmp(
472        zelf: &Py<Self>,
473        other: &PyObject,
474        op: PyComparisonOp,
475        vm: &VirtualMachine,
476    ) -> PyResult<PyComparisonValue> {
477        if let Some(res) = op.identical_optimization(zelf, other) {
478            return Ok(res.into());
479        }
480        let other = class_or_notimplemented!(Self, other);
481        let a = &*zelf.borrow_vec();
482        let b = &*other.borrow_vec();
483        a.iter()
484            .richcompare(b.iter(), op, vm)
485            .map(PyComparisonValue::Implemented)
486    }
487}
488
489impl Representable for PyList {
490    #[inline]
491    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
492        let s = if zelf.len() == 0 {
493            "[]".to_owned()
494        } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
495            collection_repr(None, "[", "]", zelf.borrow_vec().iter(), vm)?
496        } else {
497            "[...]".to_owned()
498        };
499        Ok(s)
500    }
501}
502
503fn do_sort(
504    vm: &VirtualMachine,
505    values: &mut Vec<PyObjectRef>,
506    key_func: Option<PyObjectRef>,
507    reverse: bool,
508) -> PyResult<()> {
509    let op = if reverse {
510        PyComparisonOp::Lt
511    } else {
512        PyComparisonOp::Gt
513    };
514    let cmp = |a: &PyObjectRef, b: &PyObjectRef| a.rich_compare_bool(b, op, vm);
515
516    if let Some(ref key_func) = key_func {
517        let mut items = values
518            .iter()
519            .map(|x| Ok((x.clone(), key_func.call((x.clone(),), vm)?)))
520            .collect::<Result<Vec<_>, _>>()?;
521        timsort::try_sort_by_gt(&mut items, |a, b| cmp(&a.1, &b.1))?;
522        *values = items.into_iter().map(|(val, _)| val).collect();
523    } else {
524        timsort::try_sort_by_gt(values, cmp)?;
525    }
526
527    Ok(())
528}
529
530#[pyclass(module = false, name = "list_iterator", traverse)]
531#[derive(Debug)]
532pub struct PyListIterator {
533    internal: PyMutex<PositionIterInternal<PyListRef>>,
534}
535
536impl PyPayload for PyListIterator {
537    fn class(ctx: &Context) -> &'static Py<PyType> {
538        ctx.types.list_iterator_type
539    }
540}
541
542#[pyclass(with(Unconstructible, IterNext, Iterable))]
543impl PyListIterator {
544    #[pymethod(magic)]
545    fn length_hint(&self) -> usize {
546        self.internal.lock().length_hint(|obj| obj.len())
547    }
548
549    #[pymethod(magic)]
550    fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
551        self.internal
552            .lock()
553            .set_state(state, |obj, pos| pos.min(obj.len()), vm)
554    }
555
556    #[pymethod(magic)]
557    fn reduce(&self, vm: &VirtualMachine) -> PyTupleRef {
558        self.internal
559            .lock()
560            .builtins_iter_reduce(|x| x.clone().into(), vm)
561    }
562}
563impl Unconstructible for PyListIterator {}
564
565impl SelfIter for PyListIterator {}
566impl IterNext for PyListIterator {
567    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
568        zelf.internal.lock().next(|list, pos| {
569            let vec = list.borrow_vec();
570            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
571        })
572    }
573}
574
575#[pyclass(module = false, name = "list_reverseiterator", traverse)]
576#[derive(Debug)]
577pub struct PyListReverseIterator {
578    internal: PyMutex<PositionIterInternal<PyListRef>>,
579}
580
581impl PyPayload for PyListReverseIterator {
582    fn class(ctx: &Context) -> &'static Py<PyType> {
583        ctx.types.list_reverseiterator_type
584    }
585}
586
587#[pyclass(with(Unconstructible, IterNext, Iterable))]
588impl PyListReverseIterator {
589    #[pymethod(magic)]
590    fn length_hint(&self) -> usize {
591        self.internal.lock().rev_length_hint(|obj| obj.len())
592    }
593
594    #[pymethod(magic)]
595    fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
596        self.internal
597            .lock()
598            .set_state(state, |obj, pos| pos.min(obj.len()), vm)
599    }
600
601    #[pymethod(magic)]
602    fn reduce(&self, vm: &VirtualMachine) -> PyTupleRef {
603        self.internal
604            .lock()
605            .builtins_reversed_reduce(|x| x.clone().into(), vm)
606    }
607}
608impl Unconstructible for PyListReverseIterator {}
609
610impl SelfIter for PyListReverseIterator {}
611impl IterNext for PyListReverseIterator {
612    fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
613        zelf.internal.lock().rev_next(|list, pos| {
614            let vec = list.borrow_vec();
615            Ok(PyIterReturn::from_result(vec.get(pos).cloned().ok_or(None)))
616        })
617    }
618}
619
620pub fn init(context: &Context) {
621    let list_type = &context.types.list_type;
622    PyList::extend_class(context, list_type);
623
624    PyListIterator::extend_class(context, context.types.list_iterator_type);
625    PyListReverseIterator::extend_class(context, context.types.list_reverseiterator_type);
626}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.