use crate::common::lock::{
PyMappedRwLockReadGuard, PyMappedRwLockWriteGuard, PyRwLockReadGuard, PyRwLockWriteGuard,
};
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyInt, PyStr, PyStrInterned, PyType, PyTypeRef},
bytecode::ComparisonOperator,
common::hash::{PyHash, fix_sentinel, hash_bigint},
convert::ToPyObject,
function::{Either, FromArgs, FuncArgs, PyComparisonValue, PyMethodDef, PySetterValue},
protocol::{
PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyMappingSlots, PyNumber,
PyNumberMethods, PyNumberSlots, PySequence, PySequenceMethods, PySequenceSlots,
},
types::slot_defs::{SlotAccessor, find_slot_defs_by_name},
vm::Context,
};
use core::{any::Any, any::TypeId, borrow::Borrow, cmp::Ordering, ops::Deref};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::{Signed, ToPrimitive};
use rustpython_common::wtf8::Wtf8Buf;
pub struct TypeDataSlot {
type_id: TypeId,
data: Box<dyn Any + Send + Sync>,
}
impl TypeDataSlot {
pub fn new<T: Any + Send + Sync + 'static>(data: T) -> Self {
Self {
type_id: TypeId::of::<T>(),
data: Box::new(data),
}
}
pub fn get<T: Any + 'static>(&self) -> Option<&T> {
if self.type_id == TypeId::of::<T>() {
self.data.downcast_ref()
} else {
None
}
}
pub fn get_mut<T: Any + 'static>(&mut self) -> Option<&mut T> {
if self.type_id == TypeId::of::<T>() {
self.data.downcast_mut()
} else {
None
}
}
}
pub struct TypeDataRef<'a, T: 'static> {
guard: PyMappedRwLockReadGuard<'a, T>,
}
impl<'a, T: Any + 'static> TypeDataRef<'a, T> {
pub fn try_new(guard: PyRwLockReadGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
PyRwLockReadGuard::try_map(guard, |opt| opt.as_ref().and_then(|slot| slot.get::<T>()))
.ok()
.map(|guard| Self { guard })
}
}
impl<T: Any + 'static> core::ops::Deref for TypeDataRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
pub struct TypeDataRefMut<'a, T: 'static> {
guard: PyMappedRwLockWriteGuard<'a, T>,
}
impl<'a, T: Any + 'static> TypeDataRefMut<'a, T> {
pub fn try_new(guard: PyRwLockWriteGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
PyRwLockWriteGuard::try_map(guard, |opt| {
opt.as_mut().and_then(|slot| slot.get_mut::<T>())
})
.ok()
.map(|guard| Self { guard })
}
}
impl<T: Any + 'static> core::ops::Deref for TypeDataRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: Any + 'static> core::ops::DerefMut for TypeDataRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
#[macro_export]
macro_rules! atomic_func {
($x:expr) => {
Some($x)
};
}
#[derive(Default)]
#[non_exhaustive]
pub struct PyTypeSlots {
pub(crate) name: &'static str,
pub basicsize: usize,
pub itemsize: usize,
pub as_number: PyNumberSlots,
pub as_sequence: PySequenceSlots,
pub as_mapping: PyMappingSlots,
pub hash: AtomicCell<Option<HashFunc>>,
pub call: AtomicCell<Option<GenericMethod>>,
pub vectorcall: AtomicCell<Option<VectorCallFunc>>,
pub str: AtomicCell<Option<StringifyFunc>>,
pub repr: AtomicCell<Option<StringifyFunc>>,
pub getattro: AtomicCell<Option<GetattroFunc>>,
pub setattro: AtomicCell<Option<SetattroFunc>>,
pub as_buffer: Option<AsBufferFunc>,
pub richcompare: AtomicCell<Option<RichCompareFunc>>,
pub iter: AtomicCell<Option<IterFunc>>,
pub iternext: AtomicCell<Option<IterNextFunc>>,
pub methods: &'static [PyMethodDef],
pub flags: PyTypeFlags,
pub doc: Option<&'static str>,
pub descr_get: AtomicCell<Option<DescrGetFunc>>,
pub descr_set: AtomicCell<Option<DescrSetFunc>>,
pub init: AtomicCell<Option<InitFunc>>,
pub alloc: AtomicCell<Option<AllocFunc>>,
pub new: AtomicCell<Option<NewFunc>>,
pub del: AtomicCell<Option<DelFunc>>,
pub member_count: usize,
}
impl PyTypeSlots {
pub fn new(name: &'static str, flags: PyTypeFlags) -> Self {
Self {
name,
flags,
..Default::default()
}
}
pub fn heap_default() -> Self {
Self {
..Default::default()
}
}
}
impl core::fmt::Debug for PyTypeSlots {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("PyTypeSlots")
}
}
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct PyTypeFlags: u64 {
const MANAGED_WEAKREF = 1 << 3;
const MANAGED_DICT = 1 << 4;
const SEQUENCE = 1 << 5;
const MAPPING = 1 << 6;
const DISALLOW_INSTANTIATION = 1 << 7;
const IMMUTABLETYPE = 1 << 8;
const HEAPTYPE = 1 << 9;
const BASETYPE = 1 << 10;
const METHOD_DESCRIPTOR = 1 << 17;
const _MATCH_SELF = 1 << 22;
const HAS_DICT = 1 << 40;
const HAS_WEAKREF = 1 << 41;
#[cfg(debug_assertions)]
const _CREATED_WITH_FLAGS = 1 << 63;
}
}
impl PyTypeFlags {
pub const DEFAULT: Self = Self::empty();
pub const fn heap_type_flags() -> Self {
match Self::from_bits(Self::DEFAULT.bits() | Self::HEAPTYPE.bits() | Self::BASETYPE.bits())
{
Some(flags) => flags,
None => unreachable!(),
}
}
pub const fn has_feature(self, flag: Self) -> bool {
self.contains(flag)
}
#[cfg(debug_assertions)]
pub const fn is_created_with_flags(self) -> bool {
self.contains(Self::_CREATED_WITH_FLAGS)
}
}
impl Default for PyTypeFlags {
fn default() -> Self {
Self::DEFAULT
}
}
pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult;
pub(crate) type VectorCallFunc = fn(
&PyObject, Vec<PyObjectRef>, usize, Option<&[PyObjectRef]>, &VirtualMachine,
) -> PyResult;
pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>;
pub(crate) type StringifyFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyRef<PyStr>>;
pub(crate) type GetattroFunc = fn(&PyObject, &Py<PyStr>, &VirtualMachine) -> PyResult;
pub(crate) type SetattroFunc =
fn(&PyObject, &Py<PyStr>, PySetterValue, &VirtualMachine) -> PyResult<()>;
pub(crate) type AsBufferFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyBuffer>;
pub(crate) type RichCompareFunc = fn(
&PyObject,
&PyObject,
PyComparisonOp,
&VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>>;
pub(crate) type IterFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult;
pub(crate) type IterNextFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyIterReturn>;
pub(crate) type DescrGetFunc =
fn(PyObjectRef, Option<PyObjectRef>, Option<PyObjectRef>, &VirtualMachine) -> PyResult;
pub(crate) type DescrSetFunc =
fn(&PyObject, PyObjectRef, PySetterValue, &VirtualMachine) -> PyResult<()>;
pub(crate) type AllocFunc = fn(PyTypeRef, usize, &VirtualMachine) -> PyResult;
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 SeqLenFunc = fn(PySequence<'_>, &VirtualMachine) -> PyResult<usize>;
pub(crate) type SeqConcatFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult;
pub(crate) type SeqRepeatFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult;
pub(crate) type SeqItemFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult;
pub(crate) type SeqAssItemFunc =
fn(PySequence<'_>, isize, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
pub(crate) type SeqContainsFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult<bool>;
pub(crate) type MapLenFunc = fn(PyMapping<'_>, &VirtualMachine) -> PyResult<usize>;
pub(crate) type MapSubscriptFunc = fn(PyMapping<'_>, &PyObject, &VirtualMachine) -> PyResult;
pub(crate) type MapAssSubscriptFunc =
fn(PyMapping<'_>, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
pub(crate) fn len_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?;
let len = ret.downcast_ref::<PyInt>().ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
ret.class()
))
})?;
let len = len.as_bigint();
if len.is_negative() {
return Err(vm.new_value_error("__len__() should return >= 0"));
}
let len = len
.to_isize()
.ok_or_else(|| vm.new_overflow_error("cannot fit 'int' into an index-sized integer"))?;
Ok(len as usize)
}
pub(crate) fn contains_wrapper(
obj: &PyObject,
needle: &PyObject,
vm: &VirtualMachine,
) -> PyResult<bool> {
let ret = vm.call_special_method(obj, identifier!(vm, __contains__), (needle,))?;
ret.try_to_bool(vm)
}
macro_rules! number_unary_op_wrapper {
($name:ident) => {
|a, vm| vm.call_special_method(a.deref(), identifier!(vm, $name), ())
};
}
macro_rules! number_binary_op_wrapper {
($name:ident) => {
|a, b, vm| vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(),))
};
}
macro_rules! number_binary_right_op_wrapper {
($name:ident) => {
|a, b, vm| vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),))
};
}
macro_rules! number_ternary_op_wrapper {
($name:ident) => {
|a, b, c, vm: &VirtualMachine| {
if vm.is_none(c) {
vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(),))
} else {
vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(), c.to_owned()))
}
}
};
}
macro_rules! number_ternary_right_op_wrapper {
($name:ident) => {
|a, b, c, vm: &VirtualMachine| {
if vm.is_none(c) {
vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),))
} else {
vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(), c.to_owned()))
}
}
};
}
fn getitem_wrapper<K: ToPyObject>(obj: &PyObject, needle: K, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(obj, identifier!(vm, __getitem__), (needle,))
}
fn setitem_wrapper<K: ToPyObject>(
obj: &PyObject,
needle: K,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
Some(value) => vm.call_special_method(obj, identifier!(vm, __setitem__), (needle, value)),
None => vm.call_special_method(obj, identifier!(vm, __delitem__), (needle,)),
}
.map(drop)
}
#[inline(never)]
fn mapping_setitem_wrapper(
mapping: PyMapping<'_>,
key: &PyObject,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
setitem_wrapper(mapping.obj, key, value, vm)
}
#[inline(never)]
fn mapping_getitem_wrapper(
mapping: PyMapping<'_>,
key: &PyObject,
vm: &VirtualMachine,
) -> PyResult {
getitem_wrapper(mapping.obj, key, vm)
}
#[inline(never)]
fn mapping_len_wrapper(mapping: PyMapping<'_>, vm: &VirtualMachine) -> PyResult<usize> {
len_wrapper(mapping.obj, vm)
}
#[inline(never)]
fn sequence_len_wrapper(seq: PySequence<'_>, vm: &VirtualMachine) -> PyResult<usize> {
len_wrapper(seq.obj, vm)
}
#[inline(never)]
fn sequence_getitem_wrapper(seq: PySequence<'_>, i: isize, vm: &VirtualMachine) -> PyResult {
getitem_wrapper(seq.obj, i, vm)
}
#[inline(never)]
fn sequence_setitem_wrapper(
seq: PySequence<'_>,
i: isize,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
setitem_wrapper(seq.obj, i, value, vm)
}
#[inline(never)]
fn sequence_contains_wrapper(
seq: PySequence<'_>,
needle: &PyObject,
vm: &VirtualMachine,
) -> PyResult<bool> {
contains_wrapper(seq.obj, needle, vm)
}
#[inline(never)]
fn sequence_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(seq.obj, identifier!(vm, __mul__), (n,))
}
#[inline(never)]
fn sequence_inplace_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(seq.obj, identifier!(vm, __imul__), (n,))
}
fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let ret = vm.call_special_method(zelf, identifier!(vm, __repr__), ())?;
ret.downcast::<PyStr>().map_err(|obj| {
vm.new_type_error(format!(
"__repr__ returned non-string (type {})",
obj.class()
))
})
}
fn str_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let ret = vm.call_special_method(zelf, identifier!(vm, __str__), ())?;
ret.downcast::<PyStr>().map_err(|obj| {
vm.new_type_error(format!(
"__str__ returned non-string (type {})",
obj.class()
))
})
}
fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
let hash_obj = vm.call_special_method(zelf, identifier!(vm, __hash__), ())?;
let py_int = hash_obj
.downcast_ref::<PyInt>()
.ok_or_else(|| vm.new_type_error("__hash__ method should return an integer"))?;
let big_int = py_int.as_bigint();
let hash = big_int
.to_i64()
.map(fix_sentinel)
.unwrap_or_else(|| hash_bigint(big_int));
Ok(hash)
}
pub fn hash_not_implemented(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
Err(vm.new_type_error(format!("unhashable type: '{}'", zelf.class().name())))
}
fn call_wrapper(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(zelf, identifier!(vm, __call__), args)
}
fn getattro_wrapper(zelf: &PyObject, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
let __getattribute__ = identifier!(vm, __getattribute__);
let __getattr__ = identifier!(vm, __getattr__);
match vm.call_special_method(zelf, __getattribute__, (name.to_owned(),)) {
Ok(r) => Ok(r),
Err(e)
if e.fast_isinstance(vm.ctx.exceptions.attribute_error)
&& zelf.class().has_attr(__getattr__) =>
{
vm.call_special_method(zelf, __getattr__, (name.to_owned(),))
}
Err(e) => Err(e),
}
}
fn setattro_wrapper(
zelf: &PyObject,
name: &Py<PyStr>,
value: PySetterValue,
vm: &VirtualMachine,
) -> PyResult<()> {
let name = name.to_owned();
match value {
PySetterValue::Assign(value) => {
vm.call_special_method(zelf, identifier!(vm, __setattr__), (name, value))?;
}
PySetterValue::Delete => {
vm.call_special_method(zelf, identifier!(vm, __delattr__), (name,))?;
}
};
Ok(())
}
pub(crate) fn richcompare_wrapper(
zelf: &PyObject,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
vm.call_special_method(zelf, op.method_name(&vm.ctx), (other.to_owned(),))
.map(Either::A)
}
fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let cls = zelf.class();
let iter_attr = cls.get_attr(identifier!(vm, __iter__));
match iter_attr {
Some(attr) if vm.is_none(&attr) => {
Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name())))
}
_ => vm.call_special_method(&zelf, identifier!(vm, __iter__), ()),
}
}
fn bool_wrapper(num: PyNumber<'_>, vm: &VirtualMachine) -> PyResult<bool> {
let result = vm.call_special_method(num.obj, identifier!(vm, __bool__), ())?;
if !result.class().is(vm.ctx.types.bool_type) {
return Err(vm.new_type_error(format!(
"__bool__ should return bool, returned {}",
result.class().name()
)));
}
Ok(crate::builtins::bool_::get_value(&result))
}
const fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult {
Ok(zelf)
}
fn iternext_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
PyIterReturn::from_pyresult(
vm.call_special_method(zelf, identifier!(vm, __next__), ()),
vm,
)
}
fn descr_get_wrapper(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
vm.call_special_method(&zelf, identifier!(vm, __get__), (obj, cls))
}
fn descr_set_wrapper(
zelf: &PyObject,
obj: PyObjectRef,
value: PySetterValue,
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
PySetterValue::Assign(val) => {
vm.call_special_method(zelf, identifier!(vm, __set__), (obj, val))
}
PySetterValue::Delete => vm.call_special_method(zelf, identifier!(vm, __delete__), (obj,)),
}
.map(drop)
}
fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let res = vm.call_special_method(&obj, identifier!(vm, __init__), args)?;
if !vm.is_none(&res) {
return Err(vm.new_type_error(format!(
"__init__() should return None, not '{:.200}'",
res.class().name()
)));
}
Ok(())
}
pub(crate) fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let new = cls.get_attr(identifier!(vm, __new__)).unwrap();
args.prepend_arg(cls.into());
new.call(args, vm)
}
fn del_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
vm.call_special_method(zelf, identifier!(vm, __del__), ())?;
Ok(())
}
enum SlotLookupResult<T> {
NativeSlot(T),
PythonMethod,
NotFound,
}
impl PyType {
pub(crate) fn update_slot<const ADD: bool>(&self, name: &'static PyStrInterned, ctx: &Context) {
debug_assert!(name.as_str().starts_with("__"));
debug_assert!(name.as_str().ends_with("__"));
let defs: Vec<_> = find_slot_defs_by_name(name.as_str()).collect();
for def in defs {
self.update_one_slot::<ADD>(&def.accessor, name, ctx);
}
self.update_subclasses::<ADD>(name, ctx);
}
fn update_subclasses<const ADD: bool>(&self, name: &'static PyStrInterned, ctx: &Context) {
let subclasses = self.subclasses.read();
for weak_ref in subclasses.iter() {
let Some(subclass) = weak_ref.upgrade() else {
continue;
};
let Some(subclass) = subclass.downcast_ref::<PyType>() else {
continue;
};
if subclass.attributes.read().contains_key(name) {
continue;
}
for def in find_slot_defs_by_name(name.as_str()) {
subclass.update_one_slot::<ADD>(&def.accessor, name, ctx);
}
subclass.update_subclasses::<ADD>(name, ctx);
}
}
fn update_one_slot<const ADD: bool>(
&self,
accessor: &SlotAccessor,
name: &'static PyStrInterned,
ctx: &Context,
) {
use crate::builtins::descriptor::SlotFunc;
macro_rules! update_main_slot {
($slot:ident, $wrapper:expr, $variant:ident) => {{
if ADD {
match self.lookup_slot_in_mro(name, ctx, |sf| {
if let SlotFunc::$variant(f) = sf {
Some(*f)
} else {
None
}
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.$slot.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.$slot.store(Some($wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
} else {
accessor.inherit_from_mro(self);
}
}};
}
macro_rules! update_sub_slot {
($group:ident, $slot:ident, $wrapper:expr, $variant:ident) => {{
if ADD {
let has_own = {
let guard = self.attributes.read();
let mut result = guard.contains_key(name);
if !result
&& (stringify!($slot) == "ass_item"
|| stringify!($slot) == "ass_subscript")
{
let setitem = ctx.intern_str("__setitem__");
let delitem = ctx.intern_str("__delitem__");
result = guard.contains_key(setitem) || guard.contains_key(delitem);
}
result
};
if has_own {
self.slots.$group.$slot.store(Some($wrapper));
} else {
match self.lookup_slot_in_mro(name, ctx, |sf| {
if let SlotFunc::$variant(f) = sf {
Some(*f)
} else {
None
}
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.$group.$slot.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.$group.$slot.store(Some($wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
}
} else {
accessor.inherit_from_mro(self);
}
}};
}
match accessor {
SlotAccessor::TpRepr => update_main_slot!(repr, repr_wrapper, Repr),
SlotAccessor::TpStr => update_main_slot!(str, str_wrapper, Str),
SlotAccessor::TpHash => {
if ADD {
let method = self.attributes.read().get(name).cloned().or_else(|| {
self.mro
.read()
.iter()
.find_map(|cls| cls.attributes.read().get(name).cloned())
});
if method.as_ref().is_some_and(|m| m.is(&ctx.none)) {
self.slots.hash.store(Some(hash_not_implemented));
} else {
match self.lookup_slot_in_mro(name, ctx, |sf| {
if let SlotFunc::Hash(f) = sf {
Some(*f)
} else {
None
}
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.hash.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.hash.store(Some(hash_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::TpCall => {
update_main_slot!(call, call_wrapper, Call);
if ADD {
self.slots.vectorcall.store(None);
}
}
SlotAccessor::TpIter => update_main_slot!(iter, iter_wrapper, Iter),
SlotAccessor::TpIternext => update_main_slot!(iternext, iternext_wrapper, IterNext),
SlotAccessor::TpInit => {
update_main_slot!(init, init_wrapper, Init);
if ADD {
self.slots.vectorcall.store(None);
}
}
SlotAccessor::TpNew => {
if ADD {
self.slots.new.store(Some(new_wrapper));
self.slots.vectorcall.store(None);
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::TpDel => update_main_slot!(del, del_wrapper, Del),
SlotAccessor::TpGetattro => {
let __getattr__ = identifier!(ctx, __getattr__);
let has_getattr = {
let attrs = self.attributes.read();
let in_self = attrs.contains_key(__getattr__);
drop(attrs);
in_self
|| self
.mro
.read()
.iter()
.skip(1)
.any(|cls| cls.attributes.read().contains_key(__getattr__))
};
if has_getattr {
self.slots.getattro.store(Some(getattro_wrapper));
} else if ADD {
match self.lookup_slot_in_mro(name, ctx, |sf| {
if let SlotFunc::GetAttro(f) = sf {
Some(*f)
} else {
None
}
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.getattro.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.getattro.store(Some(getattro_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::TpSetattro => {
if ADD {
match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::SetAttro(f) | SlotFunc::DelAttro(f) => Some(*f),
_ => None,
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.setattro.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.setattro.store(Some(setattro_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::TpDescrGet => update_main_slot!(descr_get, descr_get_wrapper, DescrGet),
SlotAccessor::TpDescrSet => {
if ADD {
match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::DescrSet(f) | SlotFunc::DescrDel(f) => Some(*f),
_ => None,
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.descr_set.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.descr_set.store(Some(descr_set_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::TpRichcompare => {
if ADD {
let cmp_names = [
identifier!(ctx, __eq__),
identifier!(ctx, __ne__),
identifier!(ctx, __lt__),
identifier!(ctx, __le__),
identifier!(ctx, __gt__),
identifier!(ctx, __ge__),
];
let has_python_cmp = {
let attrs = self.attributes.read();
let in_self = cmp_names.iter().any(|n| attrs.contains_key(*n));
drop(attrs);
in_self
|| self.mro.read()[1..].iter().any(|cls| {
let attrs = cls.attributes.read();
cmp_names.iter().any(|n| {
if let Some(attr) = attrs.get(*n) {
!attr.class().is(ctx.types.wrapper_descriptor_type)
&& !attr.class().is(ctx.types.method_descriptor_type)
} else {
false
}
})
})
};
if has_python_cmp {
self.slots.richcompare.store(Some(richcompare_wrapper));
} else {
match self.lookup_slot_in_mro(name, ctx, |sf| {
if let SlotFunc::RichCompare(f, _) = sf {
Some(*f)
} else {
None
}
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.richcompare.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots.richcompare.store(Some(richcompare_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::NbAdd => {
if name.as_str() == "__radd__" {
update_sub_slot!(
as_number,
right_add,
number_binary_right_op_wrapper!(__radd__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
add,
number_binary_op_wrapper!(__add__),
NumBinary
)
}
}
SlotAccessor::NbInplaceAdd => {
update_sub_slot!(
as_number,
inplace_add,
number_binary_op_wrapper!(__iadd__),
NumBinary
)
}
SlotAccessor::NbSubtract => {
if name.as_str() == "__rsub__" {
update_sub_slot!(
as_number,
right_subtract,
number_binary_right_op_wrapper!(__rsub__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
subtract,
number_binary_op_wrapper!(__sub__),
NumBinary
)
}
}
SlotAccessor::NbInplaceSubtract => {
update_sub_slot!(
as_number,
inplace_subtract,
number_binary_op_wrapper!(__isub__),
NumBinary
)
}
SlotAccessor::NbMultiply => {
if name.as_str() == "__rmul__" {
update_sub_slot!(
as_number,
right_multiply,
number_binary_right_op_wrapper!(__rmul__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
multiply,
number_binary_op_wrapper!(__mul__),
NumBinary
)
}
}
SlotAccessor::NbInplaceMultiply => {
update_sub_slot!(
as_number,
inplace_multiply,
number_binary_op_wrapper!(__imul__),
NumBinary
)
}
SlotAccessor::NbRemainder => {
if name.as_str() == "__rmod__" {
update_sub_slot!(
as_number,
right_remainder,
number_binary_right_op_wrapper!(__rmod__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
remainder,
number_binary_op_wrapper!(__mod__),
NumBinary
)
}
}
SlotAccessor::NbInplaceRemainder => {
update_sub_slot!(
as_number,
inplace_remainder,
number_binary_op_wrapper!(__imod__),
NumBinary
)
}
SlotAccessor::NbDivmod => {
if name.as_str() == "__rdivmod__" {
update_sub_slot!(
as_number,
right_divmod,
number_binary_right_op_wrapper!(__rdivmod__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
divmod,
number_binary_op_wrapper!(__divmod__),
NumBinary
)
}
}
SlotAccessor::NbPower => {
if name.as_str() == "__rpow__" {
update_sub_slot!(
as_number,
right_power,
number_ternary_right_op_wrapper!(__rpow__),
NumTernary
)
} else {
update_sub_slot!(
as_number,
power,
number_ternary_op_wrapper!(__pow__),
NumTernary
)
}
}
SlotAccessor::NbInplacePower => {
update_sub_slot!(
as_number,
inplace_power,
number_ternary_op_wrapper!(__ipow__),
NumTernary
)
}
SlotAccessor::NbFloorDivide => {
if name.as_str() == "__rfloordiv__" {
update_sub_slot!(
as_number,
right_floor_divide,
number_binary_right_op_wrapper!(__rfloordiv__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
floor_divide,
number_binary_op_wrapper!(__floordiv__),
NumBinary
)
}
}
SlotAccessor::NbInplaceFloorDivide => {
update_sub_slot!(
as_number,
inplace_floor_divide,
number_binary_op_wrapper!(__ifloordiv__),
NumBinary
)
}
SlotAccessor::NbTrueDivide => {
if name.as_str() == "__rtruediv__" {
update_sub_slot!(
as_number,
right_true_divide,
number_binary_right_op_wrapper!(__rtruediv__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
true_divide,
number_binary_op_wrapper!(__truediv__),
NumBinary
)
}
}
SlotAccessor::NbInplaceTrueDivide => {
update_sub_slot!(
as_number,
inplace_true_divide,
number_binary_op_wrapper!(__itruediv__),
NumBinary
)
}
SlotAccessor::NbMatrixMultiply => {
if name.as_str() == "__rmatmul__" {
update_sub_slot!(
as_number,
right_matrix_multiply,
number_binary_right_op_wrapper!(__rmatmul__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
matrix_multiply,
number_binary_op_wrapper!(__matmul__),
NumBinary
)
}
}
SlotAccessor::NbInplaceMatrixMultiply => {
update_sub_slot!(
as_number,
inplace_matrix_multiply,
number_binary_op_wrapper!(__imatmul__),
NumBinary
)
}
SlotAccessor::NbLshift => {
if name.as_str() == "__rlshift__" {
update_sub_slot!(
as_number,
right_lshift,
number_binary_right_op_wrapper!(__rlshift__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
lshift,
number_binary_op_wrapper!(__lshift__),
NumBinary
)
}
}
SlotAccessor::NbInplaceLshift => {
update_sub_slot!(
as_number,
inplace_lshift,
number_binary_op_wrapper!(__ilshift__),
NumBinary
)
}
SlotAccessor::NbRshift => {
if name.as_str() == "__rrshift__" {
update_sub_slot!(
as_number,
right_rshift,
number_binary_right_op_wrapper!(__rrshift__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
rshift,
number_binary_op_wrapper!(__rshift__),
NumBinary
)
}
}
SlotAccessor::NbInplaceRshift => {
update_sub_slot!(
as_number,
inplace_rshift,
number_binary_op_wrapper!(__irshift__),
NumBinary
)
}
SlotAccessor::NbAnd => {
if name.as_str() == "__rand__" {
update_sub_slot!(
as_number,
right_and,
number_binary_right_op_wrapper!(__rand__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
and,
number_binary_op_wrapper!(__and__),
NumBinary
)
}
}
SlotAccessor::NbInplaceAnd => {
update_sub_slot!(
as_number,
inplace_and,
number_binary_op_wrapper!(__iand__),
NumBinary
)
}
SlotAccessor::NbXor => {
if name.as_str() == "__rxor__" {
update_sub_slot!(
as_number,
right_xor,
number_binary_right_op_wrapper!(__rxor__),
NumBinary
)
} else {
update_sub_slot!(
as_number,
xor,
number_binary_op_wrapper!(__xor__),
NumBinary
)
}
}
SlotAccessor::NbInplaceXor => {
update_sub_slot!(
as_number,
inplace_xor,
number_binary_op_wrapper!(__ixor__),
NumBinary
)
}
SlotAccessor::NbOr => {
if name.as_str() == "__ror__" {
update_sub_slot!(
as_number,
right_or,
number_binary_right_op_wrapper!(__ror__),
NumBinary
)
} else {
update_sub_slot!(as_number, or, number_binary_op_wrapper!(__or__), NumBinary)
}
}
SlotAccessor::NbInplaceOr => {
update_sub_slot!(
as_number,
inplace_or,
number_binary_op_wrapper!(__ior__),
NumBinary
)
}
SlotAccessor::NbNegative => {
update_sub_slot!(
as_number,
negative,
number_unary_op_wrapper!(__neg__),
NumUnary
)
}
SlotAccessor::NbPositive => {
update_sub_slot!(
as_number,
positive,
number_unary_op_wrapper!(__pos__),
NumUnary
)
}
SlotAccessor::NbAbsolute => {
update_sub_slot!(
as_number,
absolute,
number_unary_op_wrapper!(__abs__),
NumUnary
)
}
SlotAccessor::NbInvert => {
update_sub_slot!(
as_number,
invert,
number_unary_op_wrapper!(__invert__),
NumUnary
)
}
SlotAccessor::NbBool => {
update_sub_slot!(as_number, boolean, bool_wrapper, NumBoolean)
}
SlotAccessor::NbInt => {
update_sub_slot!(as_number, int, number_unary_op_wrapper!(__int__), NumUnary)
}
SlotAccessor::NbFloat => {
update_sub_slot!(
as_number,
float,
number_unary_op_wrapper!(__float__),
NumUnary
)
}
SlotAccessor::NbIndex => {
update_sub_slot!(
as_number,
index,
number_unary_op_wrapper!(__index__),
NumUnary
)
}
SlotAccessor::SqLength => {
update_sub_slot!(as_sequence, length, sequence_len_wrapper, SeqLength)
}
SlotAccessor::SqConcat | SlotAccessor::SqInplaceConcat => {
if !ADD {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::SqRepeat => {
update_sub_slot!(as_sequence, repeat, sequence_repeat_wrapper, SeqRepeat)
}
SlotAccessor::SqInplaceRepeat => {
update_sub_slot!(
as_sequence,
inplace_repeat,
sequence_inplace_repeat_wrapper,
SeqRepeat
)
}
SlotAccessor::SqItem => {
update_sub_slot!(as_sequence, item, sequence_getitem_wrapper, SeqItem)
}
SlotAccessor::SqAssItem => {
if ADD {
let has_own = {
let guard = self.attributes.read();
let setitem = ctx.intern_str("__setitem__");
let delitem = ctx.intern_str("__delitem__");
guard.contains_key(setitem) || guard.contains_key(delitem)
};
if has_own {
self.slots
.as_sequence
.ass_item
.store(Some(sequence_setitem_wrapper));
} else {
match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::SeqSetItem(f) | SlotFunc::SeqDelItem(f) => Some(*f),
_ => None,
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.as_sequence.ass_item.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots
.as_sequence
.ass_item
.store(Some(sequence_setitem_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
}
} else {
accessor.inherit_from_mro(self);
}
}
SlotAccessor::SqContains => {
update_sub_slot!(
as_sequence,
contains,
sequence_contains_wrapper,
SeqContains
)
}
SlotAccessor::MpLength => {
update_sub_slot!(as_mapping, length, mapping_len_wrapper, MapLength)
}
SlotAccessor::MpSubscript => {
update_sub_slot!(as_mapping, subscript, mapping_getitem_wrapper, MapSubscript)
}
SlotAccessor::MpAssSubscript => {
if ADD {
let has_own = {
let guard = self.attributes.read();
let setitem = ctx.intern_str("__setitem__");
let delitem = ctx.intern_str("__delitem__");
guard.contains_key(setitem) || guard.contains_key(delitem)
};
if has_own {
self.slots
.as_mapping
.ass_subscript
.store(Some(mapping_setitem_wrapper));
} else {
match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
SlotFunc::MapSetSubscript(f) | SlotFunc::MapDelSubscript(f) => Some(*f),
_ => None,
}) {
SlotLookupResult::NativeSlot(func) => {
self.slots.as_mapping.ass_subscript.store(Some(func));
}
SlotLookupResult::PythonMethod => {
self.slots
.as_mapping
.ass_subscript
.store(Some(mapping_setitem_wrapper));
}
SlotLookupResult::NotFound => {
accessor.inherit_from_mro(self);
}
}
}
} else {
accessor.inherit_from_mro(self);
}
}
_ => {}
}
}
fn lookup_slot_in_mro<T: Copy>(
&self,
name: &'static PyStrInterned,
ctx: &Context,
extract: impl Fn(&crate::builtins::descriptor::SlotFunc) -> Option<T>,
) -> SlotLookupResult<T> {
use crate::builtins::descriptor::PyWrapper;
let is_subclass_of = |subclass_mro: &[PyRef<PyType>], superclass: &Py<PyType>| -> bool {
subclass_mro.iter().any(|c| c.is(superclass))
};
let try_extract = |attr: &PyObjectRef, for_class_mro: &[PyRef<PyType>]| -> Option<T> {
if attr.class().is(ctx.types.wrapper_descriptor_type) {
attr.downcast_ref::<PyWrapper>().and_then(|wrapper| {
if is_subclass_of(for_class_mro, wrapper.typ) {
extract(&wrapper.wrapped)
} else {
None
}
})
} else {
None
}
};
let mro = self.mro.read();
if let Some(attr) = self.attributes.read().get(name).cloned() {
if let Some(func) = try_extract(&attr, &mro) {
return SlotLookupResult::NativeSlot(func);
}
return SlotLookupResult::PythonMethod;
}
for (i, cls) in mro[1..].iter().enumerate() {
if let Some(attr) = cls.attributes.read().get(name).cloned() {
if let Some(func) = try_extract(&attr, &mro[i + 1..]) {
return SlotLookupResult::NativeSlot(func);
}
return SlotLookupResult::PythonMethod;
}
}
SlotLookupResult::NotFound
}
}
#[pyclass]
pub trait Constructor: PyPayload + core::fmt::Debug {
type Args: FromArgs;
#[inline]
#[pyslot]
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let args: Self::Args = args.bind(vm)?;
let payload = Self::py_new(&cls, args, vm)?;
payload.into_ref_with_type(vm, cls).map(Into::into)
}
fn py_new(cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self>;
}
pub trait DefaultConstructor: PyPayload + Default + core::fmt::Debug {
fn construct_and_init(args: Self::Args, vm: &VirtualMachine) -> PyResult<PyRef<Self>>
where
Self: Initializer,
{
let this = Self::default().into_ref(&vm.ctx);
Self::init(this.clone(), args, vm)?;
Ok(this)
}
}
impl<T> Constructor for T
where
T: DefaultConstructor,
{
type Args = FuncArgs;
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Self::default().into_ref_with_type(vm, cls).map(Into::into)
}
fn py_new(cls: &Py<PyType>, _args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
Err(vm.new_type_error(format!("cannot create {} instances", cls.slot_name())))
}
}
#[pyclass]
pub trait Initializer: PyPayload {
type Args: FromArgs;
#[inline]
#[pyslot]
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
#[cfg(debug_assertions)]
let class_name_for_debug = zelf.class().name().to_string();
let zelf = match zelf.try_into_value(vm) {
Ok(zelf) => zelf,
Err(err) => {
#[cfg(debug_assertions)]
{
if let Ok(msg) = err.as_object().repr(vm) {
let double_appearance = msg
.to_string_lossy()
.matches(&class_name_for_debug as &str)
.count()
== 2;
if double_appearance {
panic!(
"This type `{}` doesn't seem to support `init`. Override `slot_init` instead: {}",
class_name_for_debug, msg
);
}
}
}
return Err(err);
}
};
let args: Self::Args = args.bind(vm)?;
Self::init(zelf, args, vm)
}
fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()>;
}
#[pyclass]
pub trait Destructor: PyPayload {
#[inline] #[pyslot]
fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for __del__"))?;
Self::del(zelf, vm)
}
fn del(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<()>;
}
#[pyclass]
pub trait Callable: PyPayload {
type Args: FromArgs;
#[inline]
#[pyslot]
fn slot_call(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let zelf = zelf.downcast_ref().ok_or_else(|| {
let repr = zelf.repr(vm);
let help: Wtf8Buf = if let Ok(repr) = repr.as_ref() {
repr.as_wtf8().to_owned()
} else {
zelf.class().name().to_owned().into()
};
let mut msg = Wtf8Buf::from("unexpected payload for __call__ of ");
msg.push_wtf8(&help);
vm.new_type_error(msg)
})?;
let args = args.bind(vm)?;
Self::call(zelf, args, vm)
}
fn call(zelf: &Py<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult;
}
#[pyclass]
pub trait GetDescriptor: PyPayload {
#[pyslot]
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult;
#[inline]
fn _as_pyref<'a>(zelf: &'a PyObject, vm: &VirtualMachine) -> PyResult<&'a Py<Self>> {
zelf.try_to_value(vm)
}
#[inline]
fn _unwrap<'a>(
zelf: &'a PyObject,
obj: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<(&'a Py<Self>, PyObjectRef)> {
let zelf = Self::_as_pyref(zelf, vm)?;
let obj = vm.unwrap_or_none(obj);
Ok((zelf, obj))
}
#[inline]
fn _check<'a>(
zelf: &'a PyObject,
obj: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> Option<(&'a Py<Self>, PyObjectRef)> {
let obj = obj?;
Some((Self::_as_pyref(zelf, vm).unwrap(), obj))
}
#[inline]
fn _cls_is(cls: &Option<PyObjectRef>, other: &impl Borrow<PyObject>) -> bool {
cls.as_ref().is_some_and(|cls| other.borrow().is(cls))
}
}
#[pyclass]
pub trait Hashable: PyPayload {
#[inline]
#[pyslot]
fn slot_hash(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for __hash__"))?;
Self::hash(zelf, vm)
}
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash>;
}
#[pyclass]
pub trait Representable: PyPayload {
#[inline]
#[pyslot]
fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for __repr__"))?;
Self::repr(zelf, vm)
}
#[inline]
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let repr = Self::repr_wtf8(zelf, vm)?;
Ok(vm.ctx.new_str(repr))
}
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
Self::repr_str(zelf, vm).map(|utf8| utf8.into())
}
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
unreachable!("Representable requires overriding either repr_str or repr_wtf8")
}
}
#[pyclass]
pub trait Comparable: PyPayload {
#[inline]
#[pyslot]
fn slot_richcompare(
zelf: &PyObject,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
let zelf = zelf.downcast_ref().ok_or_else(|| {
vm.new_type_error(format!(
"unexpected payload for {}",
op.method_name(&vm.ctx).as_str()
))
})?;
Self::cmp(zelf, other, op, vm).map(Either::B)
}
fn cmp(
zelf: &Py<Self>,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue>;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct PyComparisonOp(ComparisonOperator);
impl From<ComparisonOperator> for PyComparisonOp {
fn from(op: ComparisonOperator) -> Self {
Self(op)
}
}
#[allow(non_upper_case_globals)]
impl PyComparisonOp {
pub const Lt: Self = Self(ComparisonOperator::Less);
pub const Gt: Self = Self(ComparisonOperator::Greater);
pub const Ne: Self = Self(ComparisonOperator::NotEqual);
pub const Eq: Self = Self(ComparisonOperator::Equal);
pub const Le: Self = Self(ComparisonOperator::LessOrEqual);
pub const Ge: Self = Self(ComparisonOperator::GreaterOrEqual);
}
impl PyComparisonOp {
pub fn eq_only(
self,
f: impl FnOnce() -> PyResult<PyComparisonValue>,
) -> PyResult<PyComparisonValue> {
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 {
match self {
Self::Lt => ord == Ordering::Less,
Self::Le => ord != Ordering::Greater,
Self::Eq => ord == Ordering::Equal,
Self::Ne => ord != Ordering::Equal,
Self::Gt => ord == Ordering::Greater,
Self::Ge => ord != Ordering::Less,
}
}
pub const fn swapped(self) -> Self {
match self {
Self::Lt => Self::Gt,
Self::Le => Self::Ge,
Self::Eq => Self::Eq,
Self::Ne => Self::Ne,
Self::Ge => Self::Le,
Self::Gt => Self::Lt,
}
}
pub fn method_name(self, ctx: &Context) -> &'static PyStrInterned {
match self {
Self::Lt => identifier!(ctx, __lt__),
Self::Le => identifier!(ctx, __le__),
Self::Eq => identifier!(ctx, __eq__),
Self::Ne => identifier!(ctx, __ne__),
Self::Ge => identifier!(ctx, __ge__),
Self::Gt => identifier!(ctx, __gt__),
}
}
pub const fn operator_token(self) -> &'static str {
match self {
Self::Lt => "<",
Self::Le => "<=",
Self::Eq => "==",
Self::Ne => "!=",
Self::Ge => ">=",
Self::Gt => ">",
}
}
#[inline]
pub fn identical_optimization(
self,
a: &impl Borrow<PyObject>,
b: &impl Borrow<PyObject>,
) -> Option<bool> {
self.map_eq(|| a.borrow().is(b.borrow()))
}
#[inline]
pub fn map_eq(self, f: impl FnOnce() -> bool) -> Option<bool> {
let eq = match self {
Self::Eq => true,
Self::Ne => false,
_ => return None,
};
f().then_some(eq)
}
}
#[pyclass]
pub trait GetAttr: PyPayload {
#[pyslot]
fn slot_getattro(obj: &PyObject, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
let zelf = obj
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for __getattribute__"))?;
Self::getattro(zelf, name, vm)
}
fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult;
}
#[pyclass]
pub trait SetAttr: PyPayload {
#[pyslot]
#[inline]
fn slot_setattro(
obj: &PyObject,
name: &Py<PyStr>,
value: PySetterValue,
vm: &VirtualMachine,
) -> PyResult<()> {
let zelf = obj
.downcast_ref::<Self>()
.ok_or_else(|| vm.new_type_error("unexpected payload for __setattr__"))?;
Self::setattro(zelf, name, value, vm)
}
fn setattro(
zelf: &Py<Self>,
name: &Py<PyStr>,
value: PySetterValue,
vm: &VirtualMachine,
) -> PyResult<()>;
}
#[pyclass]
pub trait AsBuffer: PyPayload {
#[inline]
#[pyslot]
fn slot_as_buffer(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyBuffer> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for as_buffer"))?;
Self::as_buffer(zelf, vm)
}
fn as_buffer(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyBuffer>;
}
#[pyclass]
pub trait AsMapping: PyPayload {
fn as_mapping() -> &'static PyMappingMethods;
#[inline]
fn mapping_downcast(mapping: PyMapping<'_>) -> &Py<Self> {
unsafe { mapping.obj.downcast_unchecked_ref() }
}
fn extend_slots(slots: &mut PyTypeSlots) {
slots.as_mapping.copy_from(Self::as_mapping());
}
}
#[pyclass]
pub trait AsSequence: PyPayload {
fn as_sequence() -> &'static PySequenceMethods;
#[inline]
fn sequence_downcast(seq: PySequence<'_>) -> &Py<Self> {
unsafe { seq.obj.downcast_unchecked_ref() }
}
fn extend_slots(slots: &mut PyTypeSlots) {
slots.as_sequence.copy_from(Self::as_sequence());
}
}
#[pyclass]
pub trait AsNumber: PyPayload {
#[pyslot]
fn as_number() -> &'static PyNumberMethods;
fn extend_slots(slots: &mut PyTypeSlots) {
slots.as_number.copy_from(Self::as_number());
}
fn clone_exact(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
unimplemented!()
}
#[inline]
fn number_downcast(num: PyNumber<'_>) -> &Py<Self> {
unsafe { num.obj.downcast_unchecked_ref() }
}
#[inline]
fn number_downcast_exact(num: PyNumber<'_>, vm: &VirtualMachine) -> PyRef<Self> {
if let Some(zelf) = num.downcast_ref_if_exact::<Self>(vm) {
zelf.to_owned()
} else {
Self::clone_exact(Self::number_downcast(num), vm)
}
}
}
#[pyclass]
pub trait Iterable: PyPayload {
#[pyslot]
fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let zelf = zelf
.downcast()
.map_err(|_| vm.new_type_error("unexpected payload for __iter__"))?;
Self::iter(zelf, vm)
}
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult;
fn extend_slots(_slots: &mut PyTypeSlots) {}
}
#[pyclass(with(Iterable))]
pub trait IterNext: PyPayload + Iterable {
#[pyslot]
fn slot_iternext(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for __next__"))?;
Self::next(zelf, vm)
}
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn>;
}
pub trait SelfIter: PyPayload {}
impl<T> Iterable for T
where
T: SelfIter,
{
#[cold]
fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let repr = zelf.repr(vm)?;
unreachable!("slot must be overridden for {}", repr.as_wtf8());
}
#[cold]
fn iter(_zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult {
unreachable!("slot_iter is implemented");
}
fn extend_slots(slots: &mut PyTypeSlots) {
let prev = slots.iter.swap(Some(self_iter));
debug_assert!(prev.is_some()); }
}