Skip to content

Commit

Permalink
(Failing) Set dictoffset and weaklistoffset by Py_tp_members
Browse files Browse the repository at this point in the history
  • Loading branch information
kngwyu committed Sep 6, 2020
1 parent 8624306 commit bcceb24
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 17 deletions.
36 changes: 33 additions & 3 deletions src/ffi/structmember.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,43 @@ use crate::ffi::pyport::Py_ssize_t;
use std::os::raw::{c_char, c_int};

#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct PyMemberDef {
pub name: *mut c_char,
pub name: *const c_char,
pub type_code: c_int,
pub offset: Py_ssize_t,
pub flags: c_int,
pub doc: *mut c_char,
pub doc: *const c_char,
}

impl PyMemberDef {
pub const fn init() -> Self {
Self {
name: std::ptr::null(),
type_code: 0,
offset: 0,
flags: 0,
doc: std::ptr::null(),
}
}
pub(crate) const fn dict_offset(offset: Py_ssize_t) -> Self {
Self {
name: "__dictoffset__\0".as_ptr() as _,
type_code: T_PYSSIZET,
offset: offset,
flags: READONLY,
doc: std::ptr::null(),
}
}
pub(crate) fn weakref_offset(offset: Py_ssize_t) -> Self {
Self {
name: "__weaklistoffset__\0".as_ptr() as _,
type_code: T_PYSSIZET,
offset: offset,
flags: READONLY,
doc: std::ptr::null(),
}
}
}

/* Types */
Expand Down
36 changes: 22 additions & 14 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::class::methods::{PyClassAttributeDef, PyMethodDefType, PyMethods};
use crate::class::proto_methods::PyProtoMethods;
use crate::conversion::{AsPyPointer, FromPyPointer};
use crate::derive_utils::PyBaseTypeUtils;
use crate::ffi::structmember::PyMemberDef;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{type_flags, PyLayout};
use crate::types::PyAny;
Expand Down Expand Up @@ -168,6 +169,12 @@ where
slots.push(ffi::Py_tp_getset, into_raw(props));
}

// members
let members = py_class_members::<T>();
if !members.is_empty() {
slots.push(ffi::Py_tp_members, into_raw(members));
}

// basic methods
T::basic_methods().map(|basic| unsafe { basic.as_ref() }.update_slots(&mut slots));
// number methods
Expand Down Expand Up @@ -205,32 +212,33 @@ where

#[cfg(not(Py_LIMITED_API))]
fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
// Just patch the type objects for the things there's no
// PyType_FromSpec API for... there's no reason this should work,
// except for that it does and we have tests.
if let Some(buffer) = T::buffer_methods() {
unsafe {
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.as_ref().bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.as_ref().bf_releasebuffer;
}
}
}

#[cfg(Py_LIMITED_API)]
fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {}

fn py_class_members<T: PyClass>() -> Vec<PyMemberDef> {
let mut members = vec![];
// __dict__ support
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
unsafe {
(*type_object).tp_dictoffset = dict_offset as ffi::Py_ssize_t;
}
if let Some(offset) = PyCell::<T>::dict_offset() {
members.push(PyMemberDef::dict_offset(offset as _));
}
// weakref support
if let Some(weakref_offset) = PyCell::<T>::weakref_offset() {
unsafe {
(*type_object).tp_weaklistoffset = weakref_offset as ffi::Py_ssize_t;
}
if let Some(offset) = PyCell::<T>::weakref_offset() {
members.push(PyMemberDef::weakref_offset(offset as _));
}
if !members.is_empty() {
members.push(PyMemberDef::init());
}
members
}

#[cfg(Py_LIMITED_API)]
fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {}

fn py_class_flags<T: PyClass + PyTypeInfo>() -> c_uint {
let mut flags = if T::gc_methods().is_some() || T::FLAGS & type_flags::GC != 0 {
ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC
Expand Down

0 comments on commit bcceb24

Please sign in to comment.