forked from RustPython/RustPython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtraverse_object.rs
More file actions
99 lines (91 loc) · 3.48 KB
/
traverse_object.rs
File metadata and controls
99 lines (91 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use alloc::fmt;
use core::any::TypeId;
use crate::{
PyObject, PyObjectRef,
object::{
Erased, InstanceDict, MaybeTraverse, PyInner, PyObjectPayload, debug_obj, default_dealloc,
try_clear_obj, try_traverse_obj,
},
};
use super::{Traverse, TraverseFn};
pub(in crate::object) struct PyObjVTable {
pub(in crate::object) typeid: TypeId,
/// dealloc: handles __del__, weakref clearing, and memory free.
pub(in crate::object) dealloc: unsafe fn(*mut PyObject),
pub(in crate::object) debug: unsafe fn(&PyObject, &mut fmt::Formatter<'_>) -> fmt::Result,
pub(in crate::object) trace: Option<unsafe fn(&PyObject, &mut TraverseFn<'_>)>,
/// Clear for circular reference resolution (tp_clear).
/// Called just before deallocation to extract child references.
pub(in crate::object) clear: Option<unsafe fn(*mut PyObject, &mut Vec<PyObjectRef>)>,
}
impl PyObjVTable {
pub const fn of<T: PyObjectPayload>() -> &'static Self {
&Self {
typeid: T::PAYLOAD_TYPE_ID,
dealloc: default_dealloc::<T>,
debug: debug_obj::<T>,
trace: const {
if T::HAS_TRAVERSE {
Some(try_traverse_obj::<T>)
} else {
None
}
},
clear: const {
if T::HAS_CLEAR {
Some(try_clear_obj::<T>)
} else {
None
}
},
}
}
}
unsafe impl Traverse for InstanceDict {
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
self.d.traverse(tracer_fn)
}
}
unsafe impl Traverse for PyInner<Erased> {
/// Because PyObject hold a `PyInner<Erased>`, so we need to trace it
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
// For heap type instances, traverse the type reference.
// PyAtomicRef holds a strong reference (via PyRef::leak), so GC must
// account for it to correctly detect instance ↔ type cycles.
// Static types are always alive and don't need this.
let typ = &*self.typ;
if typ.heaptype_ext.is_some() {
// Safety: Py<PyType> and PyObject share the same memory layout
let typ_obj: &PyObject = unsafe { &*(typ as *const _ as *const PyObject) };
tracer_fn(typ_obj);
}
// Traverse ObjExt prefix fields (dict and slots) if present
if let Some(ext) = self.ext_ref() {
ext.dict.traverse(tracer_fn);
ext.slots.traverse(tracer_fn);
}
if let Some(f) = self.vtable.trace {
unsafe {
let zelf = &*(self as *const Self as *const PyObject);
f(zelf, tracer_fn)
}
};
}
}
unsafe impl<T: MaybeTraverse> Traverse for PyInner<T> {
/// Type is known, so we can call `try_trace` directly instead of using erased type vtable
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
// For heap type instances, traverse the type reference (same as erased version)
let typ = &*self.typ;
if typ.heaptype_ext.is_some() {
let typ_obj: &PyObject = unsafe { &*(typ as *const _ as *const PyObject) };
tracer_fn(typ_obj);
}
// Traverse ObjExt prefix fields (dict and slots) if present
if let Some(ext) = self.ext_ref() {
ext.dict.traverse(tracer_fn);
ext.slots.traverse(tracer_fn);
}
T::try_traverse(&self.payload, tracer_fn);
}
}