mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Merge pull request #2336 from RustPython/coolreader18/pyobjectref-custom-vtable
Manually implement a vtable for PyObjectRef
This commit is contained in:
18 files changed
+577
-697
No files matched your search
@@ -161,6 +161,21 @@ jobs:
|
||||
run: python -m pip install flake8
|
||||
- name: run lint
|
||||
run: flake8 . --count --exclude=./.*,./Lib,./vm/Lib --select=E9,F63,F7,F82 --show-source --statistics
|
||||
miri:
|
||||
name: Run tests under miri
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
components: miri
|
||||
override: true
|
||||
- name: Run tests under miri
|
||||
# miri-ignore-leaks because the type-object circular reference means that there will always be
|
||||
# a memory leak, at least until we have proper cyclic gc
|
||||
run: MIRIFLAGS='-Zmiri-ignore-leaks' cargo +nightly miri test -p rustpython-vm -- miri_test
|
||||
|
||||
wasm:
|
||||
name: Check the WASM package and demo
|
||||
|
||||
Generated
+1
@@ -1894,6 +1894,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"sha3",
|
||||
"socket2",
|
||||
"static_assertions",
|
||||
"statrs",
|
||||
"thiserror",
|
||||
"thread_local",
|
||||
|
||||
@@ -79,6 +79,7 @@ cfg-if = "0.1.10"
|
||||
timsort = "0.1"
|
||||
thiserror = "1.0"
|
||||
atty = "0.2"
|
||||
static_assertions = "1.1"
|
||||
|
||||
## unicode stuff
|
||||
unicode_names2 = "0.4"
|
||||
|
||||
@@ -198,7 +198,7 @@ impl PyCode {}
|
||||
#[pyimpl]
|
||||
impl PyCodeRef {
|
||||
#[pyslot]
|
||||
fn tp_new(_cls: PyTypeRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
fn tp_new(_cls: PyTypeRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
Err(vm.new_type_error("Cannot directly create code object".to_owned()))
|
||||
}
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ macro_rules! dict_iterator {
|
||||
let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut str_parts = vec![];
|
||||
for (key, value) in zelf.dict.clone() {
|
||||
let s = vm.to_repr(&$result_fn(vm, key, value))?;
|
||||
let s = vm.to_repr(&($result_fn)(vm, key, value))?;
|
||||
str_parts.push(s.borrow_value().to_owned());
|
||||
}
|
||||
format!("{}([{}])", $class_name, str_parts.join(", "))
|
||||
@@ -694,7 +694,7 @@ macro_rules! dict_iterator {
|
||||
match zelf.dict.entries.next_entry(&mut position) {
|
||||
Some((key, value)) => {
|
||||
zelf.position.store(position);
|
||||
Ok($result_fn(vm, key, value))
|
||||
Ok(($result_fn)(vm, key, value))
|
||||
}
|
||||
None => Err(vm.new_stop_iteration()),
|
||||
}
|
||||
@@ -743,7 +743,7 @@ macro_rules! dict_iterator {
|
||||
match zelf.dict.len().checked_sub(count) {
|
||||
Some(mut pos) => {
|
||||
let (key, value) = zelf.dict.entries.next_entry(&mut pos).unwrap();
|
||||
Ok($result_fn(vm, key, value))
|
||||
Ok(($result_fn)(vm, key, value))
|
||||
}
|
||||
None => {
|
||||
zelf.position.store(std::isize::MAX as usize);
|
||||
|
||||
@@ -217,10 +217,10 @@ impl PyBaseObject {
|
||||
#[pyproperty(name = "__class__", setter)]
|
||||
fn set_class(instance: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if instance.payload_is::<PyBaseObject>() {
|
||||
match value.downcast_generic::<PyType>() {
|
||||
match value.downcast::<PyType>() {
|
||||
Ok(cls) => {
|
||||
// FIXME(#1979) cls instances might have a payload
|
||||
*instance.typ.write() = cls;
|
||||
*instance.class_lock().write() = cls;
|
||||
Ok(())
|
||||
}
|
||||
Err(value) => {
|
||||
|
||||
@@ -240,7 +240,10 @@ impl PyStr {
|
||||
if other.isinstance(&vm.ctx.types.str_type) {
|
||||
Ok(self.value.py_add(borrow_value(&other)))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot add {} and {}", self, other)))
|
||||
Err(vm.new_type_error(format!(
|
||||
"can only concatenate str (not \"{}\") to str",
|
||||
other.class().name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-18
@@ -727,24 +727,22 @@ pub fn tp_new_wrapper(
|
||||
call_tp_new(zelf, cls, args, vm)
|
||||
}
|
||||
|
||||
fn take_next_base(mut bases: Vec<Vec<PyTypeRef>>) -> (Option<PyTypeRef>, Vec<Vec<PyTypeRef>>) {
|
||||
bases = bases.into_iter().filter(|x| !x.is_empty()).collect();
|
||||
|
||||
for base in &bases {
|
||||
fn take_next_base(bases: &mut Vec<Vec<PyTypeRef>>) -> Option<PyTypeRef> {
|
||||
for base in bases.iter() {
|
||||
let head = base[0].clone();
|
||||
if !(&bases).iter().any(|x| x[1..].iter().any(|x| x.is(&head))) {
|
||||
if !bases.iter().any(|x| x[1..].iter().any(|x| x.is(&head))) {
|
||||
// Remove from other heads.
|
||||
for item in &mut bases {
|
||||
for item in bases.iter_mut() {
|
||||
if item[0].is(&head) {
|
||||
item.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
return (Some(head), bases);
|
||||
return Some(head);
|
||||
}
|
||||
}
|
||||
|
||||
(None, bases)
|
||||
None
|
||||
}
|
||||
|
||||
fn linearise_mro(mut bases: Vec<Vec<PyTypeRef>>) -> Result<Vec<PyTypeRef>, String> {
|
||||
@@ -768,22 +766,19 @@ fn linearise_mro(mut bases: Vec<Vec<PyTypeRef>>) -> Result<Vec<PyTypeRef>, Strin
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
loop {
|
||||
if (&bases).iter().all(Vec::is_empty) {
|
||||
break;
|
||||
}
|
||||
let (head, new_bases) = take_next_base(bases);
|
||||
let head = head.ok_or_else(|| {
|
||||
while !bases.is_empty() {
|
||||
let head = take_next_base(&mut bases).ok_or_else(|| {
|
||||
// Take the head class of each class here. Now that we have reached the problematic bases.
|
||||
// Because this failed, we assume the lists cannot be empty.
|
||||
format!(
|
||||
"Cannot create a consistent method resolution order (MRO) for bases {}",
|
||||
new_bases.iter().map(|x| x.first().unwrap()).join(", ")
|
||||
bases.iter().map(|x| x.first().unwrap()).format(", ")
|
||||
)
|
||||
});
|
||||
})?;
|
||||
|
||||
result.push(head.unwrap());
|
||||
bases = new_bases;
|
||||
result.push(head);
|
||||
|
||||
bases.retain(|x| !x.is_empty());
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ use super::pytype::PyTypeRef;
|
||||
use crate::common::hash::PyHash;
|
||||
use crate::function::{FuncArgs, OptionalArg};
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyObjectWeak, PyRef, PyResult, PyValue,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::pyobjectrc::{PyObjectRc, PyObjectWeak};
|
||||
use crate::slots::{Callable, Comparable, Hashable, PyComparisonOp};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct PyWeak {
|
||||
impl PyWeak {
|
||||
pub fn downgrade(obj: &PyObjectRef) -> PyWeak {
|
||||
PyWeak {
|
||||
referent: PyObjectRc::downgrade(obj),
|
||||
referent: PyObjectRef::downgrade(obj),
|
||||
hash: AtomicCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::byteslike::try_bytes_like;
|
||||
use crate::cformat::CFormatBytes;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, Either, IdProtocol, PyComparisonValue, PyIterable, PyObjectRef, PyRef, PyResult,
|
||||
PyValue, TryFromObject, TypeProtocol,
|
||||
BorrowValue, Either, IdProtocol, PyComparisonValue, PyIterable, PyObjectRef, PyResult, PyValue,
|
||||
TryFromObject, TypeProtocol,
|
||||
};
|
||||
use crate::sliceable::PySliceableSequence;
|
||||
use crate::slots::PyComparisonOp;
|
||||
@@ -91,10 +91,10 @@ impl ByteInnerNewOptions {
|
||||
|
||||
pub fn get_bytes(mut self, cls: PyTypeRef, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
|
||||
let inner = if let OptionalArg::Present(source) = self.source.take() {
|
||||
if source.class().is(&PyBytes::class(vm)) && cls.is(&PyBytes::class(vm)) {
|
||||
return self
|
||||
.check_args(vm)
|
||||
.map(|_| unsafe { PyRef::from_obj_unchecked(source) });
|
||||
if cls.is(&PyBytes::class(vm)) {
|
||||
if let Ok(source) = source.clone().downcast_exact(vm) {
|
||||
return self.check_args(vm).map(|()| source);
|
||||
}
|
||||
}
|
||||
|
||||
match_class!(match source {
|
||||
|
||||
+41
-321
@@ -37,7 +37,7 @@ use crate::dictdatatype::Dict;
|
||||
use crate::exceptions::{self, PyBaseExceptionRef};
|
||||
use crate::function::{IntoFuncArgs, IntoPyNativeFunc};
|
||||
use crate::iterator;
|
||||
pub use crate::pyobjectrc::{PyObjectRc, PyObjectWeak};
|
||||
pub use crate::pyobjectrc::{PyObject, PyObjectRef, PyObjectWeak, PyRef, PyWeakRef};
|
||||
use crate::scope::Scope;
|
||||
use crate::slots::{PyTpFlags, PyTypeSlots};
|
||||
use crate::types::{create_type_with_slots, TypeZoo};
|
||||
@@ -57,13 +57,6 @@ Basically reference counting, but then done by rust.
|
||||
* Good reference: https://github.com/ProgVal/pythonvm-rust/blob/master/src/objects/mod.rs
|
||||
*/
|
||||
|
||||
/// The `PyObjectRef` is one of the most used types. It is a reference to a
|
||||
/// python object. A single python object can have multiple references, and
|
||||
/// this reference counting is accounted for by this type. Use the `.clone()`
|
||||
/// method to create a new reference and increment the amount of references
|
||||
/// to the python object by 1.
|
||||
pub type PyObjectRef = PyObjectRc<dyn PyObjectPayload>;
|
||||
|
||||
/// Use this type for functions which return a python object or an exception.
|
||||
/// Both the python object and the python exception are `PyObjectRef` types
|
||||
/// since exceptions are also python objects.
|
||||
@@ -74,7 +67,8 @@ pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; // A valid v
|
||||
/// TODO: class attributes should maintain insertion order (use IndexMap here)
|
||||
pub type PyAttributes = HashMap<String, PyObjectRef>;
|
||||
|
||||
impl fmt::Display for PyObject<dyn PyObjectPayload> {
|
||||
// TODO: remove this impl
|
||||
impl fmt::Display for PyObjectRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(PyType { ref name, .. }) = self.payload::<PyType>() {
|
||||
let type_name = self.class().name.clone();
|
||||
@@ -377,12 +371,7 @@ impl PyContext {
|
||||
}
|
||||
|
||||
pub fn new_base_object(&self, class: PyTypeRef, dict: Option<PyDictRef>) -> PyObjectRef {
|
||||
PyObject {
|
||||
typ: PyRwLock::new(class.into_typed_pyobj()),
|
||||
dict: dict.map(|d| PyRwLock::new(d.into_typed_pyobj())),
|
||||
payload: object::PyBaseObject,
|
||||
}
|
||||
.into_ref()
|
||||
PyObject::new(object::PyBaseObject, class, dict)
|
||||
}
|
||||
|
||||
pub fn add_tp_new_wrapper(&self, ty: &PyTypeRef) {
|
||||
@@ -405,159 +394,6 @@ impl Default for PyContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an actual python object. It consists of a `typ` which is the
|
||||
/// python class, and carries some rust payload optionally. This rust
|
||||
/// payload can be a rust float or rust int in case of float and int objects.
|
||||
pub struct PyObject<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
{
|
||||
pub(crate) typ: PyRwLock<PyObjectRc<PyType>>, // __class__ member
|
||||
pub(crate) dict: Option<PyRwLock<PyObjectRc<PyDict>>>, // __dict__ member
|
||||
pub payload: T,
|
||||
}
|
||||
|
||||
impl PyObjectRef {
|
||||
/// Attempt to downcast this reference to a subclass.
|
||||
///
|
||||
/// If the downcast fails, the original ref is returned in as `Err` so
|
||||
/// another downcast can be attempted without unnecessary cloning.
|
||||
pub fn downcast<T: PyObjectPayload + PyValue>(self) -> Result<PyRef<T>, Self> {
|
||||
if self.payload_is::<T>() {
|
||||
Ok(unsafe { PyRef::from_obj_unchecked(self) })
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to downcast this reference to the specific class that is associated `T`.
|
||||
///
|
||||
/// If the downcast fails, the original ref is returned in as `Err` so
|
||||
/// another downcast can be attempted without unnecessary cloning.
|
||||
pub fn downcast_exact<T: PyObjectPayload + PyValue>(
|
||||
self,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<PyRef<T>, Self> {
|
||||
if self.class().is(T::class(vm)) {
|
||||
// TODO: is this always true?
|
||||
assert!(
|
||||
self.payload_is::<T>(),
|
||||
"obj.__class__ is T::class() but payload is not T"
|
||||
);
|
||||
Ok(unsafe { PyRef::from_obj_unchecked(self) })
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Downcast this PyObjectRef to an `PyRc<PyObject<T>>`. The [`downcast`](#method.downcast) method
|
||||
/// is generally preferred, as the `PyRef<T>` it returns implements `Deref<Target=T>`, and
|
||||
/// therefore can be used similarly to an `&T`.
|
||||
pub fn downcast_generic<T: PyObjectPayload>(self) -> Result<PyObjectRc<T>, Self> {
|
||||
if self.payload_is::<T>() {
|
||||
let ptr = PyObjectRc::into_raw(self) as *const PyObject<T>;
|
||||
let ret = unsafe { PyObjectRc::from_raw(ptr) };
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast_ref<T: PyObjectPayload + PyValue>(&self) -> Option<&PyRef<T>> {
|
||||
if self.payload_is::<T>() {
|
||||
// when payload exacts, PyObjectRef == PyRef { PyObject }
|
||||
Some(unsafe { &*(self as *const PyObjectRef as *const PyRef<T>) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a Python object.
|
||||
///
|
||||
/// Note that a `PyRef<T>` can only deref to a shared / immutable reference.
|
||||
/// It is the payload type's responsibility to handle (possibly concurrent)
|
||||
/// mutability with locks or concurrent data structures if required.
|
||||
///
|
||||
/// A `PyRef<T>` can be directly returned from a built-in function to handle
|
||||
/// situations (such as when implementing in-place methods such as `__iadd__`)
|
||||
/// where a reference to the same object must be returned.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyRef<T> {
|
||||
// invariant: this obj must always have payload of type T
|
||||
obj: PyObjectRef,
|
||||
_payload: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for PyRef<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
obj: self.obj.clone(),
|
||||
_payload: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyValue> PyRef<T> {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new_ref(payload: T, typ: PyTypeRef, dict: Option<PyDictRef>) -> Self {
|
||||
let obj = PyObject::new(payload, typ, dict);
|
||||
// SAFETY: we just created the object from a payload of type T
|
||||
unsafe { Self::from_obj_unchecked(obj) }
|
||||
}
|
||||
|
||||
fn from_obj(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
if obj.payload_is::<T>() {
|
||||
// SAFETY: we just checked the payload
|
||||
Ok(unsafe { Self::from_obj_unchecked(obj) })
|
||||
} else {
|
||||
Err(vm.new_runtime_error(format!(
|
||||
"Unexpected payload '{}' for type '{}'",
|
||||
T::class(vm).name,
|
||||
obj.class().name,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: payload type of `obj` must be `T`
|
||||
pub(crate) unsafe fn from_obj_unchecked(obj: PyObjectRef) -> Self {
|
||||
PyRef {
|
||||
obj,
|
||||
_payload: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_object(&self) -> &PyObjectRef {
|
||||
&self.obj
|
||||
}
|
||||
|
||||
pub fn into_object(self) -> PyObjectRef {
|
||||
self.obj
|
||||
}
|
||||
|
||||
pub fn into_typed_pyobj(self) -> PyObjectRc<T> {
|
||||
self.into_object().downcast_generic().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for PyRef<T>
|
||||
where
|
||||
T: PyValue,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
self.obj.payload().unwrap_or_else(|| {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("PyRef.obj did not have a payload of T")
|
||||
}
|
||||
// SAFETY: self.obj has invariant that payload is `T`
|
||||
unsafe { std::hint::unreachable_unchecked() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFromObject for PyRef<T>
|
||||
where
|
||||
T: PyValue,
|
||||
@@ -565,7 +401,13 @@ where
|
||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||
let class = T::class(vm);
|
||||
if obj.isinstance(class) {
|
||||
PyRef::from_obj(obj, vm)
|
||||
obj.downcast().map_err(|obj| {
|
||||
vm.new_runtime_error(format!(
|
||||
"Unexpected payload '{}' for type '{}'",
|
||||
class.name,
|
||||
obj.class().name,
|
||||
))
|
||||
})
|
||||
} else {
|
||||
let expected_type = &class.name;
|
||||
let actual_type = &obj.class().name;
|
||||
@@ -591,14 +433,14 @@ impl<T: PyValue> From<PyRef<T>> for PyObjectRef {
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for PyRef<T>
|
||||
where
|
||||
T: PyValue + fmt::Display,
|
||||
T: PyObjectPayload + fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PyRefExact<T> {
|
||||
pub struct PyRefExact<T: PyObjectPayload> {
|
||||
obj: PyRef<T>,
|
||||
}
|
||||
impl<T: PyValue> TryFromObject for PyRefExact<T> {
|
||||
@@ -607,9 +449,14 @@ impl<T: PyValue> TryFromObject for PyRefExact<T> {
|
||||
let cls = obj.class();
|
||||
if cls.is(target_cls) {
|
||||
drop(cls);
|
||||
Ok(Self {
|
||||
obj: PyRef::from_obj(obj, vm)?,
|
||||
})
|
||||
let obj = obj.downcast().map_err(|obj| {
|
||||
vm.new_runtime_error(format!(
|
||||
"Unexpected payload '{}' for type '{}'",
|
||||
target_cls.name,
|
||||
obj.class().name,
|
||||
))
|
||||
})?;
|
||||
Ok(Self { obj })
|
||||
} else if cls.issubclass(target_cls) {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Expected an exact instance of '{}', not a subclass '{}'",
|
||||
@@ -685,21 +532,15 @@ pub trait IdProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PyObjectPayload> IdProtocol for PyObject<T> {
|
||||
impl<T: ?Sized> IdProtocol for PyRc<T> {
|
||||
fn get_id(&self) -> usize {
|
||||
self as *const _ as *const PyObject<Never> as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + IdProtocol> IdProtocol for PyRc<T> {
|
||||
fn get_id(&self) -> usize {
|
||||
(**self).get_id()
|
||||
&**self as *const T as *const Never as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyObjectPayload> IdProtocol for PyRef<T> {
|
||||
fn get_id(&self) -> usize {
|
||||
self.obj.get_id()
|
||||
self.as_object().get_id()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,14 +559,14 @@ impl<T: IdProtocol> IdProtocol for &'_ T {
|
||||
/// A borrow of a reference to a Python object. This avoids having clone the `PyRef<T>`/
|
||||
/// `PyObjectRef`, which isn't that cheap as that increments the atomic reference counter.
|
||||
pub struct PyLease<'a, T: PyObjectPayload> {
|
||||
inner: PyRwLockReadGuard<'a, PyObjectRc<T>>,
|
||||
inner: PyRwLockReadGuard<'a, PyRef<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T: PyObjectPayload + PyValue> PyLease<'a, T> {
|
||||
// Associated function on purpose, because of deref
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn into_pyref(zelf: Self) -> PyRef<T> {
|
||||
zelf.inner.clone().into_pyref()
|
||||
zelf.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -733,7 +574,7 @@ impl<'a, T: PyObjectPayload + PyValue> Deref for PyLease<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner.payload
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +583,7 @@ where
|
||||
T: PyValue + fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.inner.payload, f)
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,25 +611,16 @@ pub trait TypeProtocol {
|
||||
}
|
||||
|
||||
impl TypeProtocol for PyObjectRef {
|
||||
fn class(&self) -> PyLease<'_, PyType> {
|
||||
(**self).class()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TypeProtocol for PyObject<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
{
|
||||
fn class(&self) -> PyLease<'_, PyType> {
|
||||
PyLease {
|
||||
inner: self.typ.read(),
|
||||
inner: self.class_lock().read(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TypeProtocol for PyRef<T> {
|
||||
impl<T: PyObjectPayload> TypeProtocol for PyRef<T> {
|
||||
fn class(&self) -> PyLease<'_, PyType> {
|
||||
self.obj.class()
|
||||
self.as_object().class()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,12 +658,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PyObject<dyn PyObjectPayload> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "[PyObj {:?}]", &self.payload)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterable Python object.
|
||||
///
|
||||
/// `PyIterable` implements `FromArgs` so that a built-in function can accept
|
||||
@@ -931,11 +757,11 @@ impl<T: TryFromObject> TryFromObject for Option<T> {
|
||||
|
||||
/// Allows coercion of a types into PyRefs, so that we can write functions that can take
|
||||
/// refs, pyobject refs or basic types.
|
||||
pub trait TryIntoRef<T> {
|
||||
pub trait TryIntoRef<T: PyObjectPayload> {
|
||||
fn try_into_ref(self, vm: &VirtualMachine) -> PyResult<PyRef<T>>;
|
||||
}
|
||||
|
||||
impl<T> TryIntoRef<T> for PyRef<T> {
|
||||
impl<T: PyObjectPayload> TryIntoRef<T> for PyRef<T> {
|
||||
fn try_into_ref(self, _vm: &VirtualMachine) -> PyResult<PyRef<T>> {
|
||||
Ok(self)
|
||||
}
|
||||
@@ -959,7 +785,7 @@ pub trait TryFromObject: Sized {
|
||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
|
||||
}
|
||||
|
||||
pub trait IntoPyRef<T> {
|
||||
pub trait IntoPyRef<T: PyObjectPayload> {
|
||||
fn into_pyref(self, vm: &VirtualMachine) -> PyRef<T>;
|
||||
}
|
||||
|
||||
@@ -981,9 +807,9 @@ pub trait IntoPyObject {
|
||||
fn into_pyobject(self, vm: &VirtualMachine) -> PyObjectRef;
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for PyRef<T> {
|
||||
impl<T: PyObjectPayload> IntoPyObject for PyRef<T> {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.obj
|
||||
self.into_object()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,105 +858,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyObject<T>
|
||||
where
|
||||
T: Sized + PyObjectPayload,
|
||||
{
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(payload: T, typ: PyTypeRef, dict: Option<PyDictRef>) -> PyObjectRef {
|
||||
PyObject {
|
||||
typ: PyRwLock::new(typ.into_typed_pyobj()),
|
||||
dict: dict.map(|d| PyRwLock::new(d.into_typed_pyobj())),
|
||||
payload,
|
||||
}
|
||||
.into_ref()
|
||||
}
|
||||
|
||||
// Move this object into a reference object, transferring ownership.
|
||||
pub fn into_ref(self) -> PyObjectRef {
|
||||
let raw = PyObjectRc::into_raw(PyObjectRc::new(self));
|
||||
unsafe { PyObjectRef::from_raw(raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyObject<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
{
|
||||
pub fn raw_payload(&self) -> &T {
|
||||
&self.payload
|
||||
}
|
||||
|
||||
pub fn dict(&self) -> Option<PyDictRef> {
|
||||
self.dict.as_ref().map(|mu| mu.read().clone().into_pyref())
|
||||
}
|
||||
/// Set the dict field. Returns `Err(dict)` if this object does not have a dict field
|
||||
/// in the first place.
|
||||
pub fn set_dict(&self, dict: PyDictRef) -> Result<(), PyDictRef> {
|
||||
match self.dict {
|
||||
Some(ref mu) => {
|
||||
*mu.write() = dict.into_typed_pyobj();
|
||||
Ok(())
|
||||
}
|
||||
None => Err(dict),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyObject<dyn PyObjectPayload> {
|
||||
#[inline]
|
||||
pub fn payload<T: PyObjectPayload>(&self) -> Option<&T> {
|
||||
self.payload.as_any().downcast_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_is<T: PyObjectPayload>(&self) -> bool {
|
||||
self.payload.as_any().is::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_if_exact<T: PyObjectPayload + PyValue>(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
) -> Option<&T> {
|
||||
if self.class().is(T::class(vm)) {
|
||||
self.payload()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_if_subclass<T: PyObjectPayload + PyValue>(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
) -> Option<&T> {
|
||||
if self.class().issubclass(T::class(vm)) {
|
||||
self.payload()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyObjectRc<T>
|
||||
where
|
||||
T: Sized + PyObjectPayload,
|
||||
{
|
||||
pub fn into_pyref(self) -> PyRef<T>
|
||||
where
|
||||
T: PyValue,
|
||||
{
|
||||
// SAFETY: we know just casted from PyRc<PyObject<T>> to PyObjectRef, so we know the
|
||||
// payload is `T`
|
||||
let raw = PyObjectRc::into_raw(self);
|
||||
unsafe {
|
||||
let rc = PyObjectRc::<dyn PyObjectPayload>::from_raw(raw);
|
||||
PyRef::from_obj_unchecked(rc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "threading")] {
|
||||
pub trait PyThreadingConstraint: Send + Sync {}
|
||||
@@ -1157,8 +884,8 @@ pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static {
|
||||
if cls.issubclass(class) {
|
||||
Ok(self.into_ref_with_type_unchecked(cls, vm))
|
||||
} else {
|
||||
let subtype = vm.to_str(&cls.obj)?;
|
||||
let basetype = vm.to_str(&class.obj)?;
|
||||
let subtype = vm.to_str(cls.as_object())?;
|
||||
let basetype = vm.to_str(cls.as_object())?;
|
||||
Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype)))
|
||||
}
|
||||
}
|
||||
@@ -1173,16 +900,9 @@ pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PyObjectPayload: Any + fmt::Debug + PyThreadingConstraint + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
pub trait PyObjectPayload: Any + fmt::Debug + PyThreadingConstraint + 'static {}
|
||||
|
||||
impl<T: PyValue + 'static> PyObjectPayload for T {
|
||||
#[inline]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<T: PyValue + 'static> PyObjectPayload for T {}
|
||||
|
||||
pub enum Either<A, B> {
|
||||
A(A),
|
||||
@@ -1300,7 +1020,7 @@ pub trait StaticType {
|
||||
|
||||
impl<T> PyClassDef for PyRef<T>
|
||||
where
|
||||
T: PyClassDef,
|
||||
T: PyObjectPayload + PyClassDef,
|
||||
{
|
||||
const NAME: &'static str = T::NAME;
|
||||
const MODULE_NAME: Option<&'static str> = T::MODULE_NAME;
|
||||
|
||||
+475
-223
@@ -1,152 +1,312 @@
|
||||
use crate::builtins::{PyDictRef, PyTypeRef};
|
||||
use crate::common::lock::PyRwLock;
|
||||
use crate::common::rc::{PyRc, PyWeak};
|
||||
use crate::pyobject::{IdProtocol, PyObject, PyObjectPayload, TypeProtocol};
|
||||
use std::borrow;
|
||||
use crate::pyobject::{self, IdProtocol, PyObjectPayload, TypeProtocol};
|
||||
use crate::VirtualMachine;
|
||||
use std::any::TypeId;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct PyObjectRc<T = dyn PyObjectPayload>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
inner: PyRc<PyObject<T>>,
|
||||
// so, PyObjectRef is basically equivalent to `PyRc<PyObject<dyn PyObjectPayload>>`, except it's
|
||||
// only one pointer in width rather than 2. We do that by manually creating a vtable, and putting
|
||||
// a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do.
|
||||
// This can lead to faster code since there's just less data to pass around, as well as because of
|
||||
// some weird stuff with trait objects, alignment, and padding.
|
||||
//
|
||||
// So, every type has an alignment, which means that if you create a value of it it's location in
|
||||
// memory has to be a multiple of it's alignment. e.g., a type with alignment 4 (like i32) could be
|
||||
// at 0xb7befbc0, 0xb7befbc4, or 0xb7befbc8, but not 0xb7befbc2. If you have a struct and there are
|
||||
// 2 fields whose sizes/alignments don't perfectly fit in with each other, e.g.:
|
||||
// +-------------+-------------+---------------------------+
|
||||
// | u16 | ? | i32 |
|
||||
// | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|
||||
// +-------------+-------------+---------------------------+
|
||||
// There has to be padding in the space between the 2 fields. But, if that field is a trait object
|
||||
// (like `dyn PyObjectPayload`) we don't *know* how much padding there is between the `payload`
|
||||
// field and the previous field. So, Rust has to consult the vtable to know the exact offset of
|
||||
// `payload` in `PyObject<dyn PyObjectPayload>`, which has a huge performance impact when *every
|
||||
// single payload access* requires a vtable lookup. Thankfully, we're able to avoid that because of
|
||||
// the way we use PyObjectRef, in that whenever we want to access the payload we (almost) always
|
||||
// access it from a generic function. So, rather than doing
|
||||
//
|
||||
// - check vtable for payload offset
|
||||
// - get offset in PyObject struct
|
||||
// - call as_any() method of PyObjectPayload
|
||||
// - call downcast_ref() method of Any
|
||||
// we can just do
|
||||
// - check vtable that typeid matches
|
||||
// - pointer cast directly to *const PyObject<T>
|
||||
//
|
||||
// and at that point the compiler can know the offset of `payload` for us because **we've given it a
|
||||
// concrete type to work with before we ever access the `payload` field**
|
||||
|
||||
/// A type to just represent "we've erased the type of this object, cast it before you use it"
|
||||
struct Erased;
|
||||
|
||||
struct PyObjVTable {
|
||||
drop: unsafe fn(*mut PyInner<Erased>),
|
||||
debug: unsafe fn(*const PyInner<Erased>, &mut fmt::Formatter) -> fmt::Result,
|
||||
}
|
||||
|
||||
pub struct PyObjectWeak<T = dyn PyObjectPayload>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
{
|
||||
inner: PyWeak<PyObject<T>>,
|
||||
unsafe fn drop_obj<T: PyObjectPayload>(x: *mut PyInner<Erased>) {
|
||||
std::ptr::drop_in_place(x as *mut PyInner<T>)
|
||||
}
|
||||
|
||||
pub trait AsPyObjectRef {
|
||||
fn _as_ref(self) -> PyRc<PyObject<dyn PyObjectPayload>>;
|
||||
unsafe fn debug_obj<T: PyObjectPayload>(
|
||||
x: *const PyInner<Erased>,
|
||||
f: &mut fmt::Formatter,
|
||||
) -> fmt::Result {
|
||||
let x = &*x.cast::<PyInner<T>>();
|
||||
fmt::Debug::fmt(x, f)
|
||||
}
|
||||
|
||||
impl<T> AsPyObjectRef for PyRc<PyObject<T>>
|
||||
where
|
||||
T: PyObjectPayload,
|
||||
{
|
||||
fn _as_ref(self) -> PyRc<PyObject<dyn PyObjectPayload>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPyObjectRef for PyRc<PyObject<dyn PyObjectPayload>> {
|
||||
fn _as_ref(self) -> PyRc<PyObject<dyn PyObjectPayload>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
pub fn into_raw(this: Self) -> *const PyObject<T> {
|
||||
let ptr = PyRc::as_ptr(&this.inner);
|
||||
std::mem::forget(this);
|
||||
ptr
|
||||
}
|
||||
|
||||
unsafe fn into_rc(this: Self) -> PyRc<PyObject<T>> {
|
||||
let raw = Self::into_raw(this);
|
||||
PyRc::from_raw(raw)
|
||||
}
|
||||
|
||||
pub fn into_ref(this: Self) -> PyObjectRc<dyn PyObjectPayload> {
|
||||
PyObjectRc::<dyn PyObjectPayload> {
|
||||
inner: unsafe { Self::into_rc(this) }._as_ref(),
|
||||
impl PyObjVTable {
|
||||
pub fn of<T: PyObjectPayload>() -> &'static Self {
|
||||
&PyObjVTable {
|
||||
drop: drop_obj::<T>,
|
||||
debug: debug_obj::<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PyInner<T> {
|
||||
// TODO: move typeid into vtable once TypeId::of is const
|
||||
typeid: TypeId,
|
||||
vtable: &'static PyObjVTable,
|
||||
|
||||
typ: PyRwLock<PyTypeRef>, // __class__ member
|
||||
dict: Option<PyRwLock<PyDictRef>>, // __dict__ member
|
||||
|
||||
payload: T,
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for PyInner<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "[PyObj {:?}]", &self.payload)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an actual python object. It consists of a `typ` which is the
|
||||
/// python class, and carries some rust payload optionally. This rust
|
||||
/// payload can be a rust float or rust int in case of float and int objects.
|
||||
#[repr(transparent)]
|
||||
pub struct PyObject<T> {
|
||||
inner: ManuallyDrop<PyInner<T>>,
|
||||
}
|
||||
|
||||
impl<T: PyObjectPayload> PyObject<T> {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(payload: T, typ: PyTypeRef, dict: Option<PyDictRef>) -> PyObjectRef {
|
||||
let inner = PyInner {
|
||||
typeid: TypeId::of::<T>(),
|
||||
vtable: PyObjVTable::of::<T>(),
|
||||
typ: PyRwLock::new(typ),
|
||||
dict: dict.map(PyRwLock::new),
|
||||
payload,
|
||||
};
|
||||
PyObjectRef::new(PyObject {
|
||||
inner: ManuallyDrop::new(inner),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for PyObject<T> {
|
||||
fn drop(&mut self) {
|
||||
let erased = &mut *self.inner as *mut _ as *mut PyInner<Erased>;
|
||||
// SAFETY: the vtable contains functions that accept payload types that always match up
|
||||
// with the payload of the object
|
||||
unsafe { (self.inner.vtable.drop)(erased) }
|
||||
}
|
||||
}
|
||||
|
||||
/// The `PyObjectRef` is one of the most used types. It is a reference to a
|
||||
/// python object. A single python object can have multiple references, and
|
||||
/// this reference counting is accounted for by this type. Use the `.clone()`
|
||||
/// method to create a new reference and increment the amount of references
|
||||
/// to the python object by 1.
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyObjectRef {
|
||||
rc: PyRc<PyObject<Erased>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyObjectWeak {
|
||||
weak: PyWeak<PyObject<Erased>>,
|
||||
}
|
||||
|
||||
/// A marker type that just references a raw python object. Don't use directly, pass as a pointer
|
||||
/// back to [`PyObjectRef::from_raw`]
|
||||
pub enum RawPyObject {}
|
||||
|
||||
impl PyObjectRef {
|
||||
pub fn into_raw(this: Self) -> *const RawPyObject {
|
||||
let ptr = PyRc::as_ptr(&this.rc);
|
||||
std::mem::forget(this);
|
||||
ptr.cast()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// See PyRc::from_raw
|
||||
pub unsafe fn from_raw(ptr: *const PyObject<T>) -> Self {
|
||||
/// The raw pointer must have been previously returned from a call to
|
||||
/// [`PyObjectRef::into_raw`]. The user is responsible for ensuring that the inner data is not
|
||||
/// dropped more than once due to mishandling the reference count by calling this function
|
||||
/// too many times.
|
||||
pub unsafe fn from_raw(ptr: *const RawPyObject) -> Self {
|
||||
Self {
|
||||
inner: PyRc::from_raw(ptr),
|
||||
rc: PyRc::from_raw(ptr.cast()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(value: PyObject<T>) -> Self
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
Self {
|
||||
inner: PyRc::new(value),
|
||||
}
|
||||
fn new<T: PyObjectPayload>(value: PyObject<T>) -> Self {
|
||||
let inner = PyRc::into_raw(PyRc::new(value));
|
||||
let rc = unsafe { PyRc::from_raw(inner as *const PyObject<Erased>) };
|
||||
Self { rc }
|
||||
}
|
||||
|
||||
pub fn strong_count(this: &Self) -> usize {
|
||||
PyRc::strong_count(&this.inner)
|
||||
PyRc::strong_count(&this.rc)
|
||||
}
|
||||
|
||||
pub fn weak_count(this: &Self) -> usize {
|
||||
PyRc::weak_count(&this.inner)
|
||||
PyRc::weak_count(&this.rc)
|
||||
}
|
||||
|
||||
pub fn downgrade(this: &Self) -> PyObjectWeak<T> {
|
||||
pub fn downgrade(this: &Self) -> PyObjectWeak {
|
||||
PyObjectWeak {
|
||||
inner: PyRc::downgrade(&this.inner),
|
||||
weak: PyRc::downgrade(&this.rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn payload_is<T: PyObjectPayload>(&self) -> bool {
|
||||
self.rc.inner.typeid == TypeId::of::<T>()
|
||||
}
|
||||
|
||||
pub fn payload<T: PyObjectPayload>(&self) -> Option<&T> {
|
||||
if self.payload_is::<T>() {
|
||||
// we cast to a PyInner<T> first because we don't know T's exact offset because of
|
||||
// varying alignment, but once we get a PyInner<T> the compiler can get it for us
|
||||
let inner =
|
||||
unsafe { &*(&*self.rc.inner as *const PyInner<Erased> as *const PyInner<T>) };
|
||||
Some(&inner.payload)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to downcast this reference to a subclass.
|
||||
///
|
||||
/// If the downcast fails, the original ref is returned in as `Err` so
|
||||
/// another downcast can be attempted without unnecessary cloning.
|
||||
pub fn downcast<T: PyObjectPayload>(self) -> Result<PyRef<T>, Self> {
|
||||
if self.payload_is::<T>() {
|
||||
Ok(unsafe { PyRef::from_obj_unchecked(self) })
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast_ref<T: PyObjectPayload>(&self) -> Option<&PyRef<T>> {
|
||||
if self.payload_is::<T>() {
|
||||
// SAFETY: just checked that the payload is T, and PyRef is repr(transparent) over
|
||||
// PyObjectRef
|
||||
Some(unsafe { &*(self as *const PyObjectRef as *const PyRef<T>) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn class_lock(&self) -> &PyRwLock<PyTypeRef> {
|
||||
&self.rc.inner.typ
|
||||
}
|
||||
|
||||
// ideally we'd be able to define these in pyobject.rs, but method visibility rules are weird
|
||||
|
||||
/// Attempt to downcast this reference to the specific class that is associated `T`.
|
||||
///
|
||||
/// If the downcast fails, the original ref is returned in as `Err` so
|
||||
/// another downcast can be attempted without unnecessary cloning.
|
||||
pub fn downcast_exact<T: PyObjectPayload + pyobject::PyValue>(
|
||||
self,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<PyRef<T>, Self> {
|
||||
if self.class().is(T::class(vm)) {
|
||||
// TODO: is this always true?
|
||||
assert!(
|
||||
self.payload_is::<T>(),
|
||||
"obj.__class__ is T::class() but payload is not T"
|
||||
);
|
||||
// SAFETY: just asserted that payload_is::<T>()
|
||||
Ok(unsafe { PyRef::from_obj_unchecked(self) })
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_if_exact<T: PyObjectPayload + pyobject::PyValue>(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
) -> Option<&T> {
|
||||
if self.class().is(T::class(vm)) {
|
||||
self.payload()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dict(&self) -> Option<PyDictRef> {
|
||||
self.rc.inner.dict.as_ref().map(|mu| mu.read().clone())
|
||||
}
|
||||
/// Set the dict field. Returns `Err(dict)` if this object does not have a dict field
|
||||
/// in the first place.
|
||||
pub fn set_dict(&self, dict: PyDictRef) -> Result<(), PyDictRef> {
|
||||
match self.rc.inner.dict {
|
||||
Some(ref mu) => {
|
||||
*mu.write() = dict;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(dict),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_if_subclass<T: pyobject::PyValue>(
|
||||
&self,
|
||||
vm: &crate::VirtualMachine,
|
||||
) -> Option<&T> {
|
||||
if self.class().issubclass(T::class(vm)) {
|
||||
self.payload()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PyObjectPayload> IdProtocol for PyObjectRc<T>
|
||||
where
|
||||
PyRc<PyObject<T>>: IdProtocol + AsPyObjectRef,
|
||||
{
|
||||
impl IdProtocol for PyObjectRef {
|
||||
fn get_id(&self) -> usize {
|
||||
self.inner.get_id()
|
||||
self.rc.get_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyObjectWeak<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
pub fn upgrade(&self) -> Option<PyObjectRc<T>> {
|
||||
self.inner.upgrade().map(|inner| PyObjectRc { inner })
|
||||
impl PyObjectWeak {
|
||||
pub fn upgrade(&self) -> Option<PyObjectRef> {
|
||||
self.weak.upgrade().map(|rc| PyObjectRef { rc })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "threading")]
|
||||
unsafe impl<T> Send for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
}
|
||||
#[cfg(feature = "threading")]
|
||||
unsafe impl<T> Sync for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "threading")]
|
||||
unsafe impl<T> Send for PyObjectWeak<T> where T: ?Sized + PyObjectPayload {}
|
||||
#[cfg(feature = "threading")]
|
||||
unsafe impl<T> Sync for PyObjectWeak<T> where T: ?Sized + PyObjectPayload {}
|
||||
|
||||
impl<T> Drop for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
impl Drop for PyObjectRef {
|
||||
fn drop(&mut self) {
|
||||
use crate::pyobject::BorrowValue;
|
||||
|
||||
// PyObjectRc will drop the value when its count goes to 0
|
||||
if PyRc::strong_count(&self.inner) != 1 {
|
||||
// PyObjectRef will drop the value when its count goes to 0
|
||||
if PyRc::strong_count(&self.rc) != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
// CPython-compatible drop implementation
|
||||
let zelf = Self::into_ref(self.clone());
|
||||
if let Some(del_slot) = zelf.class().mro_find_map(|cls| cls.slots.del.load()) {
|
||||
let zelf = self.clone();
|
||||
if let Some(del_slot) = self.class().mro_find_map(|cls| cls.slots.del.load()) {
|
||||
crate::vm::thread::with_vm(&zelf, |vm| {
|
||||
if let Err(e) = del_slot(&zelf, vm) {
|
||||
// exception in del will be ignored but printed
|
||||
@@ -169,146 +329,238 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
let _ = unsafe { PyObjectRc::<dyn PyObjectPayload>::into_rc(zelf) };
|
||||
debug_assert!(PyRc::strong_count(&self.inner) == 1); // make sure to keep same state
|
||||
// __del__ might have resurrected the object at this point, but that's fine,
|
||||
// inner.strong_count would be >1 now and it'll maybe get dropped the next time
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
type Target = PyObject<T>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &PyObject<T> {
|
||||
self.inner.deref()
|
||||
impl fmt::Debug for PyObjectRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// SAFETY: the vtable contains functions that accept payload types that always match up
|
||||
// with the payload of the object
|
||||
unsafe { (self.rc.inner.vtable.debug)(&*self.rc.inner, f) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
{
|
||||
impl fmt::Debug for PyObjectWeak {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(PyWeak)")
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a Python object.
|
||||
///
|
||||
/// Note that a `PyRef<T>` can only deref to a shared / immutable reference.
|
||||
/// It is the payload type's responsibility to handle (possibly concurrent)
|
||||
/// mutability with locks or concurrent data structures if required.
|
||||
///
|
||||
/// A `PyRef<T>` can be directly returned from a built-in function to handle
|
||||
/// situations (such as when implementing in-place methods such as `__iadd__`)
|
||||
/// where a reference to the same object must be returned.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyRef<T: PyObjectPayload> {
|
||||
// invariant: this obj must always have payload of type T
|
||||
obj: PyObjectRef,
|
||||
_payload: PhantomData<PyRc<T>>,
|
||||
}
|
||||
|
||||
impl<T: PyObjectPayload> Clone for PyRef<T> {
|
||||
fn clone(&self) -> Self {
|
||||
PyObjectRc {
|
||||
inner: self.inner.clone(),
|
||||
Self {
|
||||
obj: self.obj.clone(),
|
||||
_payload: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
PyObject<T>: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
PyObject<T>: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Pointer for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef,
|
||||
PyObject<T>: fmt::Pointer,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> borrow::Borrow<T> for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef + borrow::Borrow<T>,
|
||||
{
|
||||
fn borrow(&self) -> &T {
|
||||
self.inner.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> borrow::BorrowMut<T> for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef + borrow::BorrowMut<T>,
|
||||
{
|
||||
fn borrow_mut(&mut self) -> &mut T {
|
||||
self.inner.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for PyObjectRc<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyRc<PyObject<T>>: AsPyObjectRef + AsRef<T>,
|
||||
{
|
||||
fn as_ref(&self) -> &T {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for PyObjectWeak<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
PyObjectWeak {
|
||||
inner: self.inner.clone(),
|
||||
impl<T: PyObjectPayload> PyRef<T> {
|
||||
/// Safety: payload type of `obj` must be `T`
|
||||
unsafe fn from_obj_unchecked(obj: PyObjectRef) -> Self {
|
||||
PyRef {
|
||||
obj,
|
||||
_payload: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for PyObjectWeak<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyObject<T>: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
#[inline(always)]
|
||||
pub fn as_object(&self) -> &PyObjectRef {
|
||||
&self.obj
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_object(self) -> PyObjectRef {
|
||||
self.obj
|
||||
}
|
||||
|
||||
pub fn downgrade(this: &Self) -> PyWeakRef<T> {
|
||||
PyWeakRef {
|
||||
weak: PyObjectRef::downgrade(&this.obj),
|
||||
_payload: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// ideally we'd be able to define this in pyobject.rs, but method visibility rules are weird
|
||||
pub fn new_ref(
|
||||
payload: T,
|
||||
typ: crate::builtins::PyTypeRef,
|
||||
dict: Option<crate::builtins::PyDictRef>,
|
||||
) -> Self {
|
||||
let obj = PyObject::new(payload, typ, dict);
|
||||
// SAFETY: we just created the object from a payload of type T
|
||||
unsafe { Self::from_obj_unchecked(obj) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> borrow::Borrow<T> for PyObjectWeak<T>
|
||||
impl<T> Deref for PyRef<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyWeak<PyObject<T>>: borrow::Borrow<T>,
|
||||
T: PyObjectPayload,
|
||||
{
|
||||
fn borrow(&self) -> &T {
|
||||
self.inner.borrow()
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: per the invariant on `self.obj`, the payload of the pyobject is always T, so it
|
||||
// can always be cast to a PyInner<T>
|
||||
let obj = unsafe { &*(&*self.obj.rc.inner as *const PyInner<Erased> as *const PyInner<T>) };
|
||||
&obj.payload
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> borrow::BorrowMut<T> for PyObjectWeak<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyWeak<PyObject<T>>: borrow::BorrowMut<T>,
|
||||
{
|
||||
fn borrow_mut(&mut self) -> &mut T {
|
||||
self.inner.borrow_mut()
|
||||
#[repr(transparent)]
|
||||
pub struct PyWeakRef<T: PyObjectPayload> {
|
||||
weak: PyObjectWeak,
|
||||
_payload: PhantomData<PyWeak<T>>,
|
||||
}
|
||||
|
||||
impl<T: PyObjectPayload> PyWeakRef<T> {
|
||||
pub fn upgrade(&self) -> Option<PyRef<T>> {
|
||||
self.weak.upgrade().map(|obj| unsafe {
|
||||
// SAFETY: PyWeakRef<T> is only ever created from a PyRef<T>
|
||||
PyRef::from_obj_unchecked(obj)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for PyObjectWeak<T>
|
||||
where
|
||||
T: ?Sized + PyObjectPayload,
|
||||
PyWeak<PyObject<T>>: AsRef<T>,
|
||||
{
|
||||
fn as_ref(&self) -> &T {
|
||||
self.inner.as_ref()
|
||||
/// Paritally initialize a struct, ensuring that all fields are
|
||||
/// either given values or explicitly left uninitialized
|
||||
macro_rules! partially_init {
|
||||
(
|
||||
$ty:path {$($init_field:ident: $init_value:expr),*$(,)?},
|
||||
Uninit { $($uninit_field:ident),*$(,)? }$(,)?
|
||||
) => {{
|
||||
// check all the fields are there but *don't* actually run it
|
||||
if false {
|
||||
#[allow(invalid_value, dead_code, unreachable_code)]
|
||||
let _ = {$ty {
|
||||
$($init_field: $init_value,)*
|
||||
$($uninit_field: unreachable!(),)*
|
||||
}};
|
||||
}
|
||||
let mut m = ::std::mem::MaybeUninit::<$ty>::uninit();
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
$(::std::ptr::write(&mut (*m.as_mut_ptr()).$init_field, $init_value);)*
|
||||
}
|
||||
m
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef) {
|
||||
use crate::builtins::{object, PyType, PyWeak};
|
||||
use crate::pyobject::{PyAttributes, PyClassDef, PyClassImpl};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
// `type` inherits from `object`
|
||||
// and both `type` and `object are instances of `type`.
|
||||
// to produce this circular dependency, we need an unsafe block.
|
||||
// (and yes, this will never get dropped. TODO?)
|
||||
let (type_type, object_type) = {
|
||||
type UninitRef<T> = PyRwLock<PyRc<MaybeUninit<PyInner<T>>>>;
|
||||
|
||||
// We cast between these 2 types, so make sure (at compile time) that there's no change in
|
||||
// layout when we wrap PyInner<PyTypeObj> in MaybeUninit<>
|
||||
static_assertions::assert_eq_size!(MaybeUninit<PyInner<PyType>>, PyInner<PyType>);
|
||||
static_assertions::assert_eq_align!(MaybeUninit<PyInner<PyType>>, PyInner<PyType>);
|
||||
|
||||
let type_payload = PyType {
|
||||
name: PyTypeRef::NAME.to_owned(),
|
||||
base: None,
|
||||
bases: vec![],
|
||||
mro: vec![],
|
||||
subclasses: PyRwLock::default(),
|
||||
attributes: PyRwLock::new(PyAttributes::new()),
|
||||
slots: PyType::make_slots(),
|
||||
};
|
||||
let object_payload = PyType {
|
||||
name: object::PyBaseObject::NAME.to_owned(),
|
||||
base: None,
|
||||
bases: vec![],
|
||||
mro: vec![],
|
||||
subclasses: PyRwLock::default(),
|
||||
attributes: PyRwLock::new(PyAttributes::new()),
|
||||
slots: object::PyBaseObject::make_slots(),
|
||||
};
|
||||
let type_type = PyRc::new(partially_init!(
|
||||
PyInner::<PyType> {
|
||||
typeid: TypeId::of::<PyType>(),
|
||||
vtable: PyObjVTable::of::<PyType>(),
|
||||
dict: None,
|
||||
payload: type_payload,
|
||||
},
|
||||
Uninit { typ }
|
||||
));
|
||||
let object_type = PyRc::new(partially_init!(
|
||||
PyInner::<PyType> {
|
||||
typeid: TypeId::of::<PyType>(),
|
||||
vtable: PyObjVTable::of::<PyType>(),
|
||||
dict: None,
|
||||
payload: object_payload,
|
||||
},
|
||||
Uninit { typ },
|
||||
));
|
||||
|
||||
let object_type_ptr = PyRc::into_raw(object_type) as *mut MaybeUninit<PyInner<PyType>>
|
||||
as *mut PyInner<PyType>;
|
||||
let type_type_ptr = PyRc::into_raw(type_type.clone()) as *mut MaybeUninit<PyInner<PyType>>
|
||||
as *mut PyInner<PyType>;
|
||||
|
||||
unsafe {
|
||||
ptr::write(
|
||||
&mut (*object_type_ptr).typ as *mut PyRwLock<PyTypeRef> as *mut UninitRef<PyType>,
|
||||
PyRwLock::new(type_type.clone()),
|
||||
);
|
||||
ptr::write(
|
||||
&mut (*type_type_ptr).typ as *mut PyRwLock<PyTypeRef> as *mut UninitRef<PyType>,
|
||||
PyRwLock::new(type_type),
|
||||
);
|
||||
|
||||
let type_type =
|
||||
PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(type_type_ptr.cast()));
|
||||
let object_type =
|
||||
PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(object_type_ptr.cast()));
|
||||
|
||||
(*type_type_ptr).payload.mro = vec![object_type.clone()];
|
||||
(*type_type_ptr).payload.bases = vec![object_type.clone()];
|
||||
(*type_type_ptr).payload.base = Some(object_type.clone());
|
||||
|
||||
(type_type, object_type)
|
||||
}
|
||||
};
|
||||
|
||||
object_type
|
||||
.subclasses
|
||||
.write()
|
||||
.push(PyWeak::downgrade(&type_type.as_object()));
|
||||
|
||||
(type_type, object_type)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn miri_test_type_initialization() {
|
||||
let _ = init_type_hierarchy();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -809,7 +809,7 @@ mod _io {
|
||||
.into_pyobject(vm);
|
||||
|
||||
// TODO: loop if write() raises an interrupt
|
||||
call_method(vm, self.raw.as_ref().unwrap(), "write", (memobj.clone(),))?
|
||||
call_method(vm, self.raw.as_ref().unwrap(), "write", (memobj,))?
|
||||
} else {
|
||||
let options = BufferOptions {
|
||||
len,
|
||||
|
||||
@@ -16,8 +16,8 @@ mod decl {
|
||||
use crate::function::{Args, FuncArgs, OptionalArg, OptionalOption};
|
||||
use crate::iterator::{call_next, get_all, get_iter, get_next_object};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, IdProtocol, IntoPyObject, PyCallable, PyObjectRc, PyObjectRef, PyObjectWeak,
|
||||
PyRef, PyResult, PyValue, StaticType, TypeProtocol,
|
||||
BorrowValue, IdProtocol, IntoPyObject, PyCallable, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
PyWeakRef, StaticType, TypeProtocol,
|
||||
};
|
||||
use crate::slots::PyIter;
|
||||
use crate::vm::VirtualMachine;
|
||||
@@ -458,7 +458,7 @@ mod decl {
|
||||
current_value: Option<PyObjectRef>,
|
||||
current_key: Option<PyObjectRef>,
|
||||
next_group: bool,
|
||||
grouper: Option<PyObjectWeak<PyItertoolsGrouper>>,
|
||||
grouper: Option<PyWeakRef<PyItertoolsGrouper>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for GroupByState {
|
||||
@@ -573,7 +573,7 @@ mod decl {
|
||||
}
|
||||
.into_ref(vm);
|
||||
|
||||
state.grouper = Some(PyObjectRc::downgrade(&grouper.clone().into_typed_pyobj()));
|
||||
state.grouper = Some(PyRef::downgrade(&grouper));
|
||||
Ok((state.current_key.as_ref().unwrap().clone(), grouper).into_pyobject(vm))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +445,7 @@ pub(crate) mod _struct {
|
||||
where
|
||||
T: num_traits::PrimInt + for<'a> std::convert::TryFrom<&'a BigInt>,
|
||||
{
|
||||
match vm.to_index_opt(arg.clone()) {
|
||||
match vm.to_index_opt(arg) {
|
||||
Some(index) => try_to_primitive(index?.borrow_value(), vm),
|
||||
None => Err(new_struct_error(
|
||||
vm,
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
//! - [rust weak struct](https://doc.rust-lang.org/std/rc/struct.Weak.html)
|
||||
//!
|
||||
|
||||
use crate::pyobject::{PyObjectRc, PyObjectRef};
|
||||
use crate::pyobject::PyObjectRef;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
fn weakref_getweakrefcount(obj: PyObjectRef) -> usize {
|
||||
PyObjectRc::weak_count(&obj)
|
||||
PyObjectRef::weak_count(&obj)
|
||||
}
|
||||
|
||||
fn weakref_getweakrefs(_obj: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
|
||||
+2
-3
@@ -6,8 +6,7 @@ use crate::common::hash::{PyHash, PyUHash};
|
||||
use crate::frame::FrameRef;
|
||||
use crate::function::{Args, FuncArgs, OptionalArg};
|
||||
use crate::pyobject::{
|
||||
ItemProtocol, PyClassImpl, PyContext, PyObjectRc, PyObjectRef, PyRefExact, PyResult,
|
||||
PyStructSequence,
|
||||
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRefExact, PyResult, PyStructSequence,
|
||||
};
|
||||
use crate::vm::{PySettings, VirtualMachine};
|
||||
use crate::{builtins, exceptions, py_io, version};
|
||||
@@ -137,7 +136,7 @@ impl SysFlags {
|
||||
}
|
||||
|
||||
fn sys_getrefcount(obj: PyObjectRef) -> usize {
|
||||
PyObjectRc::strong_count(&obj)
|
||||
PyObjectRef::strong_count(&obj)
|
||||
}
|
||||
|
||||
fn sys_getsizeof(obj: PyObjectRef) -> usize {
|
||||
|
||||
+2
-108
@@ -38,13 +38,8 @@ use crate::builtins::tuple;
|
||||
use crate::builtins::weakproxy;
|
||||
use crate::builtins::weakref;
|
||||
use crate::builtins::zip;
|
||||
use crate::pyobject::{
|
||||
PyAttributes, PyClassDef, PyClassImpl, PyContext, PyObject, PyObjectRc, PyObjectRef, StaticType,
|
||||
};
|
||||
use crate::pyobject::{PyAttributes, PyContext, StaticType};
|
||||
use crate::slots::PyTypeSlots;
|
||||
use rustpython_common::{lock::PyRwLock, rc::PyRc};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
/// Holder of references to builtin types.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -121,7 +116,7 @@ pub struct TypeZoo {
|
||||
|
||||
impl TypeZoo {
|
||||
pub(crate) fn init() -> Self {
|
||||
let (type_type, object_type) = init_type_hierarchy();
|
||||
let (type_type, object_type) = crate::pyobjectrc::init_type_hierarchy();
|
||||
Self {
|
||||
// the order matters for type, object and int
|
||||
type_type: pytype::PyType::init_manually(type_type).clone(),
|
||||
@@ -269,104 +264,3 @@ pub fn create_type_with_slots(
|
||||
)
|
||||
.expect("Failed to create a new type in internal code.")
|
||||
}
|
||||
|
||||
/// Paritally initialize a struct, ensuring that all fields are
|
||||
/// either given values or explicitly left uninitialized
|
||||
macro_rules! partially_init {
|
||||
(
|
||||
$ty:path {$($init_field:ident: $init_value:expr),*$(,)?},
|
||||
Uninit { $($uninit_field:ident),*$(,)? }$(,)?
|
||||
) => {{
|
||||
// check all the fields are there but *don't* actually run it
|
||||
if false {
|
||||
#[allow(invalid_value, dead_code, unreachable_code)]
|
||||
let _ = {$ty {
|
||||
$($init_field: $init_value,)*
|
||||
$($uninit_field: unreachable!(),)*
|
||||
}};
|
||||
}
|
||||
let mut m = ::std::mem::MaybeUninit::<$ty>::uninit();
|
||||
unsafe {
|
||||
$(::std::ptr::write(&mut (*m.as_mut_ptr()).$init_field, $init_value);)*
|
||||
}
|
||||
m
|
||||
}};
|
||||
}
|
||||
|
||||
fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef) {
|
||||
// `type` inherits from `object`
|
||||
// and both `type` and `object are instances of `type`.
|
||||
// to produce this circular dependency, we need an unsafe block.
|
||||
// (and yes, this will never get dropped. TODO?)
|
||||
let (type_type, object_type) = {
|
||||
type PyTypeObj = PyObject<PyType>;
|
||||
type UninitRef<T> = PyRwLock<PyRc<MaybeUninit<PyObject<T>>>>;
|
||||
|
||||
let type_payload = PyType {
|
||||
name: PyTypeRef::NAME.to_owned(),
|
||||
base: None,
|
||||
bases: vec![],
|
||||
mro: vec![],
|
||||
subclasses: PyRwLock::default(),
|
||||
attributes: PyRwLock::new(PyAttributes::new()),
|
||||
slots: PyType::make_slots(),
|
||||
};
|
||||
let object_payload = PyType {
|
||||
name: object::PyBaseObject::NAME.to_owned(),
|
||||
base: None,
|
||||
bases: vec![],
|
||||
mro: vec![],
|
||||
subclasses: PyRwLock::default(),
|
||||
attributes: PyRwLock::new(PyAttributes::new()),
|
||||
slots: object::PyBaseObject::make_slots(),
|
||||
};
|
||||
let type_type: PyRc<MaybeUninit<PyTypeObj>> = PyRc::new(partially_init!(
|
||||
PyObject::<PyType> {
|
||||
dict: None,
|
||||
payload: type_payload,
|
||||
},
|
||||
Uninit { typ }
|
||||
));
|
||||
let object_type: PyRc<MaybeUninit<PyTypeObj>> = PyRc::new(partially_init!(
|
||||
PyObject::<PyType> {
|
||||
dict: None,
|
||||
payload: object_payload,
|
||||
},
|
||||
Uninit { typ },
|
||||
));
|
||||
|
||||
let object_type_ptr =
|
||||
PyRc::into_raw(object_type) as *mut MaybeUninit<PyTypeObj> as *mut PyTypeObj;
|
||||
let type_type_ptr =
|
||||
PyRc::into_raw(type_type.clone()) as *mut MaybeUninit<PyTypeObj> as *mut PyTypeObj;
|
||||
|
||||
unsafe {
|
||||
ptr::write(
|
||||
&mut (*object_type_ptr).typ as *mut PyRwLock<PyObjectRc<PyType>>
|
||||
as *mut UninitRef<PyType>,
|
||||
PyRwLock::new(type_type.clone()),
|
||||
);
|
||||
ptr::write(
|
||||
&mut (*type_type_ptr).typ as *mut PyRwLock<PyObjectRc<PyType>>
|
||||
as *mut UninitRef<PyType>,
|
||||
PyRwLock::new(type_type),
|
||||
);
|
||||
|
||||
let type_type = PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(type_type_ptr));
|
||||
let object_type = PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(object_type_ptr));
|
||||
|
||||
(*type_type_ptr).payload.mro = vec![object_type.clone()];
|
||||
(*type_type_ptr).payload.bases = vec![object_type.clone()];
|
||||
(*type_type_ptr).payload.base = Some(object_type.clone());
|
||||
|
||||
(type_type, object_type)
|
||||
}
|
||||
};
|
||||
|
||||
object_type
|
||||
.subclasses
|
||||
.write()
|
||||
.push(weakref::PyWeak::downgrade(&type_type.as_object()));
|
||||
|
||||
(type_type, object_type)
|
||||
}
|
||||
Reference in new issue
Block a user