From c08375baeba22bee4596c9fb7534ed6a39db71e5 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers <7943856+bschoenmaeckers@users.noreply.github.com> Date: Mon, 19 Oct 2026 18:45:53 +0200 Subject: [PATCH] Implement `PyType_FromSpec` & `PyType_FromMetaclass` (#9213) * Implement `PyType_FromSpec` & `PyType_FromMetaclass` * Review --- crates/capi/src/object/pytype.rs | 88 ++++++++++++++++++++++++++++++-- crates/capi/src/slots.rs | 28 ++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/crates/capi/src/object/pytype.rs b/crates/capi/src/object/pytype.rs index 27254dc83..77d8627d0 100644 --- a/crates/capi/src/object/pytype.rs +++ b/crates/capi/src/object/pytype.rs @@ -5,7 +5,8 @@ use crate::pystate::with_vm; use crate::slots::{PySlot, PySlotKind, PySlotType}; use crate::util::CStrExt; use crate::util::FfiPtrExt; -use core::ffi::{c_char, c_int, c_ulong, c_void}; +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}; @@ -22,6 +23,15 @@ pub struct PyType_Slot { pub pfunc: *mut c_void, } +#[repr(C)] +pub struct PyType_Spec { + pub name: *const c_char, + pub basicsize: c_int, + pub itemsize: c_int, + pub flags: c_uint, + pub slots: *mut PyType_Slot, +} + impl PyType_Slot { pub(crate) fn iter<'a>(mut slots: *const Self) -> impl Iterator { core::iter::from_fn(move || { @@ -104,11 +114,11 @@ pub unsafe extern "C" fn PyType_GetFullyQualifiedName(ptr: *mut PyTypeObject) -> #[unsafe(no_mangle)] pub unsafe extern "C" fn PyType_GetSlot(_ty: *const PyTypeObject, _slot: c_int) -> *mut c_void { - core::ptr::null_mut() + ptr::null_mut() } #[unsafe(no_mangle)] -pub extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject { +pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject { with_vm(|vm| { let mut name = None; let mut base = None; @@ -254,6 +264,78 @@ pub extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject { }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyType_FromMetaclass( + metaclass: *mut PyTypeObject, + module: *mut PyObject, + spec: *mut PyType_Spec, + bases: *mut PyObject, +) -> *mut PyObject { + let &PyType_Spec { + name, + basicsize, + itemsize, + flags, + slots: type_slots, + } = unsafe { &*spec }; + let mut slots = [PySlot::SLOT_END; 9]; + let mut write_slot = { + let mut iter = slots.iter_mut(); + move |slot| { + debug_assert!( + iter.len() >= 2, + "PyType_FromMetaclass: last slot is reserved for SLOT_END" + ); + let entry = iter + .next() + .expect("Too many slots for PyType_FromMetaclass"); + *entry = slot; + } + }; + + if !bases.is_null() { + write_slot(PySlot::new(49, bases)); + } + + if !metaclass.is_null() { + write_slot(PySlot::new(107, metaclass)); + } + + if !module.is_null() { + 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()) } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyType_FromModuleAndSpec( + module: *mut PyObject, + spec: *mut PyType_Spec, + bases: *mut PyObject, +) -> *mut PyObject { + unsafe { PyType_FromMetaclass(ptr::null_mut(), module, spec, bases) } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyType_FromSpecWithBases( + spec: *mut PyType_Spec, + bases: *mut PyObject, +) -> *mut PyObject { + unsafe { PyType_FromMetaclass(ptr::null_mut(), ptr::null_mut(), spec, bases) } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyType_FromSpec(spec: *mut PyType_Spec) -> *mut PyObject { + unsafe { PyType_FromMetaclass(ptr::null_mut(), ptr::null_mut(), spec, ptr::null_mut()) } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn PyObject_GetTypeData( obj: *mut PyObject, diff --git a/crates/capi/src/slots.rs b/crates/capi/src/slots.rs index 79b11c7b4..96a4df5a8 100644 --- a/crates/capi/src/slots.rs +++ b/crates/capi/src/slots.rs @@ -20,6 +20,16 @@ impl PySlot { const SLOT_OPTIONAL: u16 = 0x0001; const SLOT_STATIC: u16 = 0x0002; const SLOT_INTPTR: u16 = 0x0004; + pub(crate) const SLOT_END: Self = unsafe { core::mem::zeroed() }; + + pub(crate) fn new(id: u16, value: impl Into) -> Self { + Self { + sl_id: id, + sl_flags: 0, + _reserved: 0, + value: value.into(), + } + } pub(crate) fn iter<'a>(mut slots: *const Self) -> impl Iterator { core::iter::from_fn(move || { @@ -130,6 +140,24 @@ pub union PySlotValue { pub sl_uint64: u64, } +impl From<*mut T> for PySlotValue { + fn from(ptr: *mut T) -> Self { + Self { sl_ptr: ptr.cast() } + } +} + +impl From for PySlotValue { + fn from(size: isize) -> Self { + Self { sl_size: size } + } +} + +impl From for PySlotValue { + fn from(value: u64) -> Self { + Self { sl_uint64: value } + } +} + #[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub(crate) enum PySlotModule<'a> {