Skip to content

Commit d8f64fb

Browse files
committed
allow heap-getset
1 parent 6f97a98 commit d8f64fb

File tree

6 files changed

+42
-14
lines changed

6 files changed

+42
-14
lines changed

stdlib/src/pyexpat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
1818

1919
macro_rules! create_property {
2020
($ctx: expr, $attributes: expr, $name: expr, $class: expr, $element: ident) => {
21-
let attr = $ctx.new_getset(
21+
let attr = $ctx.new_static_getset(
2222
$name,
2323
$class,
2424
move |this: &PyExpatLikeXmlParser| this.$element.read().clone(),

vm/src/builtins/getset.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use super::PyType;
55
use crate::{
66
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
7+
builtins::type_::PointerSlot,
78
class::PyClassImpl,
89
function::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc, PySetterValue},
910
types::{GetDescriptor, Unconstructible},
@@ -12,7 +13,7 @@ use crate::{
1213
#[pyclass(module = false, name = "getset_descriptor")]
1314
pub struct PyGetSet {
1415
name: String,
15-
class: &'static Py<PyType>,
16+
class: PointerSlot<Py<PyType>>, // A class type freed before getset is non-sense.
1617
getter: Option<PyGetterFunc>,
1718
setter: Option<PySetterFunc>,
1819
// doc: Option<String>,
@@ -72,7 +73,7 @@ impl PyGetSet {
7273
pub fn new(name: String, class: &'static Py<PyType>) -> Self {
7374
Self {
7475
name,
75-
class,
76+
class: PointerSlot::from(class),
7677
getter: None,
7778
setter: None,
7879
}
@@ -138,13 +139,17 @@ impl PyGetSet {
138139

139140
#[pygetset]
140141
fn __qualname__(&self) -> String {
141-
format!("{}.{}", self.class.slot_name(), self.name.clone())
142+
format!(
143+
"{}.{}",
144+
unsafe { self.class.borrow_static() }.slot_name(),
145+
self.name.clone()
146+
)
142147
}
143148

144149
#[pymember]
145150
fn __objclass__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
146151
let zelf: &Py<Self> = zelf.try_to_value(vm)?;
147-
Ok(zelf.class.to_owned().into())
152+
Ok(unsafe { zelf.class.borrow_static() }.to_owned().into())
148153
}
149154
}
150155
impl Unconstructible for PyGetSet {}

vm/src/builtins/property.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ pub(crate) fn init(context: &Context) {
386386
// This is a bit unfortunate, but this instance attribute overlaps with the
387387
// class __doc__ string..
388388
extend_class!(context, context.types.property_type, {
389-
"__doc__" => context.new_getset(
389+
"__doc__" => context.new_static_getset(
390390
"__doc__",
391391
context.types.property_type,
392392
PyProperty::doc_getter,

vm/src/builtins/type.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
};
3232
use indexmap::{IndexMap, map::Entry};
3333
use itertools::Itertools;
34-
use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref, pin::Pin, ptr::NonNull};
34+
use std::{borrow::Borrow, collections::HashSet, ops::Deref, pin::Pin, ptr::NonNull};
3535

3636
#[pyclass(module = false, name = "type", traverse = "manual")]
3737
pub struct PyType {
@@ -69,6 +69,9 @@ pub struct HeapTypeExt {
6969

7070
pub struct PointerSlot<T>(NonNull<T>);
7171

72+
unsafe impl<T> Sync for PointerSlot<T> {}
73+
unsafe impl<T> Send for PointerSlot<T> {}
74+
7275
impl<T> PointerSlot<T> {
7376
pub unsafe fn borrow_static(&self) -> &'static T {
7477
unsafe { self.0.as_ref() }
@@ -126,14 +129,14 @@ unsafe impl Traverse for PyAttributes {
126129
}
127130
}
128131

129-
impl fmt::Display for PyType {
130-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131-
fmt::Display::fmt(&self.name(), f)
132+
impl std::fmt::Display for PyType {
133+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134+
std::fmt::Display::fmt(&self.name(), f)
132135
}
133136
}
134137

135-
impl fmt::Debug for PyType {
136-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138+
impl std::fmt::Debug for PyType {
139+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137140
write!(f, "[PyType {}]", &self.name())
138141
}
139142
}

vm/src/class.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub trait PyClassImpl: PyClassDef {
8585
let __dict__ = identifier!(ctx, __dict__);
8686
class.set_attr(
8787
__dict__,
88-
ctx.new_getset(
88+
ctx.new_static_getset(
8989
"__dict__",
9090
class,
9191
crate::builtins::object::object_get_dict,

vm/src/vm/context.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ impl Context {
570570
PyRef::new_ref(getset, self.types.getset_type.to_owned(), None)
571571
}
572572

573-
pub fn new_getset<G, S, T, U>(
573+
pub fn new_static_getset<G, S, T, U>(
574574
&self,
575575
name: impl Into<String>,
576576
class: &'static Py<PyType>,
@@ -586,6 +586,26 @@ impl Context {
586586
PyRef::new_ref(getset, self.types.getset_type.to_owned(), None)
587587
}
588588

589+
/// Creates a new `PyGetSet` with a heap type.
590+
///
591+
/// # Safety
592+
/// In practice, this constructor is safe because a getset is always owned by its `class` type.
593+
/// However, it can be broken if used unconventionally.
594+
pub unsafe fn new_getset<G, S, T, U>(
595+
&self,
596+
name: impl Into<String>,
597+
class: &Py<PyType>,
598+
g: G,
599+
s: S,
600+
) -> PyRef<PyGetSet>
601+
where
602+
G: IntoPyGetterFunc<T>,
603+
S: IntoPySetterFunc<U>,
604+
{
605+
let class = unsafe { &*(class as *const _) };
606+
self.new_static_getset(name, class, g, s)
607+
}
608+
589609
pub fn new_base_object(&self, class: PyTypeRef, dict: Option<PyDictRef>) -> PyObjectRef {
590610
debug_assert_eq!(
591611
class.slots.flags.contains(PyTypeFlags::HAS_DICT),

0 commit comments

Comments
 (0)