Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 43 additions & 88 deletions crates/capi/src/object/pytype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use core::ffi::{c_char, c_int, c_uint, c_ulong, c_void};
use core::ptr;
use rustpython_vm::builtins::{PyStr, PyType};
use rustpython_vm::function::{ItemDoc, PyMethodFlags};
use rustpython_vm::types::{AtomicPyTypeFlags, PyTypeFlags, PyTypeSlots, SlotAccessor};
use rustpython_vm::types::{
AtomicPyTypeFlags, PyTypeFlags, PyTypeSlots,
SlotAccessor::{TpBase, TpDealloc, TpDoc, TpGetset, TpMembers, TpMethods, TpNew, TpTraverse},
};
use rustpython_vm::{AsObject, Py, PyObject};

pub type PyTypeObject = Py<PyType>;
Expand All @@ -32,25 +35,6 @@ pub struct PyType_Spec {
pub slots: *mut PyType_Slot,
}

impl PyType_Slot {
pub(crate) fn iter<'a>(mut slots: *const Self) -> impl Iterator<Item = &'a Self> {
core::iter::from_fn(move || {
let slot = unsafe { &*slots };
if slot.slot == 0 {
None
} else {
slots = unsafe { slots.add(1) };
Some(slot)
}
})
}

fn as_slot_accessor(&self) -> Option<SlotAccessor> {
let slot_id: u8 = self.slot.try_into().ok()?;
slot_id.try_into().ok()
}
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn Py_TYPE(op: *mut PyObject) -> *const PyTypeObject {
unsafe { op.assume_borrowed() }.class()
Expand Down Expand Up @@ -127,12 +111,15 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
let attrs = Default::default();
let mut getsets = Vec::new();
let mut members = Vec::new();
let mut metaclass = vm.ctx.types.type_type.to_owned();

for slot in PySlot::iter(slots) {
match slot.as_kind(vm)? {
kind @ PySlotKind::Type(type_slot) => {
PySlotKind::Type(type_slot) => {
match type_slot {
PySlotType::Name(value) => name = Some(value),
PySlotType::Metaclass(meta) => metaclass = meta.to_owned(),
Comment thread
bschoenmaeckers marked this conversation as resolved.
PySlotType::Module(_) => {}
PySlotType::Flags(value) => {
let flags = PyTypeFlags::from_bits(value).ok_or_else(|| {
vm.new_value_error(format!(
Expand All @@ -148,68 +135,37 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
));
}
}
PySlotType::Slots { value, .. } => {
for slot in PyType_Slot::iter(value) {
let accessor = slot.as_slot_accessor().ok_or_else(|| {
vm.new_value_error(format!(
"Invalid slot id: {} for PyType_FromSlots",
slot.slot
))
})?;
match accessor {
SlotAccessor::TpDoc => {
let doc = unsafe {
slot.pfunc.cast::<c_char>().try_as_str_opt(vm)?
}
.map_or(
ItemDoc::NONE,
|doc| {
let text: &'static str =
Box::leak(doc.to_owned().into_boxed_str());
ItemDoc::static_text(text)
},
);
type_slots.doc = doc;
}
SlotAccessor::TpNew => {
type_slots.new.store(Some(|ty, _args, vm| {
Err(vm.new_not_implemented_error(format!("tp_new is not yet implemented in PyType_FromSlots for {ty:?}")))
}));
}
SlotAccessor::TpBase => {
base = unsafe { Some(&*slot.pfunc.cast::<PyTypeObject>()) }
}
SlotAccessor::TpDealloc => {
//TODO
}
SlotAccessor::TpMethods => {
for def in PyMethodDef::iter(slot.pfunc.cast()) {
let name = unsafe { def.ml_name.try_as_str(vm)? };
let is_static =
PyMethodFlags::from_bits_retain(def.ml_flags as _)
.contains(PyMethodFlags::STATIC);
let method = build_method_def(vm, def, !is_static)?;
methods.push((name, method));
}
}
SlotAccessor::TpGetset => {
getsets.extend(PyGetSetDef::iter(slot.pfunc.cast()));
}
SlotAccessor::TpMembers => {
members.extend(PyMemberDef::iter(slot.pfunc.cast()));
}
slot => {
return Err(vm.new_not_implemented_error(format!(
"PyType_FromSlots with PyType_Slot {slot:?} not implemented yet"
)));
}
}
PySlotType::TypeSlot(TpDoc, pfunc) => {
let doc = unsafe { pfunc.cast::<c_char>().try_as_str_opt(vm)? }.map_or(
ItemDoc::NONE,
|doc| {
let text: &'static str =
Box::leak(doc.to_owned().into_boxed_str());
ItemDoc::static_text(text)
},
);
type_slots.doc = doc;
}
PySlotType::TypeSlot(TpBase, pfunc) => {
base = unsafe { Some(&*pfunc.cast::<PyTypeObject>()) }
}
PySlotType::TypeSlot(TpMembers, pfunc) => {
members.extend(PyMemberDef::iter(pfunc.cast()));
}
PySlotType::TypeSlot(TpGetset, pfunc) => {
getsets.extend(PyGetSetDef::iter(pfunc.cast()));
}
PySlotType::TypeSlot(TpMethods, pfunc) => {
for def in PyMethodDef::iter(pfunc.cast()) {
let name = unsafe { def.ml_name.try_as_str(vm)? };
let is_static = PyMethodFlags::from_bits_retain(def.ml_flags as _)
.contains(PyMethodFlags::STATIC);
let method = build_method_def(vm, def, !is_static)?;
methods.push((name, method));
}
}
_ => {
return Err(vm.new_not_implemented_error(format!(
"PyType_FromSlots with slot {kind:?} not implemented yet"
)));
PySlotType::TypeSlot(TpNew | TpDealloc | TpTraverse | _, _) => {
Comment thread
bschoenmaeckers marked this conversation as resolved.
Comment thread
bschoenmaeckers marked this conversation as resolved.
Comment thread
bschoenmaeckers marked this conversation as resolved.
// TODO
}
}
}
Expand All @@ -232,7 +188,6 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
return Err(vm.new_system_error("PyType_FromSlots requires a name slot"));
};

let metaclass = vm.ctx.types.type_type.to_owned();
let class = PyType::new_heap(name, bases, attrs, type_slots, metaclass, &vm.ctx).map_err(
|msg| vm.new_system_error(format!("Failed to create type from slots: {msg}")),
)?;
Expand Down Expand Up @@ -293,6 +248,12 @@ pub unsafe extern "C" fn PyType_FromMetaclass(
}
};

write_slot(PySlot::new(95, name.cast_mut()));
write_slot(PySlot::new(96, basicsize as isize));
write_slot(PySlot::new(98, itemsize as isize));
write_slot(PySlot::new(99, flags as u64));
write_slot(PySlot::new(93, type_slots));

if !bases.is_null() {
write_slot(PySlot::new(49, bases));
}
Expand All @@ -305,12 +266,6 @@ pub unsafe extern "C" fn PyType_FromMetaclass(
write_slot(PySlot::new(108, module));
}

write_slot(PySlot::new(95, name.cast_mut()));
write_slot(PySlot::new(96, basicsize as isize));
write_slot(PySlot::new(98, itemsize as isize));
write_slot(PySlot::new(99, flags as u64));
write_slot(PySlot::new(93, type_slots));

unsafe { PyType_FromSlots(slots.as_ptr()) }
}

Expand Down
99 changes: 77 additions & 22 deletions crates/capi/src/slots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,36 @@ use crate::methodobject::PyMethodDef;
use crate::modsupport::PyABIInfo;
use crate::object::PyType_Slot;
use crate::util::CStrExt;
use alloc::borrow::Cow;
use core::ffi::{c_char, c_int, c_void};
use core::slice::from_ref;
use core::num::TryFromIntError;
use itertools::Either;
use rustpython_vm::builtins::{PyModule, PyType};
use rustpython_vm::types::SlotAccessor;
use rustpython_vm::{Py, PyResult, VirtualMachine};

#[repr(C)]
#[derive(Clone)]
pub struct PySlot {
pub sl_id: u16,
pub sl_flags: u16,
_reserved: u32,
pub value: PySlotValue,
}

impl TryFrom<&PyType_Slot> for PySlot {
type Error = TryFromIntError;

fn try_from(slot: &PyType_Slot) -> Result<Self, Self::Error> {
Ok(Self {
sl_id: slot.slot.try_into()?,
Comment thread
bschoenmaeckers marked this conversation as resolved.
sl_flags: 0,
_reserved: 0,
value: slot.pfunc.into(),
})
}
}

impl PySlot {
const SLOT_OPTIONAL: u16 = 0x0001;
const SLOT_STATIC: u16 = 0x0002;
Expand All @@ -31,14 +48,55 @@ impl PySlot {
}
}

pub(crate) fn iter<'a>(mut slots: *const Self) -> impl Iterator<Item = &'a Self> {
pub(crate) fn iter<'a>(slots: *const Self) -> impl Iterator<Item = Cow<'a, Self>> {
let mut stack: Vec<Either<*const Self, *const PyType_Slot>> = vec![Either::Left(slots)];
core::iter::from_fn(move || {
let slot = unsafe { &*slots };
if slot.sl_id == 0 {
None
} else {
slots = unsafe { slots.add(1) };
Some(slot)
loop {
assert!(
stack.len() <= 10,
"Too many nested slots, possible infinite loop in slot iteration"
);
return match stack.last_mut()? {
Either::Left(slot_ptr) => {
let slot = unsafe { &**slot_ptr };
if slot.sl_id == 0 {
stack.pop();
continue;
}

*slot_ptr = unsafe { slot_ptr.add(1) };

if slot.sl_id == 92 {
let subslots = unsafe { slot.value.sl_ptr }.cast_const().cast::<Self>();
if !subslots.is_null() {
stack.push(Either::Left(subslots));
}
continue;
}

if slot.sl_id == 93 {
let type_slots = unsafe { slot.value.sl_ptr }
.cast_const()
.cast::<PyType_Slot>();
if !type_slots.is_null() {
stack.push(Either::Right(type_slots));
}
continue;
}

Some(Cow::Borrowed(slot))
}
Either::Right(slot_ptr) => {
let type_slot = unsafe { &**slot_ptr };
if type_slot.slot == 0 {
stack.pop();
continue;
}

*slot_ptr = unsafe { slot_ptr.add(1) };
Some(Cow::Owned(type_slot.try_into().expect("invalid slot")))
}
Comment thread
bschoenmaeckers marked this conversation as resolved.
};
}
})
}
Expand All @@ -58,12 +116,15 @@ impl PySlot {
self.sl_flags & Self::SLOT_INTPTR != 0
}

pub(crate) fn as_kind(&self, vm: &VirtualMachine) -> PyResult<PySlotKind<'_>> {
pub(crate) fn as_kind<'a>(&self, vm: &VirtualMachine) -> PyResult<PySlotKind<'a>> {
let value_ptr = unsafe { self.value.sl_ptr };
let is_static = self.is_static();
let kind = match self.sl_id {
48 => PySlotKind::Type(PySlotType::Base(unsafe { &*value_ptr.cast() })),
49 => PySlotKind::Type(PySlotType::Bases(unsafe { from_ref(&*value_ptr.cast()) })),
1..82 => {
let slot_id: u8 = self.sl_id.try_into().unwrap();
let accessor = slot_id.try_into().unwrap();
PySlotKind::Type(PySlotType::TypeSlot(accessor, value_ptr))
Comment thread
bschoenmaeckers marked this conversation as resolved.
}
84 => {
let create = unsafe {
core::mem::transmute::<
Expand All @@ -89,11 +150,9 @@ impl PySlot {
87 => PySlotKind::Module(PySlotModule::Gil {
gil_used: !value_ptr.is_null(),
}),
// 92 => Py_slot_subslots
93 => PySlotKind::Type(PySlotType::Slots {
value: value_ptr.cast(),
is_static,
}),
92 | 93 => {
unreachable!("Py_slot_subslots and Py_tp_slots should be handled in the iterator")
}
95 => PySlotKind::Type(PySlotType::Name(unsafe {
value_ptr.cast::<c_char>().try_as_str(vm)?
})),
Expand Down Expand Up @@ -132,6 +191,7 @@ impl PySlot {
}

#[repr(C)]
#[derive(Copy, Clone)]
pub union PySlotValue {
pub sl_ptr: *mut c_void,
pub sl_func: unsafe extern "C" fn(),
Expand Down Expand Up @@ -174,12 +234,7 @@ pub(crate) enum PySlotModule<'a> {
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
pub(crate) enum PySlotType<'a> {
Base(&'a Py<PyType>),
Bases(&'a [Py<PyType>]),
Slots {
value: *mut PyType_Slot,
is_static: bool,
},
TypeSlot(SlotAccessor, *mut c_void),
Name(&'a str),
Flags(u64),
BasicSize(isize),
Expand Down
Loading