diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 6e350b4df..0f12def12 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -380,12 +380,7 @@ impl PyRange { impl PyRange { fn protocol_length(&self, vm: &VirtualMachine) -> PyResult { - self.len() - .to_isize() - .ok_or_else(|| { - vm.new_overflow_error("RustPython int too large to convert to C ssize_t".to_owned()) - }) - .map(|x| x as usize) + PyInt::from(self.len()).try_to_primitive(vm) } const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index 6278df559..bd57b0ddf 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -684,6 +684,7 @@ pub trait DictKey { fn key_hash(&self, vm: &VirtualMachine) -> PyResult; fn key_is(&self, other: &PyObject) -> bool; fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult; + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult; } /// Implement trait for PyObjectRef such that we can use python objects @@ -701,6 +702,10 @@ impl DictKey for PyObjectRef { fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult { (**self).key_eq(vm, other_key) } + #[inline] + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + (**self).key_as_isize(vm) + } } impl DictKey for &PyObject { @@ -716,7 +721,12 @@ impl DictKey for &PyObject { fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult { (**self).key_eq(vm, other_key) } + #[inline] + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + (**self).key_as_isize(vm) + } } + impl DictKey for PyObject { fn key_hash(&self, vm: &VirtualMachine) -> PyResult { self.hash(vm) @@ -729,6 +739,10 @@ impl DictKey for PyObject { fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult { vm.identical_or_equal(self, other_key) } + + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + vm.to_index(self)?.try_to_primitive(vm) + } } impl DictKey for PyStrRef { @@ -749,6 +763,10 @@ impl DictKey for PyStrRef { vm.bool_eq(self.as_object(), other_key) } } + + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + self.as_object().key_as_isize(vm) + } } impl DictKey for PyRefExact { @@ -761,6 +779,9 @@ impl DictKey for PyRefExact { fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult { (**self).key_eq(vm, other_key) } + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + (**self).key_as_isize(vm) + } } // AsRef fit this case but not possible in rust 1.46 @@ -788,6 +809,10 @@ impl DictKey for &str { s.key_eq(vm, other_key) } } + + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("'str' object cannot be interpreted as an integer".to_owned())) + } } impl DictKey for String { @@ -802,6 +827,10 @@ impl DictKey for String { fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult { self.as_str().key_eq(vm, other_key) } + + fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult { + self.as_str().key_as_isize(vm) + } } impl DictKey for usize { @@ -825,6 +854,10 @@ impl DictKey for usize { vm.bool_eq(&int.into_object(), other_key) } } + + fn key_as_isize(&self, _vm: &VirtualMachine) -> PyResult { + Ok(*self as isize) + } } fn str_exact<'a>(obj: &'a PyObject, vm: &VirtualMachine) -> Option<&'a PyStr> { diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index cc17e3ac1..19dbec364 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -81,11 +81,6 @@ impl PyObjectRef { Ok(vm.length_hint_opt(self)?.unwrap_or(defaultvalue)) } - // item protocol - // PyObject *PyObject_GetItem(PyObject *o, PyObject *key) - // int PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v) - // int PyObject_DelItem(PyObject *o, PyObject *key) - // PyObject *PyObject_Dir(PyObject *o) /// Takes an object and returns an iterator for it. @@ -431,23 +426,19 @@ impl PyObject { if let Ok(mapping) = PyMapping::try_protocol(self, vm) { mapping.subscript(&needle, vm) + } else if let Ok(seq) = PySequence::try_protocol(self, vm) { + let i = needle.key_as_isize(vm)?; + seq.get_item(i, vm) } else { - // TODO: sequence protocol - match vm.get_special_method(self.to_owned(), "__getitem__")? { - Ok(special_method) => return special_method.invoke((needle,), vm), - Err(obj) => { - if obj.class().issubclass(&vm.ctx.types.type_type) { - if obj.is(&vm.ctx.types.type_type) { - return PyGenericAlias::new(obj.clone_class(), needle, vm) - .into_pyresult(vm); - } + if self.class().issubclass(&vm.ctx.types.type_type) { + if self.is(&vm.ctx.types.type_type) { + return PyGenericAlias::new(self.clone_class(), needle, vm).into_pyresult(vm); + } - if let Some(class_getitem) = - vm.get_attribute_opt(obj, "__class_getitem__")? - { - return vm.invoke(&class_getitem, (needle,)); - } - } + if let Some(class_getitem) = + vm.get_attribute_opt(self.to_owned(), "__class_getitem__")? + { + return vm.invoke(&class_getitem, (needle,)); } } Err(vm.new_type_error(format!("'{}' object is not subscriptable", self.class()))) @@ -464,22 +455,20 @@ impl PyObject { return dict.set_item(needle, value, vm); } - let needle = needle.into_pyobject(vm); - let mapping = PyMapping::from(self); + let seq = PySequence::from(self); + if let Some(f) = mapping.methods(vm).ass_subscript { + let needle = needle.into_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 { - // TODO: sequence protocol - vm.get_special_method(self.to_owned(), "__setitem__")? - .map_err(|obj| { - vm.new_type_error(format!( - "'{}' does not support item assignment", - obj.class() - )) - })? - .invoke((needle, value), vm)?; - Ok(()) + Err(vm.new_type_error(format!( + "'{}' does not support item assignment", + self.class() + ))) } } @@ -492,19 +481,17 @@ impl PyObject { return dict.del_item(needle, vm); } - let needle = needle.into_pyobject(vm); - let mapping = PyMapping::from(self); + let seq = PySequence::from(self); + if let Some(f) = mapping.methods(vm).ass_subscript { + let needle = needle.into_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 { - //TODO: sequence protocol - vm.get_special_method(self.to_owned(), "__delitem__")? - .map_err(|obj| { - vm.new_type_error(format!("'{}' does not support item deletion", obj.class())) - })? - .invoke((needle,), vm)?; - Ok(()) + Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) } } } diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index ad1c811d5..ac70d1a7c 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -9,9 +9,9 @@ mod _sre { }, common::{ascii, hash::PyHash}, function::{ArgCallable, IntoPyObject, OptionalArg, PosArgs}, - protocol::PyBuffer, + protocol::{PyBuffer, PyMappingMethods}, stdlib::sys, - types::{Comparable, Hashable}, + types::{AsMapping, Comparable, Hashable}, PyComparisonValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromBorrowedObject, TryFromObject, VirtualMachine, }; @@ -553,7 +553,7 @@ mod _sre { regs: Vec<(isize, isize)>, } - #[pyimpl] + #[pyimpl(with(AsMapping))] impl Match { pub(crate) fn new(state: &State, pattern: PyRef, string: PyObjectRef) -> Self { let mut regs = vec![(state.start as isize, state.string_position as isize)]; @@ -782,6 +782,22 @@ mod _sre { fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::new(cls, args, vm) } + + const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + length: None, + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping) + .getitem(needle.to_owned(), vm) + .map(|x| x.into_pyobject(vm)) + }), + ass_subscript: None, + }; + } + + impl AsMapping for Match { + fn as_mapping(_zelf: &crate::PyObjectView, _vm: &VirtualMachine) -> PyMappingMethods { + Self::MAPPING_METHODS + } } #[pyattr]