use std::cmp::Ordering; use crate::builtins::memory::Buffer; use crate::builtins::pystr::PyStrRef; use crate::common::hash::PyHash; use crate::common::lock::PyRwLock; use crate::function::{FuncArgs, OptionalArg, PyNativeFunc}; use crate::pyobject::{ Either, IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, }; use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; bitflags! { pub struct PyTpFlags: u64 { const HEAPTYPE = 1 << 9; const BASETYPE = 1 << 10; const HAS_DICT = 1 << 40; #[cfg(debug_assertions)] const _CREATED_WITH_FLAGS = 1 << 63; } } impl PyTpFlags { // CPython default: Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG pub const DEFAULT: Self = Self::HEAPTYPE; pub fn has_feature(self, flag: Self) -> bool { self.contains(flag) } #[cfg(debug_assertions)] pub fn is_created_with_flags(self) -> bool { self.contains(Self::_CREATED_WITH_FLAGS) } } impl Default for PyTpFlags { fn default() -> Self { Self::DEFAULT } } pub(crate) type GenericMethod = fn(&PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type DelFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<()>; pub(crate) type DescrGetFunc = fn(PyObjectRef, Option, Option, &VirtualMachine) -> PyResult; pub(crate) type HashFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult; pub(crate) type CmpFunc = fn( &PyObjectRef, &PyObjectRef, PyComparisonOp, &VirtualMachine, ) -> PyResult>; pub(crate) type GetattroFunc = fn(PyObjectRef, PyStrRef, &VirtualMachine) -> PyResult; pub(crate) type BufferFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult>; pub(crate) type IterFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult; pub(crate) type IterNextFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult; #[derive(Default)] pub struct PyTypeSlots { pub flags: PyTpFlags, pub name: PyRwLock>, // tp_name, not class name pub new: Option, pub del: AtomicCell>, pub call: AtomicCell>, pub descr_get: AtomicCell>, pub hash: AtomicCell>, pub cmp: AtomicCell>, pub getattro: AtomicCell>, pub buffer: Option, pub iter: AtomicCell>, pub iternext: AtomicCell>, } impl PyTypeSlots { pub fn from_flags(flags: PyTpFlags) -> Self { Self { flags, ..Default::default() } } } impl std::fmt::Debug for PyTypeSlots { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("PyTypeSlots") } } #[pyimpl] pub trait SlotDesctuctor: PyValue { #[pyslot] fn tp_del(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { if let Some(zelf) = zelf.downcast_ref() { Self::del(zelf, vm) } else { Err(vm.new_type_error("unexpected payload for __del__".to_owned())) } } #[pymethod(magic)] fn __del__(zelf: PyRef, vm: &VirtualMachine) -> PyResult<()> { Self::del(&zelf, vm) } fn del(zelf: &PyRef, vm: &VirtualMachine) -> PyResult<()>; } #[pyimpl] pub trait Callable: PyValue { #[pyslot] fn tp_call(zelf: &PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { if let Some(zelf) = zelf.downcast_ref() { Self::call(zelf, args, vm) } else { Err(vm.new_type_error("unexpected payload for __call__".to_owned())) } } #[pymethod] fn __call__(zelf: PyRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { Self::call(&zelf, args, vm) } fn call(zelf: &PyRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult; } #[pyimpl] pub trait SlotDescriptor: PyValue { #[pyslot] fn descr_get( zelf: PyObjectRef, obj: Option, cls: Option, vm: &VirtualMachine, ) -> PyResult; #[pymethod(magic)] fn get( zelf: PyObjectRef, obj: PyObjectRef, cls: OptionalArg, vm: &VirtualMachine, ) -> PyResult { Self::descr_get(zelf, Some(obj), cls.into_option(), vm) } fn _zelf(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult> { PyRef::::try_from_object(vm, zelf) } fn _unwrap( zelf: PyObjectRef, obj: Option, vm: &VirtualMachine, ) -> PyResult<(PyRef, PyObjectRef)> { let zelf = Self::_zelf(zelf, vm)?; let obj = vm.unwrap_or_none(obj); Ok((zelf, obj)) } fn _check( zelf: PyObjectRef, obj: Option, vm: &VirtualMachine, ) -> Result<(PyRef, PyObjectRef), PyResult> { // CPython descr_check if let Some(obj) = obj { // if (!PyObject_TypeCheck(obj, descr->d_type)) { // PyErr_Format(PyExc_TypeError, // "descriptor '%V' for '%.100s' objects " // "doesn't apply to a '%.100s' object", // descr_name((PyDescrObject *)descr), "?", // descr->d_type->tp_name, // obj->ob_type->tp_name); // *pres = NULL; // return 1; // } else { Ok((Self::_zelf(zelf, vm).unwrap(), obj)) // } } else { Err(Ok(zelf)) } } fn _cls_is(cls: &Option, other: &T) -> bool where T: IdProtocol, { cls.as_ref().map_or(false, |cls| other.is(cls)) } } #[pyimpl] pub trait Hashable: PyValue { #[pyslot] fn tp_hash(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(zelf) = zelf.downcast_ref() { Self::hash(zelf, vm) } else { Err(vm.new_type_error("unexpected payload for __hash__".to_owned())) } } #[pymethod] fn __hash__(zelf: PyRef, vm: &VirtualMachine) -> PyResult { Self::hash(&zelf, vm) } fn hash(zelf: &PyRef, vm: &VirtualMachine) -> PyResult; } pub trait Unhashable: PyValue {} impl Hashable for T where T: Unhashable, { fn hash(_zelf: &PyRef, vm: &VirtualMachine) -> PyResult { Err(vm.new_type_error("unhashable type".to_owned())) } } #[pyimpl] pub trait Comparable: PyValue { #[pyslot] fn tp_cmp( zelf: &PyObjectRef, other: &PyObjectRef, op: PyComparisonOp, vm: &VirtualMachine, ) -> PyResult> { if let Some(zelf) = zelf.downcast_ref() { Self::cmp(zelf, other, op, vm).map(Either::B) } else { Err(vm.new_type_error(format!("unexpected payload for {}", op.method_name()))) } } fn cmp( zelf: &PyRef, other: &PyObjectRef, op: PyComparisonOp, vm: &VirtualMachine, ) -> PyResult; #[pymethod(magic)] fn eq( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Eq, vm) } #[pymethod(magic)] fn ne( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Ne, vm) } #[pymethod(magic)] fn lt( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Lt, vm) } #[pymethod(magic)] fn le( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Le, vm) } #[pymethod(magic)] fn ge( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Ge, vm) } #[pymethod(magic)] fn gt( zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Gt, vm) } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PyComparisonOp { // be intentional with bits so that we can do eval_ord with just a bitwise and // bits: | Equal | Greater | Less | Lt = 0b001, Gt = 0b010, Ne = 0b011, Eq = 0b100, Le = 0b101, Ge = 0b110, } use PyComparisonOp::*; impl PyComparisonOp { pub fn eq_only( self, f: impl FnOnce() -> PyResult, ) -> PyResult { match self { Self::Eq => f(), Self::Ne => f().map(|x| x.map(|eq| !eq)), _ => Ok(PyComparisonValue::NotImplemented), } } pub fn eval_ord(self, ord: Ordering) -> bool { let bit = match ord { Ordering::Less => Lt, Ordering::Equal => Eq, Ordering::Greater => Gt, }; self as u8 & bit as u8 != 0 } pub fn swapped(self) -> Self { match self { Lt => Gt, Le => Ge, Eq => Eq, Ne => Ne, Ge => Le, Gt => Lt, } } pub fn method_name(self) -> &'static str { match self { Lt => "__lt__", Le => "__le__", Eq => "__eq__", Ne => "__ne__", Ge => "__ge__", Gt => "__gt__", } } pub fn operator_token(self) -> &'static str { match self { Lt => "<", Le => "<=", Eq => "==", Ne => "!=", Ge => ">=", Gt => ">", } } /// Returns an appropriate return value for the comparison when a and b are the same object, if an /// appropriate return value exists. pub fn identical_optimization(self, a: &impl IdProtocol, b: &impl IdProtocol) -> Option { self.map_eq(|| a.is(b)) } /// Returns `Some(true)` when self is `Eq` and `f()` returns true. Returns `Some(false)` when self /// is `Ne` and `f()` returns true. Otherwise returns `None`. pub fn map_eq(self, f: impl FnOnce() -> bool) -> Option { match self { Self::Eq => { if f() { Some(true) } else { None } } Self::Ne => { if f() { Some(false) } else { None } } _ => None, } } } #[pyimpl] pub trait SlotGetattro: PyValue { #[pyslot] fn tp_getattro(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult { if let Ok(zelf) = obj.downcast::() { Self::getattro(zelf, name, vm) } else { Err(vm.new_type_error("unexpected payload for __getattribute__".to_owned())) } } // TODO: make zelf: &PyRef fn getattro(zelf: PyRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult; #[pymethod] fn __getattribute__(zelf: PyRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult { Self::getattro(zelf, name, vm) } } #[pyimpl] pub trait BufferProtocol: PyValue { #[pyslot] fn tp_buffer(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult> { if let Some(zelf) = zelf.downcast_ref() { Self::get_buffer(zelf, vm) } else { Err(vm.new_type_error("unexpected payload for get_buffer".to_owned())) } } fn get_buffer(zelf: &PyRef, vm: &VirtualMachine) -> PyResult>; } #[pyimpl] pub trait Iterable: PyValue { #[pyslot] fn tp_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Ok(zelf) = zelf.downcast() { Self::iter(zelf, vm) } else { Err(vm.new_type_error("unexpected payload for __iter__".to_owned())) } } #[pymethod(magic)] fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult; } #[pyimpl(with(Iterable))] pub trait PyIter: PyValue { #[pyslot] fn tp_iternext(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(zelf) = zelf.downcast_ref() { Self::next(zelf, vm) } else { Err(vm.new_type_error("unexpected payload for __next__".to_owned())) } } fn next(zelf: &PyRef, vm: &VirtualMachine) -> PyResult; #[pymethod] fn __next__(zelf: PyRef, vm: &VirtualMachine) -> PyResult { Self::next(&zelf, vm) } } impl Iterable for T where T: PyIter, { fn tp_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { Ok(zelf) } fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyResult { Ok(zelf.into_object()) } }