diff --git a/Cargo.lock b/Cargo.lock index eb986773c8..f376072567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1518,7 +1518,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "syn-ext", + "syn-ext 0.3.1", ] [[package]] @@ -1645,7 +1645,7 @@ dependencies = [ "rustpython-compiler", "rustpython-doc", "syn", - "syn-ext", + "syn-ext 0.4.0", "textwrap 0.15.0", ] @@ -2120,6 +2120,15 @@ dependencies = [ "syn", ] +[[package]] +name = "syn-ext" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6" +dependencies = [ + "syn", +] + [[package]] name = "system-configuration" version = "0.5.0" diff --git a/benches/benchmarks/fannkuch.py b/benches/benchmarks/fannkuch.py new file mode 100644 index 0000000000..6b4a5dc113 --- /dev/null +++ b/benches/benchmarks/fannkuch.py @@ -0,0 +1,55 @@ +""" +The Computer Language Benchmarks Game +http://benchmarksgame.alioth.debian.org/ + +Contributed by Sokolov Yura, modified by Tupteq. +""" + +# import pyperf + + +DEFAULT_ARG = 9 + + +def fannkuch(n): + count = list(range(1, n + 1)) + max_flips = 0 + m = n - 1 + r = n + perm1 = list(range(n)) + perm = list(range(n)) + perm1_ins = perm1.insert + perm1_pop = perm1.pop + + while 1: + while r != 1: + count[r - 1] = r + r -= 1 + + if perm1[0] != 0 and perm1[m] != m: + perm = perm1[:] + flips_count = 0 + k = perm[0] + while k: + perm[:k + 1] = perm[k::-1] + flips_count += 1 + k = perm[0] + + if flips_count > max_flips: + max_flips = flips_count + + while r != n: + perm1_ins(r, perm1_pop(0)) + count[r] -= 1 + if count[r] > 0: + break + r += 1 + else: + return max_flips + + +if __name__ == "__main__": + #runner = pyperf.Runner() + arg = DEFAULT_ARG + #runner.bench_func('fannkuch', fannkuch, arg) + fannkuch(arg) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 576f221e4f..afe1de788e 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -12,7 +12,7 @@ proc-macro = true [dependencies] syn = { version = "1.0.91", features = ["full", "extra-traits"] } -syn-ext = { version = "0.3.1", features = ["full"] } +syn-ext = { version = "0.4.0", features = ["full"] } quote = "1.0.18" proc-macro2 = "1.0.37" rustpython-compiler = { path = "../compiler/porcelain", version = "0.1.1" } diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index c394556609..d940f7611f 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -548,25 +548,28 @@ where Item: ItemLike + ToTokens + GetIdent, { fn gen_impl_item(&self, args: ImplItemArgs<'_, Item>) -> Result<()> { - let func = args - .item - .function_or_method() - .map_err(|_| self.new_syn_error(args.item.span(), "can only be on a method"))?; - let ident = &func.sig().ident; + let (ident, span) = if let Ok(c) = args.item.constant() { + (c.ident(), c.span()) + } else if let Ok(f) = args.item.function_or_method() { + (&f.sig().ident, f.span()) + } else { + return Err(self.new_syn_error(args.item.span(), "can only be on a method")); + }; let item_attr = args.attrs.remove(self.index()); let item_meta = SlotItemMeta::from_attr(ident.clone(), &item_attr)?; let slot_ident = item_meta.slot_name()?; + let slot_ident = Ident::new(&slot_ident.to_string().to_lowercase(), slot_ident.span()); let slot_name = slot_ident.to_string(); let tokens = { const NON_ATOMIC_SLOTS: &[&str] = &["as_buffer"]; if NON_ATOMIC_SLOTS.contains(&slot_name.as_str()) { - quote_spanned! { func.span() => + quote_spanned! { span => slots.#slot_ident = Some(Self::#ident as _); } } else { - quote_spanned! { func.span() => + quote_spanned! { span => slots.#slot_ident.store(Some(Self::#ident as _)); } } @@ -599,11 +602,11 @@ where Ok(py_name) }; let (ident, py_name, tokens) = - if args.item.function_or_method().is_ok() || args.item.is_const() { + if args.item.function_or_method().is_ok() || args.item.constant().is_ok() { let ident = args.item.get_ident().unwrap(); let py_name = get_py_name(&attr, ident)?; - let value = if args.item.is_const() { + let value = if args.item.constant().is_ok() { // TODO: ctx.new_value quote_spanned!(ident.span() => ctx.new_int(Self::#ident).into()) } else { diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 3b6fe0a3a7..ee9bef2a82 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -1238,8 +1238,8 @@ mod array { }, }; - impl PyArray { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + impl AsMapping for PyArray { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping)._getitem(needle, vm) @@ -1255,12 +1255,6 @@ mod array { }; } - impl AsMapping for PyArray { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } - } - impl Iterable for PyArray { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { Ok(PyArrayIter { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 04762df510..94fcdb5401 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -36,7 +36,7 @@ use crate::{ TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; -use std::{borrow::Cow, mem::size_of}; +use std::mem::size_of; #[pyclass(module = false, name = "bytearray")] #[derive(Debug, Default)] @@ -690,23 +690,6 @@ impl PyByteArray { } } -impl PyByteArray { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| { - Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) - }), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm) - } else { - zelf.delitem(needle.to_owned(), vm) - } - }), - }; -} - impl Constructor for PyByteArray { type Args = FuncArgs; @@ -784,19 +767,24 @@ impl<'a> BufferResizeGuard<'a> for PyByteArray { } impl AsMapping for PyByteArray { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) + }), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm) + } else { + zelf.delitem(needle.to_owned(), vm) + } + }), + }; } impl AsSequence for PyByteArray { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyByteArray { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index ccfdb88f80..faa8dc5ac0 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -25,7 +25,7 @@ use crate::{ TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; -use std::{borrow::Cow, mem::size_of, ops::Deref}; +use std::{mem::size_of, ops::Deref}; #[pyclass(module = false, name = "bytes")] #[derive(Clone, Debug)] @@ -550,14 +550,6 @@ impl PyBytes { } } -impl PyBytes { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), - ass_subscript: None, - }; -} - static BUFFER_METHODS: BufferMethods = BufferMethods { obj_bytes: |buffer| buffer.obj_as::().as_bytes().into(), obj_bytes_mut: |_| panic!(), @@ -577,19 +569,15 @@ impl AsBuffer for PyBytes { } impl AsMapping for PyBytes { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), + ass_subscript: None, + }; } impl AsSequence for PyBytes { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyBytes { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) @@ -614,7 +602,7 @@ impl PyBytes { let other = >::try_from_object(vm, other.to_owned())?; Self::sequence_downcast(seq).contains(other, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index cd5066057b..8be379ee1e 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -25,7 +25,7 @@ use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; use rustpython_common::lock::PyMutex; -use std::{borrow::Cow, fmt}; +use std::fmt; pub type DictContentType = dictdatatype::Dict; @@ -212,21 +212,6 @@ impl PyDict { pub fn size(&self) -> dictdatatype::DictSize { self.entries.size() } - - pub(crate) const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| { - Self::mapping_downcast(mapping).inner_getitem(needle, vm) - }), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - zelf.inner_setitem(needle, value, vm) - } else { - zelf.inner_delitem(needle, vm) - } - }), - }; } // Python dict methods: @@ -476,21 +461,26 @@ impl Initializer for PyDict { } impl AsMapping for PyDict { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping).inner_getitem(needle, vm) + }), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + zelf.inner_setitem(needle, value, vm) + } else { + zelf.inner_delitem(needle, vm) + } + }), + }; } impl AsSequence for PyDict { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyDict { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq).entries.contains(vm, target)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1058,12 +1048,7 @@ impl Comparable for PyDictKeys { } impl AsSequence for PyDictKeys { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictKeys { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, target, vm| { Self::sequence_downcast(seq) @@ -1071,7 +1056,7 @@ impl PyDictKeys { .entries .contains(vm, target) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1112,12 +1097,7 @@ impl Comparable for PyDictItems { } impl AsSequence for PyDictItems { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictItems { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, target, vm| { Self::sequence_downcast(seq) @@ -1125,7 +1105,7 @@ impl PyDictItems { .entries .contains(vm, target) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1134,14 +1114,9 @@ impl PyDictValues {} impl Unconstructible for PyDictValues {} impl AsSequence for PyDictValues { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictValues { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/genericalias.rs b/vm/src/builtins/genericalias.rs index 9c585124c9..4f1d6fedde 100644 --- a/vm/src/builtins/genericalias.rs +++ b/vm/src/builtins/genericalias.rs @@ -306,8 +306,8 @@ pub fn subs_parameters PyResult>( Ok(PyTuple::new_ref(new_args, &vm.ctx)) } -impl PyGenericAlias { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyGenericAlias { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -316,12 +316,6 @@ impl PyGenericAlias { }; } -impl AsMapping for PyGenericAlias { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl Callable for PyGenericAlias { type Args = FuncArgs; fn call(zelf: &crate::Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs index e1d588dfb3..98bdb4a414 100644 --- a/vm/src/builtins/iter.rs +++ b/vm/src/builtins/iter.rs @@ -14,7 +14,6 @@ use rustpython_common::{ lock::{PyMutex, PyRwLock, PyRwLockUpgradableReadGuard}, static_cell, }; -use std::borrow::Cow; /// Marks status of iterator. #[derive(Debug, Clone)] @@ -163,7 +162,7 @@ pub fn builtins_reversed(vm: &VirtualMachine) -> &PyObject { #[derive(Debug)] pub struct PySequenceIterator { // cached sequence methods - seq_methods: Cow<'static, PySequenceMethods>, + seq_methods: &'static PySequenceMethods, internal: PyMutex>, } @@ -177,9 +176,8 @@ impl PyPayload for PySequenceIterator { impl PySequenceIterator { pub fn new(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let seq = PySequence::try_protocol(obj.as_ref(), vm)?; - let seq_methods = seq.methods_cow(vm).clone(); Ok(Self { - seq_methods, + seq_methods: seq.methods, internal: PyMutex::new(PositionIterInternal::new(obj, 0)), }) } @@ -188,7 +186,7 @@ impl PySequenceIterator { fn length_hint(&self, vm: &VirtualMachine) -> PyObjectRef { let internal = self.internal.lock(); if let IterStatus::Active(obj) = &internal.status { - let seq = PySequence::with_methods(obj, self.seq_methods.clone()); + let seq = PySequence::with_methods(obj, self.seq_methods); seq.length(vm) .map(|x| PyInt::from(x).into_pyobject(vm)) .unwrap_or_else(|_| vm.ctx.not_implemented()) @@ -212,7 +210,7 @@ impl IterNextIterable for PySequenceIterator {} impl IterNext for PySequenceIterator { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|obj, pos| { - let seq = PySequence::with_methods(obj, zelf.seq_methods.clone()); + let seq = PySequence::with_methods(obj, zelf.seq_methods); PyIterReturn::from_getitem_result(seq.get_item(pos as isize, vm), vm) }) } diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index f6187ad9e0..27a22ea284 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -19,7 +19,7 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, }; -use std::{borrow::Cow, fmt, ops::DerefMut}; +use std::{fmt, ops::DerefMut}; /// Built-in mutable sequence. /// @@ -139,7 +139,7 @@ impl PyList { } fn inplace_concat(zelf: &Py, other: &PyObject, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(mut seq) = PySequence::from(other).extract_cloned(Ok, vm) { + if let Ok(mut seq) = extract_cloned(other, Ok, vm) { zelf.borrow_vec_mut().append(&mut seq); zelf.to_owned().into() } else { @@ -149,7 +149,7 @@ impl PyList { #[pymethod(magic)] fn iadd(zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(mut seq) = PySequence::from(other.as_ref()).extract_cloned(Ok, vm) { + if let Ok(mut seq) = extract_cloned(&*other, Ok, vm) { zelf.borrow_vec_mut().append(&mut seq); zelf.into() } else { @@ -215,7 +215,7 @@ impl PyList { match SequenceIndex::try_from_borrowed_object(vm, needle)? { SequenceIndex::Int(index) => self.borrow_vec_mut().set_item_by_index(vm, index, value), SequenceIndex::Slice(slice) => { - let sec = PySequence::from(value.as_ref()).extract_cloned(Ok, vm)?; + let sec = extract_cloned(&*value, Ok, vm)?; self.borrow_vec_mut().set_item_by_slice(vm, slice, &sec) } } @@ -352,19 +352,29 @@ impl PyList { } } -impl PyList { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - zelf._setitem(needle, value, vm) - } else { - zelf._delitem(needle, vm) - } - }), - }; +fn extract_cloned(obj: &PyObject, mut f: F, vm: &VirtualMachine) -> PyResult> +where + F: FnMut(PyObjectRef) -> PyResult, +{ + use crate::builtins::PyTuple; + if let Some(tuple) = obj.payload_if_exact::(vm) { + tuple.iter().map(|x| f(x.clone())).collect() + } else if let Some(list) = obj.payload_if_exact::(vm) { + list.borrow_vec().iter().map(|x| f(x.clone())).collect() + } else { + let iter = obj.to_owned().get_iter(vm)?; + let iter = iter.iter::(vm)?; + let len = PySequence::new(obj, vm) + .and_then(|seq| seq.length_opt(vm)) + .transpose()? + .unwrap_or(0); + let mut v = Vec::with_capacity(len); + for x in iter { + v.push(f(x?)?); + } + v.shrink_to_fit(); + Ok(v) + } } impl<'a> MutObjectSequenceOp<'a> for PyList { @@ -404,21 +414,22 @@ impl Initializer for PyList { } impl AsMapping for PyList { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + zelf._setitem(needle, value, vm) + } else { + zelf._delitem(needle, vm) + } + }), + }; } impl AsSequence for PyList { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} -impl PyList { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 4ab71a1dff..d603bca740 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -2,12 +2,11 @@ use super::{PyDict, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, convert::ToPyObject, - function::OptionalArg, + function::{ArgMapping, OptionalArg}, protocol::{PyMapping, PyMappingMethods, PySequence, PySequenceMethods}, types::{AsMapping, AsSequence, Constructor, Iterable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; -use std::borrow::Cow; #[pyclass(module = false, name = "mappingproxy")] #[derive(Debug)] @@ -18,7 +17,7 @@ pub struct PyMappingProxy { #[derive(Debug)] enum MappingProxyInner { Class(PyTypeRef), - Dict(PyObjectRef), + Mapping(ArgMapping), } impl PyPayload for PyMappingProxy { @@ -39,21 +38,21 @@ impl Constructor for PyMappingProxy { type Args = PyObjectRef; fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult { - if !PyMapping::from(mapping.as_ref()).check(vm) - || mapping.payload_if_subclass::(vm).is_some() - || mapping.payload_if_subclass::(vm).is_some() - { - Err(vm.new_type_error(format!( - "mappingproxy() argument must be a mapping, not {}", - mapping.class() - ))) - } else { - Self { - mapping: MappingProxyInner::Dict(mapping), + if let Some(methods) = PyMapping::find_methods(&mapping, vm) { + if mapping.payload_if_subclass::(vm).is_none() + && mapping.payload_if_subclass::(vm).is_none() + { + return Self { + mapping: MappingProxyInner::Mapping(ArgMapping::with_methods(mapping, methods)), + } + .into_ref_with_type(vm, cls) + .map(Into::into); } - .into_ref_with_type(vm, cls) - .map(Into::into) } + Err(vm.new_type_error(format!( + "mappingproxy() argument must be a mapping, not {}", + mapping.class() + ))) } } @@ -64,7 +63,7 @@ impl PyMappingProxy { MappingProxyInner::Class(class) => key .as_interned_str(vm) .and_then(|key| class.attributes.read().get(key).cloned()), - MappingProxyInner::Dict(obj) => obj.get_item(&*key, vm).ok(), + MappingProxyInner::Mapping(mapping) => mapping.mapping().subscript(&*key, vm).ok(), }; Ok(opt) } @@ -92,7 +91,7 @@ impl PyMappingProxy { MappingProxyInner::Class(class) => Ok(key .as_interned_str(vm) .map_or(false, |key| class.attributes.read().contains_key(key))), - MappingProxyInner::Dict(obj) => PySequence::from(obj.as_ref()).contains(key, vm), + MappingProxyInner::Mapping(mapping) => PySequence::contains(&*mapping, key, vm), } } @@ -101,40 +100,34 @@ impl PyMappingProxy { self._contains(&key, vm) } - #[pymethod] - pub fn items(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), + fn to_object(&self, vm: &VirtualMachine) -> PyResult { + Ok(match &self.mapping { + MappingProxyInner::Mapping(d) => d.as_ref().to_owned(), MappingProxyInner::Class(c) => { PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) } - }; + }) + } + + #[pymethod] + pub fn items(&self, vm: &VirtualMachine) -> PyResult { + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, items).as_str(), ()) } #[pymethod] pub fn keys(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, keys).as_str(), ()) } #[pymethod] pub fn values(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, values).as_str(), ()) } #[pymethod] pub fn copy(&self, vm: &VirtualMachine) -> PyResult { match &self.mapping { - MappingProxyInner::Dict(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()), + MappingProxyInner::Mapping(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()), MappingProxyInner::Class(c) => { Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)) } @@ -142,12 +135,7 @@ impl PyMappingProxy { } #[pymethod(magic)] fn repr(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; Ok(format!("mappingproxy({})", obj.repr(vm)?)) } @@ -157,8 +145,8 @@ impl PyMappingProxy { } } -impl PyMappingProxy { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyMappingProxy { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -167,37 +155,16 @@ impl PyMappingProxy { }; } -impl AsMapping for PyMappingProxy { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyMappingProxy { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyMappingProxy { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq)._contains(target, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } impl Iterable for PyMappingProxy { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let obj = match &zelf.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - // TODO: something that's much more efficient than this - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = zelf.to_object(vm)?; let iter = obj.get_iter(vm)?; Ok(iter.into()) } diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 14bcbca790..61b28e3bc5 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -24,7 +24,7 @@ use crate::{ }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; -use std::{borrow::Cow, cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range}; +use std::{cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range}; #[derive(FromArgs)] pub struct PyMemoryViewNewArgs { @@ -957,8 +957,8 @@ impl Drop for PyMemoryView { } } -impl PyMemoryView { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyMemoryView { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, vm| Self::mapping_downcast(mapping).len(vm)), subscript: Some(|mapping, needle, vm| { let zelf = Self::mapping_downcast(mapping); @@ -975,20 +975,8 @@ impl PyMemoryView { }; } -impl AsMapping for PyMemoryView { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyMemoryView { - fn as_sequence(_zelf: &Py, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyMemoryView { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, vm| { let zelf = Self::sequence_downcast(seq); zelf.try_not_released(vm)?; @@ -999,7 +987,7 @@ impl PyMemoryView { zelf.try_not_released(vm)?; zelf.getitem_by_idx(i, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 1348d7654a..312a46efe0 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -16,7 +16,7 @@ use crossbeam_utils::atomic::AtomicCell; use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; -use std::{borrow::Cow, cmp::max}; +use std::cmp::max; // Search flag passed to iter_search enum SearchType { @@ -389,16 +389,20 @@ impl PyRange { .try_to_primitive::(vm) .map(|x| x as usize) } +} - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyRange { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, vm| Self::mapping_downcast(mapping).protocol_length(vm)), subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) }), ass_subscript: None, }; +} - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { +impl AsSequence for PyRange { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)), item: Some(|seq, i, vm| { Self::sequence_downcast(seq) @@ -409,25 +413,10 @@ impl PyRange { contains: Some(|seq, needle, vm| { Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm)) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } -impl AsMapping for PyRange { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - -impl AsSequence for PyRange { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} - impl Hashable for PyRange { fn hash(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { let length = zelf.compute_length(); diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index eac707c3ba..a637b3b6ef 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -20,7 +20,6 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; -use std::borrow::Cow; use std::{fmt, ops::Deref}; pub type SetContentType = dictdatatype::Dict<()>; @@ -663,19 +662,10 @@ impl Initializer for PySet { } impl AsSequence for PySet { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PySet { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -904,19 +894,10 @@ impl PyFrozenSet { } impl AsSequence for PyFrozenSet { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyFrozenSet { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 8a58f680d2..1700795d6a 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1301,13 +1301,7 @@ impl Iterable for PyStr { } impl AsMapping for PyStr { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - -impl PyStr { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), ass_subscript: None, @@ -1315,16 +1309,7 @@ impl PyStr { } impl AsSequence for PyStr { - fn as_sequence( - _zelf: &Py, - _vm: &VirtualMachine, - ) -> std::borrow::Cow<'static, PySequenceMethods> { - std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} - -impl PyStr { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { let zelf = Self::sequence_downcast(seq); @@ -1340,7 +1325,7 @@ impl PyStr { .map(|x| zelf.new_substr(x.to_string()).into_ref(vm).into()) }), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq)._contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 84865a0916..59ea9a6447 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -17,7 +17,7 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; -use std::{borrow::Cow, fmt, marker::PhantomData}; +use std::{fmt, marker::PhantomData}; /// tuple() -> empty tuple /// tuple(iterable) -> tuple initialized from iterable's items @@ -345,31 +345,16 @@ impl PyTuple { } } -impl PyTuple { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyTuple { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), ass_subscript: None, }; } -impl AsMapping for PyTuple { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyTuple { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} - -impl PyTuple { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { let zelf = Self::sequence_downcast(seq); @@ -393,7 +378,7 @@ impl PyTuple { let zelf = Self::sequence_downcast(seq); zelf._contains(needle, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index e2072a3425..813974b53c 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -224,8 +224,10 @@ impl PyUnion { Ok(res) } +} - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyUnion { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -234,12 +236,6 @@ impl PyUnion { }; } -impl AsMapping for PyUnion { - fn as_mapping(_zelf: &Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl Comparable for PyUnion { fn cmp( zelf: &crate::Py, diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs index de18517ce3..b6035ff968 100644 --- a/vm/src/function/protocol.rs +++ b/vm/src/function/protocol.rs @@ -4,9 +4,10 @@ use crate::{ convert::ToPyObject, identifier, protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, + types::AsMapping, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; -use std::{borrow::Borrow, marker::PhantomData}; +use std::{borrow::Borrow, marker::PhantomData, ops::Deref}; #[derive(Clone, Debug)] pub struct ArgCallable { @@ -99,24 +100,29 @@ where } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ArgMapping { obj: PyObjectRef, - mapping_methods: PyMappingMethods, + methods: &'static PyMappingMethods, } impl ArgMapping { + #[inline] + pub fn with_methods(obj: PyObjectRef, methods: &'static PyMappingMethods) -> Self { + Self { obj, methods } + } + #[inline(always)] pub fn from_dict_exact(dict: PyDictRef) -> Self { Self { obj: dict.into(), - mapping_methods: PyDict::MAPPING_METHODS, + methods: &PyDict::AS_MAPPING, } } #[inline(always)] pub fn mapping(&self) -> PyMapping { - PyMapping::with_methods(&self.obj, self.mapping_methods) + PyMapping::with_methods(&self.obj, self.methods) } } @@ -134,6 +140,14 @@ impl AsRef for ArgMapping { } } +impl Deref for ArgMapping { + type Target = PyObject; + #[inline(always)] + fn deref(&self) -> &PyObject { + &self.obj + } +} + impl From for PyObjectRef { #[inline(always)] fn from(value: ArgMapping) -> PyObjectRef { @@ -151,11 +165,8 @@ impl ToPyObject for ArgMapping { impl TryFromObject for ArgMapping { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { let mapping = PyMapping::try_protocol(&obj, vm)?; - let mapping_methods = *mapping.methods(vm); - Ok(Self { - obj, - mapping_methods, - }) + let methods = mapping.methods; + Ok(Self { obj, methods }) } } diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index a9389db473..48663e7a8f 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -3,7 +3,6 @@ use crate::{ dict::{PyDictItems, PyDictKeys, PyDictValues}, PyDict, PyStrInterned, }, - common::lock::OnceCell, convert::ToPyResult, AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine, }; @@ -11,7 +10,6 @@ use crate::{ // Mapping protocol // https://docs.python.org/3/c-api/mapping.html #[allow(clippy::type_complexity)] -#[derive(Default, Copy, Clone)] pub struct PyMappingMethods { pub length: Option PyResult>, pub subscript: Option PyResult>, @@ -19,22 +17,24 @@ pub struct PyMappingMethods { Option, &VirtualMachine) -> PyResult<()>>, } -#[derive(Clone)] -pub struct PyMapping<'a> { - pub obj: &'a PyObject, - methods: OnceCell, +impl std::fmt::Debug for PyMappingMethods { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "mapping methods") + } } -impl<'a> From<&'a PyObject> for PyMapping<'a> { - #[inline(always)] - fn from(obj: &'a PyObject) -> Self { - Self { - obj, - methods: OnceCell::new(), - } +impl PyMappingMethods { + fn check(&self) -> bool { + self.subscript.is_some() } } +#[derive(Clone)] +pub struct PyMapping<'a> { + pub obj: &'a PyObject, + pub methods: &'static PyMappingMethods, +} + impl AsRef for PyMapping<'_> { #[inline(always)] fn as_ref(&self) -> &PyObject { @@ -43,47 +43,45 @@ impl AsRef for PyMapping<'_> { } impl<'a> PyMapping<'a> { + #[inline] + pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option { + let methods = Self::find_methods(obj, vm)?; + Some(Self { obj, methods }) + } + #[inline(always)] - pub fn with_methods(obj: &'a PyObject, methods: PyMappingMethods) -> Self { - Self { - obj, - methods: OnceCell::from(methods), - } + pub fn with_methods(obj: &'a PyObject, methods: &'static PyMappingMethods) -> Self { + Self { obj, methods } } pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult { - let zelf = Self::from(obj); - if zelf.check(vm) { - Ok(zelf) - } else { - Err(vm.new_type_error(format!("{} is not a mapping object", zelf.obj.class()))) + if let Some(methods) = Self::find_methods(obj, vm) { + if methods.check() { + return Ok(Self::with_methods(obj, methods)); + } } + + Err(vm.new_type_error(format!("{} is not a mapping object", obj.class()))) } } impl PyMapping<'_> { // PyMapping::Check #[inline] - pub fn check(&self, vm: &VirtualMachine) -> bool { - self.methods(vm).subscript.is_some() - } - - pub fn methods(&self, vm: &VirtualMachine) -> &PyMappingMethods { - self.methods.get_or_init(|| { - if let Some(f) = self - .obj - .class() - .mro_find_map(|cls| cls.slots.as_mapping.load()) - { - f(self.obj, vm) - } else { - PyMappingMethods::default() - } - }) + pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { + Self::find_methods(obj, vm).map_or(false, PyMappingMethods::check) + } + + pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyMappingMethods> { + if let Some(f) = obj.class().mro_find_map(|cls| cls.slots.as_mapping.load()) { + Some(f(obj, vm)) + } else { + None + } } pub fn length_opt(&self, vm: &VirtualMachine) -> Option> { - self.methods(vm).length.map(|f| f(self, vm)) + self.methods.length.map(|f| f(self, vm)) } pub fn length(&self, vm: &VirtualMachine) -> PyResult { @@ -110,7 +108,7 @@ impl PyMapping<'_> { fn _subscript(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { let f = self - .methods(vm) + .methods .subscript .ok_or_else(|| vm.new_type_error(format!("{} is not a mapping", self.obj.class())))?; f(self, needle, vm) @@ -122,7 +120,7 @@ impl PyMapping<'_> { value: Option, vm: &VirtualMachine, ) -> PyResult<()> { - let f = self.methods(vm).ass_subscript.ok_or_else(|| { + let f = self.methods.ass_subscript.ok_or_else(|| { vm.new_type_error(format!( "'{}' object does not support item assignment", self.obj.class() diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index d4ed959eea..b475b78f02 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -522,9 +522,9 @@ impl PyObject { // int PyObject_TypeCheck(PyObject *o, PyTypeObject *type) pub fn length_opt(&self, vm: &VirtualMachine) -> Option> { - PySequence::from(self) - .length_opt(vm) - .or_else(|| PyMapping::from(self).length_opt(vm)) + PySequence::new(self, vm) + .and_then(|seq| seq.length_opt(vm)) + .or_else(|| PyMapping::new(self, vm).and_then(|mapping| mapping.length_opt(vm))) } pub fn length(&self, vm: &VirtualMachine) -> PyResult { @@ -570,21 +570,23 @@ impl PyObject { return dict.set_item(needle, value, vm); } - let mapping = PyMapping::from(self); - let seq = PySequence::from(self); - - if let Some(f) = mapping.methods(vm).ass_subscript { - let needle = needle.to_pyobject(vm); - f(&mapping, &needle, Some(value), vm) - } else if let Some(f) = seq.methods(vm).ass_item { - let i = needle.key_as_isize(vm)?; - f(&seq, i, Some(value), vm) - } else { - Err(vm.new_type_error(format!( - "'{}' does not support item assignment", - self.class() - ))) + if let Some(mapping) = PyMapping::new(self, vm) { + if let Some(f) = mapping.methods.ass_subscript { + let needle = needle.to_pyobject(vm); + return f(&mapping, &needle, Some(value), vm); + } + } + if let Some(seq) = PySequence::new(self, vm) { + if let Some(f) = seq.methods.ass_item { + let i = needle.key_as_isize(vm)?; + return f(&seq, i, Some(value), vm); + } } + + Err(vm.new_type_error(format!( + "'{}' does not support item assignment", + self.class() + ))) } pub fn del_item(&self, needle: &K, vm: &VirtualMachine) -> PyResult<()> { @@ -592,17 +594,19 @@ impl PyObject { return dict.del_item(needle, vm); } - let mapping = PyMapping::from(self); - let seq = PySequence::from(self); - - if let Some(f) = mapping.methods(vm).ass_subscript { - let needle = needle.to_pyobject(vm); - f(&mapping, &needle, None, vm) - } else if let Some(f) = seq.methods(vm).ass_item { - let i = needle.key_as_isize(vm)?; - f(&seq, i, None, vm) - } else { - Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) + if let Some(mapping) = PyMapping::new(self, vm) { + if let Some(f) = mapping.methods.ass_subscript { + let needle = needle.to_pyobject(vm); + return f(&mapping, &needle, None, vm); + } } + if let Some(seq) = PySequence::new(self, vm) { + if let Some(f) = seq.methods.ass_item { + let i = needle.key_as_isize(vm)?; + return f(&seq, i, None, vm); + } + } + + Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) } } diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index ac15be3e06..fd87c8a1ff 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -1,22 +1,17 @@ use crate::{ builtins::{PyList, PyListRef, PySlice, PyTuple, PyTupleRef}, - common::lock::OnceCell, convert::ToPyObject, function::PyArithmeticValue, protocol::PyMapping, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use itertools::Itertools; -use std::{ - borrow::{Borrow, Cow}, - fmt::Debug, -}; +use std::fmt::Debug; // Sequence Protocol // https://docs.python.org/3/c-api/sequence.html #[allow(clippy::type_complexity)] -#[derive(Default, Clone)] pub struct PySequenceMethods { pub length: Option PyResult>, pub concat: Option PyResult>, @@ -30,8 +25,19 @@ pub struct PySequenceMethods { } impl PySequenceMethods { - pub const fn not_implemented() -> &'static Self { - &NOT_IMPLEMENTED + pub const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods { + length: None, + concat: None, + repeat: None, + item: None, + ass_item: None, + contains: None, + inplace_concat: None, + inplace_repeat: None, + }; + + pub fn check(&self) -> bool { + self.item.is_some() } } @@ -52,61 +58,50 @@ impl Debug for PySequenceMethods { pub struct PySequence<'a> { pub obj: &'a PyObject, - // some function don't need it, so lazy initialize - methods: OnceCell>, + pub methods: &'static PySequenceMethods, } -impl<'a> From<&'a PyObject> for PySequence<'a> { - fn from(obj: &'a PyObject) -> Self { - Self { - obj, - methods: OnceCell::new(), - } +impl<'a> PySequence<'a> { + #[inline] + pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option { + let methods = Self::find_methods(obj, vm)?; + Some(Self { obj, methods }) } -} -impl<'a> PySequence<'a> { - pub fn with_methods(obj: &'a PyObject, methods: Cow<'static, PySequenceMethods>) -> Self { - Self { - obj, - methods: OnceCell::from(methods), - } + #[inline] + pub fn with_methods(obj: &'a PyObject, methods: &'static PySequenceMethods) -> Self { + Self { obj, methods } } pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult { - let zelf = Self::from(obj); - if zelf.check(vm) { - Ok(zelf) - } else { - Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class()))) + if let Some(methods) = Self::find_methods(obj, vm) { + if methods.check() { + return Ok(Self::with_methods(obj, methods)); + } } + + Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class()))) } } impl PySequence<'_> { // PySequence_Check - pub fn check(&self, vm: &VirtualMachine) -> bool { - self.methods(vm).item.is_some() - } - - pub fn methods(&self, vm: &VirtualMachine) -> &PySequenceMethods { - self.methods_cow(vm).borrow() + pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { + Self::find_methods(obj, vm).map_or(false, PySequenceMethods::check) } - pub fn methods_cow(&self, vm: &VirtualMachine) -> &Cow<'static, PySequenceMethods> { - self.methods.get_or_init(|| { - let cls = self.obj.class(); - if !cls.is(vm.ctx.types.dict_type) { - if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) { - return f(self.obj, vm); - } + pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PySequenceMethods> { + let cls = obj.class(); + if !cls.is(vm.ctx.types.dict_type) { + if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) { + return Some(f(obj, vm)); } - Cow::Borrowed(PySequenceMethods::not_implemented()) - }) + } + None } pub fn length_opt(&self, vm: &VirtualMachine) -> Option> { - self.methods(vm).length.map(|f| f(self, vm)) + self.methods.length.map(|f| f(self, vm)) } pub fn length(&self, vm: &VirtualMachine) -> PyResult { @@ -119,12 +114,12 @@ impl PySequence<'_> { } pub fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).concat { + if let Some(f) = self.methods.concat { return f(self, other, vm); } // if both arguments apear to be sequences, try fallback to __add__ - if self.check(vm) && PySequence::from(other).check(vm) { + if PySequence::check(other, vm) { let ret = vm._add(self.obj, other)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); @@ -137,30 +132,29 @@ impl PySequence<'_> { } pub fn repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).repeat { + if let Some(f) = self.methods.repeat { return f(self, n, vm); } // try fallback to __mul__ - if self.check(vm) { - let ret = vm._mul(self.obj, &n.to_pyobject(vm))?; - if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { - return Ok(ret); - } + let ret = vm._mul(self.obj, &n.to_pyobject(vm))?; + if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { + return Ok(ret); } + Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class()))) } pub fn inplace_concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).inplace_concat { + if let Some(f) = self.methods.inplace_concat { return f(self, other, vm); } - if let Some(f) = self.methods(vm).concat { + if let Some(f) = self.methods.concat { return f(self, other, vm); } // if both arguments apear to be sequences, try fallback to __iadd__ - if self.check(vm) && PySequence::from(other).check(vm) { + if PySequence::check(other, vm) { let ret = vm._iadd(self.obj, other)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); @@ -173,24 +167,23 @@ impl PySequence<'_> { } pub fn inplace_repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).inplace_repeat { + if let Some(f) = self.methods.inplace_repeat { return f(self, n, vm); } - if let Some(f) = self.methods(vm).repeat { + if let Some(f) = self.methods.repeat { return f(self, n, vm); } - if self.check(vm) { - let ret = vm._imul(self.obj, &n.to_pyobject(vm))?; - if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { - return Ok(ret); - } + let ret = vm._imul(self.obj, &n.to_pyobject(vm))?; + if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { + return Ok(ret); } + Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class()))) } pub fn get_item(&self, i: isize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).item { + if let Some(f) = self.methods.item { return f(self, i, vm); } Err(vm.new_type_error(format!( @@ -200,7 +193,7 @@ impl PySequence<'_> { } fn _ass_item(&self, i: isize, value: Option, vm: &VirtualMachine) -> PyResult<()> { - if let Some(f) = self.methods(vm).ass_item { + if let Some(f) = self.methods.ass_item { return f(self, i, value, vm); } Err(vm.new_type_error(format!( @@ -242,8 +235,8 @@ impl PySequence<'_> { value: Option, vm: &VirtualMachine, ) -> PyResult<()> { - let mapping = PyMapping::from(self.obj); - if let Some(f) = mapping.methods(vm).ass_subscript { + let mapping = PyMapping::new(self.obj, vm).unwrap(); + if let Some(f) = mapping.methods.ass_subscript { let slice = PySlice { start: Some(start.to_pyobject(vm)), stop: stop.to_pyobject(vm), @@ -294,23 +287,6 @@ impl PySequence<'_> { Ok(list) } - pub fn contains(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).contains { - return f(self, target, vm); - } - - let iter = self.obj.to_owned().get_iter(vm)?; - let iter = iter.iter::(vm)?; - - for elem in iter { - let elem = elem?; - if vm.bool_eq(&elem, target)? { - return Ok(true); - } - } - Ok(false) - } - pub fn count(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult { let mut n = 0; @@ -372,35 +348,22 @@ impl PySequence<'_> { } } - pub fn extract_cloned(&self, mut f: F, vm: &VirtualMachine) -> PyResult> - where - F: FnMut(PyObjectRef) -> PyResult, - { - if let Some(tuple) = self.obj.payload_if_exact::(vm) { - tuple.iter().map(|x| f(x.clone())).collect() - } else if let Some(list) = self.obj.payload_if_exact::(vm) { - list.borrow_vec().iter().map(|x| f(x.clone())).collect() - } else { - let iter = self.obj.to_owned().get_iter(vm)?; - let iter = iter.iter::(vm)?; - let len = self.length(vm).unwrap_or(0); - let mut v = Vec::with_capacity(len); - for x in iter { - v.push(f(x?)?); + pub fn contains(zelf: &PyObject, target: &PyObject, vm: &VirtualMachine) -> PyResult { + if let Some(seq) = PySequence::new(zelf, vm) { + if let Some(contains) = seq.methods.contains { + return contains(&seq, target, vm); + } + } + + let iter = zelf.to_owned().get_iter(vm)?; + let iter = iter.iter::(vm)?; + + for elem in iter { + let elem = elem?; + if vm.bool_eq(&elem, target)? { + return Ok(true); } - v.shrink_to_fit(); - Ok(v) } + Ok(false) } } - -const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods { - length: None, - concat: None, - repeat: None, - item: None, - ass_item: None, - contains: None, - inplace_concat: None, - inplace_repeat: None, -}; diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 508f1b4119..59efa9a674 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -518,16 +518,7 @@ mod _collections { } impl AsSequence for PyDeque { - fn as_sequence( - _zelf: &crate::Py, - _vm: &VirtualMachine, - ) -> std::borrow::Cow<'static, PySequenceMethods> { - std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } - } - - impl PyDeque { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index 7ffbb35dca..2b6b88d9f2 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -780,8 +780,10 @@ mod _sre { fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::new(cls, args, vm) } + } - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + impl AsMapping for Match { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping) @@ -792,12 +794,6 @@ mod _sre { }; } - impl AsMapping for Match { - fn as_mapping(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } - } - #[pyattr] #[pyclass(name = "SRE_Scanner")] #[derive(Debug, PyPayload)] diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index b626327e91..245e7a81c8 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -14,10 +14,7 @@ use crate::{ }; use crossbeam_utils::atomic::AtomicCell; use num_traits::{Signed, ToPrimitive}; -use std::{ - borrow::{Borrow, Cow}, - cmp::Ordering, -}; +use std::{borrow::Borrow, cmp::Ordering}; // The corresponding field in CPython is `tp_` prefixed. // e.g. name -> tp_name @@ -140,7 +137,7 @@ impl Default for PyTypeFlags { } pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult; -pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> PyMappingMethods; +pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> &'static PyMappingMethods; pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult; // CallFunc = GenericMethod pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult; @@ -162,20 +159,10 @@ pub(crate) type DescrSetFunc = pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>; pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; -pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> Cow<'static, PySequenceMethods>; - -macro_rules! then_some_closure { - ($cond:expr, $closure:expr) => { - if $cond { - Some($closure) - } else { - None - } - }; -} +pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> &'static PySequenceMethods; -fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?; +fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult { + let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?; let len = ret.payload::().ok_or_else(|| { vm.new_type_error(format!( "'{}' object cannot be interpreted as an integer", @@ -192,84 +179,163 @@ fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { Ok(len as usize) } -fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { - PyMappingMethods { - length: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __len__)), - |mapping, vm| { length_wrapper(mapping.obj.to_owned(), vm) } - ), - subscript: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __getitem__)), - |mapping, needle, vm| { - vm.call_special_method( +const fn bool_int(v: bool) -> usize { + if v { + 1 + } else { + 0 + } +} + +pub(crate) fn static_as_mapping_generic( + has_length: bool, + has_subscript: bool, + has_ass_subscript: bool, +) -> &'static PyMappingMethods { + static METHODS: &[PyMappingMethods] = &[ + new_generic(false, false, false), + new_generic(true, false, false), + new_generic(false, true, false), + new_generic(true, true, false), + new_generic(false, false, true), + new_generic(true, false, true), + new_generic(false, true, true), + new_generic(true, true, true), + ]; + + fn length(mapping: &PyMapping, vm: &VirtualMachine) -> PyResult { + length_wrapper(mapping.obj, vm) + } + fn subscript(mapping: &PyMapping, needle: &PyObject, vm: &VirtualMachine) -> PyResult { + vm.call_special_method( + mapping.obj.to_owned(), + identifier!(vm, __getitem__), + (needle.to_owned(),), + ) + } + fn ass_subscript( + mapping: &PyMapping, + needle: &PyObject, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + match value { + Some(value) => vm + .call_special_method( + mapping.obj.to_owned(), + identifier!(vm, __setitem__), + (needle.to_owned(), value), + ) + .map(|_| Ok(()))?, + None => vm + .call_special_method( mapping.obj.to_owned(), - identifier!(vm, __getitem__), + identifier!(vm, __delitem__), (needle.to_owned(),), ) - } - ), - ass_subscript: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __setitem__)) - | zelf.class().has_attr(identifier!(vm, __delitem__)), - |mapping, needle, value, vm| match value { - Some(value) => vm - .call_special_method( - mapping.obj.to_owned(), - identifier!(vm, __setitem__), - (needle.to_owned(), value), - ) - .map(|_| Ok(()))?, - None => vm - .call_special_method( - mapping.obj.to_owned(), - identifier!(vm, __delitem__), - (needle.to_owned(),) - ) - .map(|_| Ok(()))?, - } - ), + .map(|_| Ok(()))?, + } } + + const fn new_generic( + has_length: bool, + has_subscript: bool, + has_ass_subscript: bool, + ) -> PyMappingMethods { + PyMappingMethods { + length: if has_length { Some(length) } else { None }, + subscript: if has_subscript { Some(subscript) } else { None }, + ass_subscript: if has_ass_subscript { + Some(ass_subscript) + } else { + None + }, + } + } + + let key = + bool_int(has_length) | (bool_int(has_subscript) << 1) | (bool_int(has_ass_subscript) << 2); + + &METHODS[key] +} + +fn as_mapping_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyMappingMethods { + let (has_length, has_subscript, has_ass_subscript) = ( + zelf.class().has_attr(identifier!(vm, __len__)), + zelf.class().has_attr(identifier!(vm, __getitem__)), + zelf.class().has_attr(identifier!(vm, __setitem__)) + | zelf.class().has_attr(identifier!(vm, __delitem__)), + ); + static_as_mapping_generic(has_length, has_subscript, has_ass_subscript) } -fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { +pub(crate) fn static_as_sequence_generic( + has_length: bool, + has_ass_item: bool, +) -> &'static PySequenceMethods { + static METHODS: &[PySequenceMethods] = &[ + new_generic(false, false), + new_generic(true, false), + new_generic(false, true), + new_generic(true, true), + ]; + + fn length(seq: &PySequence, vm: &VirtualMachine) -> PyResult { + length_wrapper(seq.obj, vm) + } + fn item(seq: &PySequence, i: isize, vm: &VirtualMachine) -> PyResult { + vm.call_special_method(seq.obj.to_owned(), identifier!(vm, __getitem__), (i,)) + } + fn ass_item( + seq: &PySequence, + i: isize, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + match value { + Some(value) => vm + .call_special_method( + seq.obj.to_owned(), + identifier!(vm, __setitem__), + (i.to_pyobject(vm), value), + ) + .map(|_| Ok(()))?, + None => vm + .call_special_method( + seq.obj.to_owned(), + identifier!(vm, __delitem__), + (i.to_pyobject(vm),), + ) + .map(|_| Ok(()))?, + } + } + + const fn new_generic(has_length: bool, has_ass_item: bool) -> PySequenceMethods { + PySequenceMethods { + length: if has_length { Some(length) } else { None }, + item: Some(item), + ass_item: if has_ass_item { Some(ass_item) } else { None }, + ..PySequenceMethods::NOT_IMPLEMENTED + } + } + + let key = bool_int(has_length) | (bool_int(has_ass_item) << 1); + + &METHODS[key] +} + +fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PySequenceMethods { if !zelf.class().has_attr(identifier!(vm, __getitem__)) { - return Cow::Borrowed(PySequenceMethods::not_implemented()); - } - - Cow::Owned(PySequenceMethods { - length: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __len__)), - |seq, vm| { length_wrapper(seq.obj.to_owned(), vm) } - ), - item: Some(|seq, i, vm| { - vm.call_special_method( - seq.obj.to_owned(), - identifier!(vm, __getitem__), - (i.to_pyobject(vm),), - ) - }), - ass_item: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __setitem__)) - | zelf.class().has_attr(identifier!(vm, __delitem__)), - |seq, i, value, vm| match value { - Some(value) => vm - .call_special_method( - seq.obj.to_owned(), - identifier!(vm, __setitem__), - (i.to_pyobject(vm), value), - ) - .map(|_| Ok(()))?, - None => vm - .call_special_method( - seq.obj.to_owned(), - identifier!(vm, __delitem__), - (i.to_pyobject(vm),) - ) - .map(|_| Ok(()))?, - } - ), - ..Default::default() - }) + return &PySequenceMethods::NOT_IMPLEMENTED; + } + + let (has_length, has_ass_item) = ( + zelf.class().has_attr(identifier!(vm, __len__)), + zelf.class().has_attr(identifier!(vm, __setitem__)) + | zelf.class().has_attr(identifier!(vm, __delitem__)), + ); + + static_as_sequence_generic(has_length, has_ass_item) } fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { @@ -384,8 +450,8 @@ impl PyType { } match name.as_str() { "__len__" | "__getitem__" | "__setitem__" | "__delitem__" => { - update_slot!(as_mapping, as_mapping_wrapper); - update_slot!(as_sequence, as_sequence_wrapper); + update_slot!(as_mapping, as_mapping_generic); + update_slot!(as_sequence, as_sequence_generic); } "__hash__" => { update_slot!(hash, hash_wrapper); @@ -896,15 +962,15 @@ pub trait AsBuffer: PyPayload { #[pyimpl] pub trait AsMapping: PyPayload { + const AS_MAPPING: PyMappingMethods; + #[inline] #[pyslot] - fn slot_as_mapping(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { - let zelf = unsafe { zelf.downcast_unchecked_ref::() }; - Self::as_mapping(zelf, vm) + fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PyMappingMethods { + &Self::AS_MAPPING } - fn as_mapping(zelf: &Py, vm: &VirtualMachine) -> PyMappingMethods; - + #[inline] fn mapping_downcast<'a>(mapping: &'a PyMapping) -> &'a Py { unsafe { mapping.obj.downcast_unchecked_ref() } } @@ -912,15 +978,14 @@ pub trait AsMapping: PyPayload { #[pyimpl] pub trait AsSequence: PyPayload { + const AS_SEQUENCE: PySequenceMethods; + #[inline] #[pyslot] - fn slot_as_sequence(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - let zelf = unsafe { zelf.downcast_unchecked_ref::() }; - Self::as_sequence(zelf, vm) + fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PySequenceMethods { + &Self::AS_SEQUENCE } - fn as_sequence(zelf: &Py, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods>; - fn sequence_downcast<'a>(seq: &'a PySequence) -> &'a Py { unsafe { seq.obj.downcast_unchecked_ref() } }