mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Flatten Py_slot_subslots and Py_tp_slots in PySlot iterator (#9246)
* Flatten `Py_slot_subslots` and `Py_tp_slots` in `PySlot` iterator * Review * Add support for metaclass slots
This commit is contained in:
1 parent
ce2ef3317f
commit
fb9c099511
2 files changed
+120
-110
No files matched your search
@@ -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>;
|
||||
@@ -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()
|
||||
@@ -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(),
|
||||
PySlotType::Module(_) => {}
|
||||
PySlotType::Flags(value) => {
|
||||
let flags = PyTypeFlags::from_bits(value).ok_or_else(|| {
|
||||
vm.new_value_error(format!(
|
||||
@@ -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 | _, _) => {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}")),
|
||||
)?;
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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()) }
|
||||
}
|
||||
|
||||
|
||||
+77
-22
@@ -3,12 +3,16 @@ 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,
|
||||
@@ -16,6 +20,19 @@ pub struct PySlot {
|
||||
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()?,
|
||||
sl_flags: 0,
|
||||
_reserved: 0,
|
||||
value: slot.pfunc.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PySlot {
|
||||
const SLOT_OPTIONAL: u16 = 0x0001;
|
||||
const SLOT_STATIC: u16 = 0x0002;
|
||||
@@ -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")))
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
84 => {
|
||||
let create = unsafe {
|
||||
core::mem::transmute::<
|
||||
@@ -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)?
|
||||
})),
|
||||
@@ -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(),
|
||||
@@ -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),
|
||||
|
||||
Reference in new issue
Block a user