rustpython_vm/object/
ext.rs

1use super::{
2    core::{Py, PyObject, PyObjectRef, PyRef},
3    payload::{PyObjectPayload, PyPayload},
4};
5use crate::common::{
6    atomic::{Ordering, PyAtomic, Radium},
7    lock::PyRwLockReadGuard,
8};
9use crate::{
10    builtins::{PyBaseExceptionRef, PyStrInterned, PyType},
11    convert::{IntoPyException, ToPyObject, ToPyResult, TryFromObject},
12    vm::Context,
13    VirtualMachine,
14};
15use std::{borrow::Borrow, fmt, marker::PhantomData, ops::Deref, ptr::null_mut};
16
17/* Python objects and references.
18
19Okay, so each python object itself is an class itself (PyObject). Each
20python object can have several references to it (PyObjectRef). These
21references are Rc (reference counting) rust smart pointers. So when
22all references are destroyed, the object itself also can be cleaned up.
23Basically reference counting, but then done by rust.
24
25*/
26
27/*
28 * Good reference: https://github.com/ProgVal/pythonvm-rust/blob/master/src/objects/mod.rs
29 */
30
31/// Use this type for functions which return a python object or an exception.
32/// Both the python object and the python exception are `PyObjectRef` types
33/// since exceptions are also python objects.
34pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; // A valid value, or an exception
35
36impl<T: fmt::Display> fmt::Display for PyRef<T>
37where
38    T: PyObjectPayload + fmt::Display,
39{
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        fmt::Display::fmt(&**self, f)
42    }
43}
44impl<T: fmt::Display> fmt::Display for Py<T>
45where
46    T: PyObjectPayload + fmt::Display,
47{
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        fmt::Display::fmt(&**self, f)
50    }
51}
52
53#[repr(transparent)]
54pub struct PyExact<T: PyObjectPayload> {
55    inner: Py<T>,
56}
57
58impl<T: PyPayload> PyExact<T> {
59    /// # Safety
60    /// Given reference must be exact type of payload T
61    #[inline(always)]
62    pub unsafe fn ref_unchecked(r: &Py<T>) -> &Self {
63        &*(r as *const _ as *const Self)
64    }
65}
66
67impl<T: PyPayload> Deref for PyExact<T> {
68    type Target = Py<T>;
69    #[inline(always)]
70    fn deref(&self) -> &Py<T> {
71        &self.inner
72    }
73}
74
75impl<T: PyObjectPayload> Borrow<PyObject> for PyExact<T> {
76    #[inline(always)]
77    fn borrow(&self) -> &PyObject {
78        self.inner.borrow()
79    }
80}
81
82impl<T: PyObjectPayload> AsRef<PyObject> for PyExact<T> {
83    #[inline(always)]
84    fn as_ref(&self) -> &PyObject {
85        self.inner.as_ref()
86    }
87}
88
89impl<T: PyObjectPayload> Borrow<Py<T>> for PyExact<T> {
90    #[inline(always)]
91    fn borrow(&self) -> &Py<T> {
92        &self.inner
93    }
94}
95
96impl<T: PyObjectPayload> AsRef<Py<T>> for PyExact<T> {
97    #[inline(always)]
98    fn as_ref(&self) -> &Py<T> {
99        &self.inner
100    }
101}
102
103impl<T: PyPayload> std::borrow::ToOwned for PyExact<T> {
104    type Owned = PyRefExact<T>;
105    fn to_owned(&self) -> Self::Owned {
106        let owned = self.inner.to_owned();
107        unsafe { PyRefExact::new_unchecked(owned) }
108    }
109}
110
111impl<T: PyPayload> PyRef<T> {
112    pub fn into_exact_or(
113        self,
114        ctx: &Context,
115        f: impl FnOnce(Self) -> PyRefExact<T>,
116    ) -> PyRefExact<T> {
117        if self.class().is(T::class(ctx)) {
118            unsafe { PyRefExact::new_unchecked(self) }
119        } else {
120            f(self)
121        }
122    }
123}
124
125/// PyRef but guaranteed not to be a subtype instance
126#[derive(Debug)]
127#[repr(transparent)]
128pub struct PyRefExact<T: PyObjectPayload> {
129    inner: PyRef<T>,
130}
131
132impl<T: PyObjectPayload> PyRefExact<T> {
133    /// # Safety
134    /// obj must have exact type for the payload
135    pub unsafe fn new_unchecked(obj: PyRef<T>) -> Self {
136        Self { inner: obj }
137    }
138
139    pub fn into_pyref(self) -> PyRef<T> {
140        self.inner
141    }
142}
143
144impl<T: PyObjectPayload> Clone for PyRefExact<T> {
145    fn clone(&self) -> Self {
146        let inner = self.inner.clone();
147        Self { inner }
148    }
149}
150
151impl<T: PyPayload> TryFromObject for PyRefExact<T> {
152    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
153        let target_cls = T::class(&vm.ctx);
154        let cls = obj.class();
155        if cls.is(target_cls) {
156            let obj = obj
157                .downcast()
158                .map_err(|obj| vm.new_downcast_runtime_error(target_cls, &obj))?;
159            Ok(Self { inner: obj })
160        } else if cls.fast_issubclass(target_cls) {
161            Err(vm.new_type_error(format!(
162                "Expected an exact instance of '{}', not a subclass '{}'",
163                target_cls.name(),
164                cls.name(),
165            )))
166        } else {
167            Err(vm.new_type_error(format!(
168                "Expected type '{}', not '{}'",
169                target_cls.name(),
170                cls.name(),
171            )))
172        }
173    }
174}
175
176impl<T: PyPayload> Deref for PyRefExact<T> {
177    type Target = PyExact<T>;
178    #[inline(always)]
179    fn deref(&self) -> &PyExact<T> {
180        unsafe { PyExact::ref_unchecked(self.inner.deref()) }
181    }
182}
183
184impl<T: PyObjectPayload> Borrow<PyObject> for PyRefExact<T> {
185    #[inline(always)]
186    fn borrow(&self) -> &PyObject {
187        self.inner.borrow()
188    }
189}
190
191impl<T: PyObjectPayload> AsRef<PyObject> for PyRefExact<T> {
192    #[inline(always)]
193    fn as_ref(&self) -> &PyObject {
194        self.inner.as_ref()
195    }
196}
197
198impl<T: PyObjectPayload> Borrow<Py<T>> for PyRefExact<T> {
199    #[inline(always)]
200    fn borrow(&self) -> &Py<T> {
201        self.inner.borrow()
202    }
203}
204
205impl<T: PyObjectPayload> AsRef<Py<T>> for PyRefExact<T> {
206    #[inline(always)]
207    fn as_ref(&self) -> &Py<T> {
208        self.inner.as_ref()
209    }
210}
211
212impl<T: PyPayload> Borrow<PyExact<T>> for PyRefExact<T> {
213    #[inline(always)]
214    fn borrow(&self) -> &PyExact<T> {
215        self
216    }
217}
218
219impl<T: PyPayload> AsRef<PyExact<T>> for PyRefExact<T> {
220    #[inline(always)]
221    fn as_ref(&self) -> &PyExact<T> {
222        self
223    }
224}
225
226impl<T: PyPayload> ToPyObject for PyRefExact<T> {
227    #[inline(always)]
228    fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
229        self.inner.into()
230    }
231}
232
233pub struct PyAtomicRef<T> {
234    inner: PyAtomic<*mut u8>,
235    _phantom: PhantomData<T>,
236}
237
238cfg_if::cfg_if! {
239    if #[cfg(feature = "threading")] {
240        unsafe impl<T: Send + PyObjectPayload> Send for PyAtomicRef<T> {}
241        unsafe impl<T: Sync + PyObjectPayload> Sync for PyAtomicRef<T> {}
242        unsafe impl<T: Send + PyObjectPayload> Send for PyAtomicRef<Option<T>> {}
243        unsafe impl<T: Sync + PyObjectPayload> Sync for PyAtomicRef<Option<T>> {}
244        unsafe impl Send for PyAtomicRef<PyObject> {}
245        unsafe impl Sync for PyAtomicRef<PyObject> {}
246        unsafe impl Send for PyAtomicRef<Option<PyObject>> {}
247        unsafe impl Sync for PyAtomicRef<Option<PyObject>> {}
248    }
249}
250
251impl<T: fmt::Debug> fmt::Debug for PyAtomicRef<T> {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "PyAtomicRef(")?;
254        unsafe {
255            self.inner
256                .load(Ordering::Relaxed)
257                .cast::<T>()
258                .as_ref()
259                .fmt(f)
260        }?;
261        write!(f, ")")
262    }
263}
264
265impl<T: PyObjectPayload> From<PyRef<T>> for PyAtomicRef<T> {
266    fn from(pyref: PyRef<T>) -> Self {
267        let py = PyRef::leak(pyref);
268        Self {
269            inner: Radium::new(py as *const _ as *mut _),
270            _phantom: Default::default(),
271        }
272    }
273}
274
275impl<T: PyObjectPayload> Deref for PyAtomicRef<T> {
276    type Target = Py<T>;
277
278    fn deref(&self) -> &Self::Target {
279        unsafe {
280            self.inner
281                .load(Ordering::Relaxed)
282                .cast::<Py<T>>()
283                .as_ref()
284                .unwrap_unchecked()
285        }
286    }
287}
288
289impl<T: PyObjectPayload> PyAtomicRef<T> {
290    /// # Safety
291    /// The caller is responsible to keep the returned PyRef alive
292    /// until no more reference can be used via PyAtomicRef::deref()
293    #[must_use]
294    pub unsafe fn swap(&self, pyref: PyRef<T>) -> PyRef<T> {
295        let py = PyRef::leak(pyref) as *const Py<T> as *mut _;
296        let old = Radium::swap(&self.inner, py, Ordering::AcqRel);
297        PyRef::from_raw(old.cast())
298    }
299
300    pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
301        let old = unsafe { self.swap(pyref) };
302        if let Some(frame) = vm.current_frame() {
303            frame.temporary_refs.lock().push(old.into());
304        }
305    }
306}
307
308impl<T: PyObjectPayload> From<Option<PyRef<T>>> for PyAtomicRef<Option<T>> {
309    fn from(opt_ref: Option<PyRef<T>>) -> Self {
310        let val = opt_ref
311            .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
312            .unwrap_or(null_mut());
313        Self {
314            inner: Radium::new(val),
315            _phantom: Default::default(),
316        }
317    }
318}
319
320impl<T: PyObjectPayload> PyAtomicRef<Option<T>> {
321    pub fn deref(&self) -> Option<&Py<T>> {
322        unsafe { self.inner.load(Ordering::Relaxed).cast::<Py<T>>().as_ref() }
323    }
324
325    pub fn to_owned(&self) -> Option<PyRef<T>> {
326        self.deref().map(|x| x.to_owned())
327    }
328
329    /// # Safety
330    /// The caller is responsible to keep the returned PyRef alive
331    /// until no more reference can be used via PyAtomicRef::deref()
332    #[must_use]
333    pub unsafe fn swap(&self, opt_ref: Option<PyRef<T>>) -> Option<PyRef<T>> {
334        let val = opt_ref
335            .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
336            .unwrap_or(null_mut());
337        let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
338        unsafe { old.cast::<Py<T>>().as_ref().map(|x| PyRef::from_raw(x)) }
339    }
340
341    pub fn swap_to_temporary_refs(&self, opt_ref: Option<PyRef<T>>, vm: &VirtualMachine) {
342        let Some(old) = (unsafe { self.swap(opt_ref) }) else {
343            return;
344        };
345        if let Some(frame) = vm.current_frame() {
346            frame.temporary_refs.lock().push(old.into());
347        }
348    }
349}
350
351impl From<PyObjectRef> for PyAtomicRef<PyObject> {
352    fn from(obj: PyObjectRef) -> Self {
353        let obj = obj.into_raw();
354        Self {
355            inner: Radium::new(obj as *mut _),
356            _phantom: Default::default(),
357        }
358    }
359}
360
361impl Deref for PyAtomicRef<PyObject> {
362    type Target = PyObject;
363
364    fn deref(&self) -> &Self::Target {
365        unsafe {
366            self.inner
367                .load(Ordering::Relaxed)
368                .cast::<PyObject>()
369                .as_ref()
370                .unwrap_unchecked()
371        }
372    }
373}
374
375impl PyAtomicRef<PyObject> {
376    /// # Safety
377    /// The caller is responsible to keep the returned PyRef alive
378    /// until no more reference can be used via PyAtomicRef::deref()
379    #[must_use]
380    pub unsafe fn swap(&self, obj: PyObjectRef) -> PyObjectRef {
381        let obj = obj.into_raw();
382        let old = Radium::swap(&self.inner, obj as *mut _, Ordering::AcqRel);
383        PyObjectRef::from_raw(old as _)
384    }
385
386    pub fn swap_to_temporary_refs(&self, obj: PyObjectRef, vm: &VirtualMachine) {
387        let old = unsafe { self.swap(obj) };
388        if let Some(frame) = vm.current_frame() {
389            frame.temporary_refs.lock().push(old);
390        }
391    }
392}
393
394impl From<Option<PyObjectRef>> for PyAtomicRef<Option<PyObject>> {
395    fn from(obj: Option<PyObjectRef>) -> Self {
396        let val = obj.map(|x| x.into_raw() as *mut _).unwrap_or(null_mut());
397        Self {
398            inner: Radium::new(val),
399            _phantom: Default::default(),
400        }
401    }
402}
403
404impl PyAtomicRef<Option<PyObject>> {
405    pub fn deref(&self) -> Option<&PyObject> {
406        unsafe {
407            self.inner
408                .load(Ordering::Relaxed)
409                .cast::<PyObject>()
410                .as_ref()
411        }
412    }
413
414    pub fn to_owned(&self) -> Option<PyObjectRef> {
415        self.deref().map(|x| x.to_owned())
416    }
417
418    /// # Safety
419    /// The caller is responsible to keep the returned PyRef alive
420    /// until no more reference can be used via PyAtomicRef::deref()
421    #[must_use]
422    pub unsafe fn swap(&self, obj: Option<PyObjectRef>) -> Option<PyObjectRef> {
423        let val = obj.map(|x| x.into_raw() as *mut _).unwrap_or(null_mut());
424        let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
425        old.cast::<PyObject>()
426            .as_ref()
427            .map(|x| PyObjectRef::from_raw(x))
428    }
429
430    pub fn swap_to_temporary_refs(&self, obj: Option<PyObjectRef>, vm: &VirtualMachine) {
431        let Some(old) = (unsafe { self.swap(obj) }) else {
432            return;
433        };
434        if let Some(frame) = vm.current_frame() {
435            frame.temporary_refs.lock().push(old);
436        }
437    }
438}
439
440pub trait AsObject
441where
442    Self: Borrow<PyObject>,
443{
444    #[inline(always)]
445    fn as_object(&self) -> &PyObject {
446        self.borrow()
447    }
448
449    #[inline(always)]
450    fn get_id(&self) -> usize {
451        self.as_object().unique_id()
452    }
453
454    #[inline(always)]
455    fn is<T>(&self, other: &T) -> bool
456    where
457        T: AsObject,
458    {
459        self.get_id() == other.get_id()
460    }
461
462    #[inline(always)]
463    fn class(&self) -> &Py<PyType> {
464        self.as_object().class()
465    }
466
467    fn get_class_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
468        self.class().get_attr(attr_name)
469    }
470
471    /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only
472    /// use this if `cls` is known to have not overridden the base __instancecheck__ magic method.
473    #[inline]
474    fn fast_isinstance(&self, cls: &Py<PyType>) -> bool {
475        self.class().fast_issubclass(cls)
476    }
477}
478
479impl<T> AsObject for T where T: Borrow<PyObject> {}
480
481impl PyObject {
482    #[inline(always)]
483    fn unique_id(&self) -> usize {
484        self as *const PyObject as usize
485    }
486}
487
488// impl<T: ?Sized> Borrow<PyObject> for PyRc<T> {
489//     #[inline(always)]
490//     fn borrow(&self) -> &PyObject {
491//         unsafe { &*(&**self as *const T as *const PyObject) }
492//     }
493// }
494
495/// A borrow of a reference to a Python object. This avoids having clone the `PyRef<T>`/
496/// `PyObjectRef`, which isn't that cheap as that increments the atomic reference counter.
497// TODO: check if we still need this
498#[allow(dead_code)]
499pub struct PyLease<'a, T: PyObjectPayload> {
500    inner: PyRwLockReadGuard<'a, PyRef<T>>,
501}
502
503impl<'a, T: PyObjectPayload + PyPayload> PyLease<'a, T> {
504    #[inline(always)]
505    pub fn into_owned(self) -> PyRef<T> {
506        self.inner.clone()
507    }
508}
509
510impl<'a, T: PyObjectPayload + PyPayload> Borrow<PyObject> for PyLease<'a, T> {
511    #[inline(always)]
512    fn borrow(&self) -> &PyObject {
513        self.inner.as_ref()
514    }
515}
516
517impl<'a, T: PyObjectPayload + PyPayload> Deref for PyLease<'a, T> {
518    type Target = PyRef<T>;
519    #[inline(always)]
520    fn deref(&self) -> &Self::Target {
521        &self.inner
522    }
523}
524
525impl<'a, T> fmt::Display for PyLease<'a, T>
526where
527    T: PyPayload + fmt::Display,
528{
529    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
530        fmt::Display::fmt(&**self, f)
531    }
532}
533
534impl<T: PyObjectPayload> ToPyObject for PyRef<T> {
535    #[inline(always)]
536    fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
537        self.into()
538    }
539}
540
541impl ToPyObject for PyObjectRef {
542    #[inline(always)]
543    fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
544        self
545    }
546}
547
548impl ToPyObject for &PyObject {
549    #[inline(always)]
550    fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
551        self.to_owned()
552    }
553}
554
555// Allows a built-in function to return any built-in object payload without
556// explicitly implementing `ToPyObject`.
557impl<T> ToPyObject for T
558where
559    T: PyPayload + Sized,
560{
561    #[inline(always)]
562    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
563        PyPayload::into_pyobject(self, vm)
564    }
565}
566
567impl<T> ToPyResult for T
568where
569    T: ToPyObject,
570{
571    #[inline(always)]
572    fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
573        Ok(self.to_pyobject(vm))
574    }
575}
576
577impl<T, E> ToPyResult for Result<T, E>
578where
579    T: ToPyObject,
580    E: IntoPyException,
581{
582    #[inline(always)]
583    fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
584        self.map(|res| T::to_pyobject(res, vm))
585            .map_err(|e| E::into_pyexception(e, vm))
586    }
587}
588
589impl IntoPyException for PyBaseExceptionRef {
590    #[inline(always)]
591    fn into_pyexception(self, _vm: &VirtualMachine) -> PyBaseExceptionRef {
592        self
593    }
594}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.