Files
RustPython/vm/src/slots.rs

471 lines
13 KiB
Rust

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<PyObjectRef>, Option<PyObjectRef>, &VirtualMachine) -> PyResult;
pub(crate) type HashFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyHash>;
pub(crate) type CmpFunc = fn(
&PyObjectRef,
&PyObjectRef,
PyComparisonOp,
&VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>>;
pub(crate) type GetattroFunc = fn(PyObjectRef, PyStrRef, &VirtualMachine) -> PyResult;
pub(crate) type BufferFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<Box<dyn Buffer>>;
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<Option<String>>, // tp_name, not class name
pub new: Option<PyNativeFunc>,
pub del: AtomicCell<Option<DelFunc>>,
pub call: AtomicCell<Option<GenericMethod>>,
pub descr_get: AtomicCell<Option<DescrGetFunc>>,
pub hash: AtomicCell<Option<HashFunc>>,
pub cmp: AtomicCell<Option<CmpFunc>>,
pub getattro: AtomicCell<Option<GetattroFunc>>,
pub buffer: Option<BufferFunc>,
pub iter: AtomicCell<Option<IterFunc>>,
pub iternext: AtomicCell<Option<IterNextFunc>>,
}
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<Self>, vm: &VirtualMachine) -> PyResult<()> {
Self::del(&zelf, vm)
}
fn del(zelf: &PyRef<Self>, 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<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Self::call(&zelf, args, vm)
}
fn call(zelf: &PyRef<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult;
}
#[pyimpl]
pub trait SlotDescriptor: PyValue {
#[pyslot]
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult;
#[pymethod(magic)]
fn get(
zelf: PyObjectRef,
obj: PyObjectRef,
cls: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
Self::descr_get(zelf, Some(obj), cls.into_option(), vm)
}
fn _zelf(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
PyRef::<Self>::try_from_object(vm, zelf)
}
fn _unwrap(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<(PyRef<Self>, PyObjectRef)> {
let zelf = Self::_zelf(zelf, vm)?;
let obj = vm.unwrap_or_none(obj);
Ok((zelf, obj))
}
fn _check(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> Result<(PyRef<Self>, 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<T>(cls: &Option<PyObjectRef>, 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<PyHash> {
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<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
Self::hash(&zelf, vm)
}
fn hash(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyHash>;
}
pub trait Unhashable: PyValue {}
impl<T> Hashable for T
where
T: Unhashable,
{
fn hash(_zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
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<Either<PyObjectRef, PyComparisonValue>> {
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<Self>,
other: &PyObjectRef,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue>;
#[pymethod(magic)]
fn eq(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::cmp(&zelf, &other, PyComparisonOp::Eq, vm)
}
#[pymethod(magic)]
fn ne(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::cmp(&zelf, &other, PyComparisonOp::Ne, vm)
}
#[pymethod(magic)]
fn lt(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::cmp(&zelf, &other, PyComparisonOp::Lt, vm)
}
#[pymethod(magic)]
fn le(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::cmp(&zelf, &other, PyComparisonOp::Le, vm)
}
#[pymethod(magic)]
fn ge(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::cmp(&zelf, &other, PyComparisonOp::Ge, vm)
}
#[pymethod(magic)]
fn gt(
zelf: PyRef<Self>,
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
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<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 {
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<bool> {
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<bool> {
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>() {
Self::getattro(zelf, name, vm)
} else {
Err(vm.new_type_error("unexpected payload for __getattribute__".to_owned()))
}
}
// TODO: make zelf: &PyRef<Self>
fn getattro(zelf: PyRef<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult;
#[pymethod]
fn __getattribute__(zelf: PyRef<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
Self::getattro(zelf, name, vm)
}
}
#[pyimpl]
pub trait BufferProtocol: PyValue {
#[pyslot]
fn tp_buffer(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Box<dyn Buffer>> {
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<Self>, vm: &VirtualMachine) -> PyResult<Box<dyn Buffer>>;
}
#[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<Self>, 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<Self>, vm: &VirtualMachine) -> PyResult;
#[pymethod]
fn __next__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Self::next(&zelf, vm)
}
}
impl<T> Iterable for T
where
T: PyIter,
{
fn tp_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult {
Ok(zelf)
}
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult {
Ok(zelf.into_object())
}
}