diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index da2fb020e..391f220d6 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -32,6 +32,17 @@ impl<'a> From<&'a PyObject> for PyMapping<'a> { methods: OnceCell::new(), } } + + pub fn length(&self, vm: &VirtualMachine) -> PyResult { + if let Some(f) = self.methods(vm).length { + f(self.0.clone(), vm) + } else { + Err(vm.new_type_error(format!( + "object of type '{}' has no len() or not a mapping", + self.0.class().name() + ))) + } + } } impl AsRef for PyMapping<'_> { diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 148abe09b..f24fcb040 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -408,12 +408,12 @@ impl PyObject { // int PyObject_TypeCheck(PyObject *o, PyTypeObject *type) pub fn length(&self, vm: &VirtualMachine) -> PyResult { - vm.obj_len_opt(self).unwrap_or_else(|| { - Err(vm.new_type_error(format!( - "object of type '{}' has no len()", - self.class().name() - ))) - }) + let seq = PySequence::from(self); + if let Ok(len) = seq.length(vm) { + Ok(len) + } else { + PyMapping::try_from_object(vm, self.to_owned())?.length(vm) + } } pub fn get_item( diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 90c115d29..a0387ab88 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -297,7 +297,7 @@ impl PySequence<'_> { if let Some(tuple) = self.obj.downcast_ref_if_exact::(vm) { Ok(tuple.to_owned()) } else if let Some(list) = self.obj.downcast_ref_if_exact::(vm) { - Ok(vm.ctx.new_tuple(list.borrow_vec().to_vec()).into()) + Ok(vm.ctx.new_tuple(list.borrow_vec().to_vec())) } else { let iter = self.obj.to_owned().get_iter(vm)?; let iter = iter.iter(vm)?; diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index d08b1ab9b..7eaaa5297 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -166,7 +166,7 @@ macro_rules! then_some_closure { pub use crate::builtins::object::{generic_getattr, generic_setattr}; fn slot_length(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let ret = vm.call_special_method(obj.to_owned(), "__len__", ())?; + let ret = vm.call_special_method(obj, "__len__", ())?; let len = ret.payload::().ok_or_else(|| { vm.new_type_error(format!( "'{}' object cannot be interpreted as an integer", diff --git a/vm/src/vm.rs b/vm/src/vm.rs index c3b1515c7..c6ce5453b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -30,7 +30,7 @@ use crate::{ PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crossbeam_utils::atomic::AtomicCell; -use num_traits::{Signed, ToPrimitive}; +use num_traits::ToPrimitive; use std::borrow::Cow; use std::cell::{Cell, Ref, RefCell}; use std::collections::{HashMap, HashSet}; @@ -1672,41 +1672,12 @@ impl VirtualMachine { .invoke((), self) } - pub fn obj_len_opt(&self, obj: &PyObject) -> Option> { - self.get_special_method(obj.to_owned(), "__len__") - .map(Result::ok) - .transpose() - .map(|meth| { - let len = meth?.invoke((), self)?; - let len = len - .payload_if_subclass::(self) - .ok_or_else(|| { - self.new_type_error(format!( - "'{}' object cannot be interpreted as an integer", - len.class().name() - )) - })? - .as_bigint(); - if len.is_negative() { - return Err(self.new_value_error("__len__() should return >= 0".to_owned())); - } - let len = len.to_isize().ok_or_else(|| { - self.new_overflow_error( - "cannot fit 'int' into an index-sized integer".to_owned(), - ) - })?; - Ok(len as usize) - }) - } - pub fn length_hint_opt(&self, iter: PyObjectRef) -> PyResult> { - if let Some(len) = self.obj_len_opt(&iter) { - match len { - Ok(len) => return Ok(Some(len)), - Err(e) => { - if !e.isinstance(&self.ctx.exceptions.type_error) { - return Err(e); - } + match iter.length(self) { + Ok(len) => return Ok(Some(len)), + Err(e) => { + if !e.isinstance(&self.ctx.exceptions.type_error) { + return Err(e); } } }