diff --git a/src/conversion.rs b/src/conversion.rs index 99276714..269a06d4 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -252,8 +252,12 @@ pub mod extract_impl { fn extract(source: &'a PyAny) -> PyResult; } - pub struct Cloned; - pub struct Reference; + pub struct Cloned { + _private: (), + } + pub struct Reference { + _private: (), + } impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned where diff --git a/src/instance.rs b/src/instance.rs index 5df15e8e..98d592a0 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,7 +3,7 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::{PyObjectLayout, PyTypeInfo}; +use crate::type_object::PyTypeInfo; use crate::types::PyAny; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer, @@ -121,13 +121,13 @@ impl Py { pub trait AsPyRef: Sized { /// Return reference to object. - fn as_ref(&self, py: Python) -> &T; + fn as_ref(&self, py: Python) -> &T::Reference; } impl AsPyRef for Py { - fn as_ref(&self, _py: Python) -> &T { - let any = self as *const Py as *const PyAny; - unimplemented!() + fn as_ref(&self, _py: Python) -> &T::Reference { + let ptr = self as *const Py as *const T::Reference; + unsafe { &*ptr } } } diff --git a/src/object.rs b/src/object.rs index 44193fa0..bf02992e 100644 --- a/src/object.rs +++ b/src/object.rs @@ -158,7 +158,7 @@ impl PyObject { /// Extracts some type from the Python object. /// This is a wrapper function around `FromPyObject::extract()`. - pub fn extract<'p, D>(&'p self, py: Python) -> PyResult + pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult where D: FromPyObject<'p>, { diff --git a/src/pycell.rs b/src/pycell.rs index 157dc6ef..b40fbe93 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -13,6 +13,9 @@ use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; /// Inner type of `PyCell` without dict slots and reference counter. +/// This struct has two usages: +/// 1. As an inner type of `PyRef` and `PyRefMut`. +/// 2. As a base class when `#[pyclass(Base)]` is specified. #[doc(hidden)] #[repr(C)] pub struct PyCellBase { @@ -20,20 +23,23 @@ pub struct PyCellBase { value: ManuallyDrop>, } -impl PyCellBase { - fn get(&self) -> &T { - unsafe { &*self.value.get() } - } - fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } +impl AsPyPointer for PyCellBase { + fn as_ptr(&self) -> *mut ffi::PyObject { + (self as *const _) as *mut _ } } -impl PyObjectLayout for PyCellBase { +unsafe impl PyObjectLayout for PyCellBase { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { Some(&mut self.ob_base) } + unsafe fn unchecked_ref(&self) -> &T { + &*self.value.get() + } + unsafe fn unchecked_refmut(&mut self) -> &mut T { + &mut *self.value.get() + } unsafe fn py_init(&mut self, value: T) { self.value = ManuallyDrop::new(UnsafeCell::new(value)); } @@ -43,7 +49,7 @@ impl PyObjectLayout for PyCellBase { } } -/// `Pycell` represents the concrete layout of `T: PyClass` when it is converted +/// `PyCell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// /// You can use it to test your `#[pyclass]` correctly works. @@ -68,9 +74,8 @@ impl PyObjectLayout for PyCellBase { /// ``` #[repr(C)] pub struct PyCell { - ob_base: ::ConcreteLayout, - value: ManuallyDrop>, - borrow_flag: BorrowFlag, + base: PyCellBase, + borrow_flag: Cell, dict: T::Dict, weakref: T::WeakRef, } @@ -90,39 +95,52 @@ impl PyCell { } pub fn borrow(&self) -> PyRef<'_, T> { - unsafe { - unimplemented!() - // if self.borrow.get() == usize::max_value() { - // borrow_fail(); - // } - // self.borrow.set(self.borrow.get() + 1); - // Ref { - // value: &*self.value.get(), - // borrow: &self.borrow, - // } - } + self.try_borrow().expect("Already mutably borrowed") } pub fn borrow_mut(&self) -> PyRefMut<'_, T> { - unsafe { - unimplemented!() - // if self.borrow.get() != 0 { - // borrow_fail(); - // } - // self.borrow.set(usize::max_value()); - // RefMut { - // value: &mut *self.value.get(), - // borrow: &self.borrow, - // } + self.try_borrow_mut().expect("Already borrowed") + } + + pub fn try_borrow(&self) -> Result, PyBorrowError> { + let flag = self.borrow_flag.get(); + if flag != BorrowFlag::HAS_MUTABLE_BORROW { + Err(PyBorrowError { _private: () }) + } else { + self.borrow_flag.set(flag.increment()); + Ok(PyRef { + value: &self.base, + flag: &self.borrow_flag, + }) + } + } + + pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> { + if self.borrow_flag.get() != BorrowFlag::UNUSED { + Err(PyBorrowMutError { _private: () }) + } else { + self.borrow_flag.set(BorrowFlag::HAS_MUTABLE_BORROW); + Ok(PyRefMut { + value: unsafe { &mut *(self.base.as_ptr() as *mut _) }, + flag: &self.borrow_flag, + }) } } pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - unimplemented!() + if self.borrow_flag.get() != BorrowFlag::HAS_MUTABLE_BORROW { + Err(PyBorrowError { _private: () }) + } else { + Ok(&*self.base.value.get()) + } } pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { - unimplemented!() + if self.borrow_flag.get() != BorrowFlag::UNUSED { + Err(PyBorrowMutError { _private: () }) + } else { + Ok(&mut *self.base.value.get()) + } } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> @@ -135,26 +153,32 @@ impl PyCell { return Err(PyErr::fetch(py)); } let self_ = base as *mut Self; - (*self_).borrow_flag = BorrowFlag::UNUSED; + (*self_).borrow_flag = Cell::new(BorrowFlag::UNUSED); (*self_).dict = T::Dict::new(); (*self_).weakref = T::WeakRef::new(); Ok(self_) } } -impl PyObjectLayout for PyCell { +unsafe impl PyObjectLayout for PyCell { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { - Some(&mut self.ob_base) + Some(&mut self.base.ob_base) + } + unsafe fn unchecked_ref(&self) -> &T { + self.base.unchecked_ref() + } + unsafe fn unchecked_refmut(&mut self) -> &mut T { + self.base.unchecked_refmut() } unsafe fn py_init(&mut self, value: T) { - self.value = ManuallyDrop::new(UnsafeCell::new(value)); + self.base.value = ManuallyDrop::new(UnsafeCell::new(value)); } unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.value); + ManuallyDrop::drop(&mut self.base.value); self.dict.clear_dict(py); self.weakref.clear_weakrefs(self.as_ptr(), py); - self.ob_base.py_drop(py); + self.base.ob_base.py_drop(py); } } @@ -206,8 +230,8 @@ pub struct PyRef<'p, T: PyClass> { } impl<'p, T: PyClass> PyRef<'p, T> { - fn get_super(&'p self) -> &'p T::BaseType { - unimplemented!() + pub fn get_super(&'p self) -> &'p T::BaseType { + unsafe { self.value.ob_base.unchecked_ref() } } } @@ -216,13 +240,13 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { #[inline] fn deref(&self) -> &T { - self.value.get() + unsafe { self.value.unchecked_ref() } } } impl<'p, T: PyClass> Drop for PyRef<'p, T> { fn drop(&mut self) { - self.flag.set(self.flag.get()); + self.flag.set(self.flag.get().decrement()); } } @@ -231,19 +255,28 @@ pub struct PyRefMut<'p, T: PyClass> { flag: &'p Cell, } +impl<'p, T: PyClass> PyRefMut<'p, T> { + pub fn get_super(&'p self) -> &'p T::BaseType { + unsafe { self.value.ob_base.unchecked_ref() } + } + pub fn get_super_mut(&'p mut self) -> &'p mut T::BaseType { + unsafe { self.value.ob_base.unchecked_refmut() } + } +} + impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { type Target = T; #[inline] fn deref(&self) -> &T { - self.value.get() + unsafe { self.value.unchecked_ref() } } } impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - self.value.get_mut() + unsafe { self.value.unchecked_refmut() } } } @@ -259,7 +292,10 @@ struct BorrowFlag(usize); impl BorrowFlag { const UNUSED: BorrowFlag = BorrowFlag(0); const HAS_MUTABLE_BORROW: BorrowFlag = BorrowFlag(usize::max_value()); - fn decrement(self) -> Self { + const fn increment(self) -> Self { + Self(self.0 + 1) + } + const fn decrement(self) -> Self { Self(self.0 - 1) } } diff --git a/src/type_object.rs b/src/type_object.rs index 68a2da19..eb9e5299 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -15,15 +15,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// is of `PyAny`. /// /// This trait is intended to be used internally. -pub trait PyObjectLayout { +pub unsafe trait PyObjectLayout { const IS_NATIVE_TYPE: bool = true; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { None } - unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} + unsafe fn unchecked_ref(&self) -> &T; + unsafe fn unchecked_refmut(&mut self) -> &mut T; } /// `T: PyObjectSizedLayout` represents `T` is not a instance of @@ -101,6 +102,9 @@ pub unsafe trait PyTypeInfo: Sized { /// Layout type ConcreteLayout: PyObjectLayout; + /// This type is an abstraction layer for `AsPyRef`. + type Reference; + /// Initializer for layout type Initializer: PyObjectInit; diff --git a/src/types/any.rs b/src/types/any.rs index f0d17649..a234e41b 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -23,7 +23,14 @@ use crate::{ffi, PyObject}; /// ``` #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); -impl crate::type_object::PyObjectLayout for ffi::PyObject {} +unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { + unsafe fn unchecked_ref(&self) -> &PyAny { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&mut self) -> &mut PyAny { + &mut *((&self) as *const &mut Self as *const _ as *mut _) + } +} impl crate::type_object::PyObjectSizedLayout for ffi::PyObject {} pyobject_native_type_named!(PyAny); pyobject_native_type_convert!( diff --git a/src/types/mod.rs b/src/types/mod.rs index cde4a860..79880313 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -56,10 +56,23 @@ macro_rules! pyobject_native_type_named ( }; ); +macro_rules! impl_layout { + ($name: ty, $layout: path) => { + unsafe impl $crate::type_object::PyObjectLayout<$name> for $layout { + unsafe fn unchecked_ref(&self) -> &$name { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&mut self) -> &mut $name { + &mut *((&self) as *const &mut Self as *const _ as *mut _) + } + } + }; +} + #[macro_export] macro_rules! pyobject_native_type { ($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => { - impl $crate::type_object::PyObjectLayout<$name> for $layout {} + impl_layout!($name, $layout); impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {} pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*); @@ -81,7 +94,7 @@ macro_rules! pyobject_native_type { #[macro_export] macro_rules! pyobject_native_var_type { ($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => { - impl $crate::type_object::PyObjectLayout<$name> for $crate::ffi::PyObject {} + impl_layout!($name, $crate::ffi::PyObject); pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $crate::ffi::PyObject, $typeobject, $module, $checkfunction $(,$type_param)*); @@ -118,6 +131,7 @@ macro_rules! pyobject_native_type_convert( type Type = (); type BaseType = $crate::types::PyAny; type ConcreteLayout = $layout; + type Reference = $name; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; const NAME: &'static str = stringify!($name);