forked from Rust-related/RustPython
471 lines
13 KiB
Rust
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())
|
|
}
|
|
}
|