* HeapTypeExt::type_data

* Apply TypeDataSlot to ctypes
This commit is contained in:
Jeong, YunWon
2025-12-12 07:57:31 +09:00
committed by GitHub
parent bfd873a8b8
commit d34b2cf8f0
9 changed files with 345 additions and 177 deletions

View File

@@ -26,12 +26,13 @@ use crate::{
protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
types::{
AsNumber, Callable, Constructor, GetAttr, PyTypeFlags, PyTypeSlots, Representable, SetAttr,
TypeDataRef, TypeDataRefMut, TypeDataSlot,
},
};
use indexmap::{IndexMap, map::Entry};
use itertools::Itertools;
use num_traits::ToPrimitive;
use std::{borrow::Borrow, collections::HashSet, ops::Deref, pin::Pin, ptr::NonNull};
use std::{any::Any, borrow::Borrow, collections::HashSet, ops::Deref, pin::Pin, ptr::NonNull};
#[pyclass(module = false, name = "type", traverse = "manual")]
pub struct PyType {
@@ -65,6 +66,7 @@ pub struct HeapTypeExt {
pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
pub sequence_methods: PySequenceMethods,
pub mapping_methods: PyMappingMethods,
pub type_data: PyRwLock<Option<TypeDataSlot>>,
}
pub struct PointerSlot<T>(NonNull<T>);
@@ -203,6 +205,7 @@ impl PyType {
slots: None,
sequence_methods: PySequenceMethods::default(),
mapping_methods: PyMappingMethods::default(),
type_data: PyRwLock::new(None),
};
let base = bases[0].clone();
@@ -563,6 +566,50 @@ impl PyType {
|ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
)
}
// Type Data Slot API - CPython's PyObject_GetTypeData equivalent
/// Initialize type data for this type. Can only be called once.
/// Returns an error if the type is not a heap type or if data is already initialized.
pub fn init_type_data<T: Any + Send + Sync + 'static>(&self, data: T) -> Result<(), String> {
let ext = self
.heaptype_ext
.as_ref()
.ok_or_else(|| "Cannot set type data on non-heap types".to_string())?;
let mut type_data = ext.type_data.write();
if type_data.is_some() {
return Err("Type data already initialized".to_string());
}
*type_data = Some(TypeDataSlot::new(data));
Ok(())
}
/// Get a read guard to the type data.
/// Returns None if the type is not a heap type, has no data, or the data type doesn't match.
pub fn get_type_data<T: Any + 'static>(&self) -> Option<TypeDataRef<'_, T>> {
self.heaptype_ext
.as_ref()
.and_then(|ext| TypeDataRef::try_new(ext.type_data.read()))
}
/// Get a write guard to the type data.
/// Returns None if the type is not a heap type, has no data, or the data type doesn't match.
pub fn get_type_data_mut<T: Any + 'static>(&self) -> Option<TypeDataRefMut<'_, T>> {
self.heaptype_ext
.as_ref()
.and_then(|ext| TypeDataRefMut::try_new(ext.type_data.write()))
}
/// Check if this type has type data of the given type.
pub fn has_type_data<T: Any + 'static>(&self) -> bool {
self.heaptype_ext.as_ref().is_some_and(|ext| {
ext.type_data
.read()
.as_ref()
.is_some_and(|slot| slot.get::<T>().is_some())
})
}
}
impl Py<PyType> {
@@ -1167,6 +1214,7 @@ impl Constructor for PyType {
slots: heaptype_slots.clone(),
sequence_methods: PySequenceMethods::default(),
mapping_methods: PyMappingMethods::default(),
type_data: PyRwLock::new(None),
};
(slots, heaptype_ext)
};

View File

@@ -389,27 +389,28 @@ pub(crate) mod _ctypes {
/// Get the size of a ctypes type or instance
#[pyfunction(name = "sizeof")]
pub fn size_of(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
use super::array::{PyCArray, PyCArrayType};
use super::pointer::PyCPointer;
use super::structure::{PyCStructType, PyCStructure};
use super::union::{PyCUnion, PyCUnionType};
use super::union::PyCUnionType;
use super::util::StgInfo;
use crate::builtins::PyType;
// 1. Instances with stg_info
if obj.fast_isinstance(PyCArray::static_type()) {
// Get stg_info from the type
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCArrayType>() {
return Ok(type_obj.stg_info.size);
}
// 1. Check TypeDataSlot on class (for instances)
if let Some(stg_info) = obj.class().get_type_data::<StgInfo>() {
return Ok(stg_info.size);
}
// 2. Check TypeDataSlot on type itself (for type objects)
if let Some(type_obj) = obj.downcast_ref::<PyType>()
&& let Some(stg_info) = type_obj.get_type_data::<StgInfo>()
{
return Ok(stg_info.size);
}
// 3. Instances with cdata buffer
if let Some(structure) = obj.downcast_ref::<PyCStructure>() {
return Ok(structure.cdata.read().size());
}
if obj.fast_isinstance(PyCUnion::static_type()) {
// Get stg_info from the type
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCUnionType>() {
return Ok(type_obj.stg_info.size);
}
}
if let Some(simple) = obj.downcast_ref::<PyCSimple>() {
return Ok(simple.cdata.read().size());
}
@@ -417,11 +418,6 @@ pub(crate) mod _ctypes {
return Ok(std::mem::size_of::<usize>());
}
// 2. Types (metatypes with stg_info)
if let Some(array_type) = obj.downcast_ref::<PyCArrayType>() {
return Ok(array_type.stg_info.size);
}
// 3. Type objects
if let Ok(type_ref) = obj.clone().downcast::<crate::builtins::PyType>() {
// Structure types - check if metaclass is or inherits from PyCStructType
@@ -659,33 +655,37 @@ pub(crate) mod _ctypes {
#[pyfunction]
fn alignment(tp: Either<PyTypeRef, PyObjectRef>, vm: &VirtualMachine) -> PyResult<usize> {
use super::array::{PyCArray, PyCArrayType};
use super::base::PyCSimpleType;
use super::pointer::PyCPointer;
use super::structure::PyCStructure;
use super::union::PyCUnion;
use super::util::StgInfo;
use crate::builtins::PyType;
let obj = match &tp {
Either::A(t) => t.as_object(),
Either::B(o) => o.as_ref(),
};
// Try to get alignment from stg_info directly (for instances)
if let Some(array_type) = obj.downcast_ref::<PyCArrayType>() {
return Ok(array_type.stg_info.align);
// 1. Check TypeDataSlot on class (for instances)
if let Some(stg_info) = obj.class().get_type_data::<StgInfo>() {
return Ok(stg_info.align);
}
// 2. Check TypeDataSlot on type itself (for type objects)
if let Some(type_obj) = obj.downcast_ref::<PyType>()
&& let Some(stg_info) = type_obj.get_type_data::<StgInfo>()
{
return Ok(stg_info.align);
}
// 3. Fallback for simple types without TypeDataSlot
if obj.fast_isinstance(PyCSimple::static_type()) {
// Get stg_info from the type by reading _type_ attribute
let cls = obj.class().to_owned();
let stg_info = PyCSimpleType::get_stg_info(&cls, vm);
return Ok(stg_info.align);
}
if obj.fast_isinstance(PyCArray::static_type()) {
// Get stg_info from the type
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCArrayType>() {
return Ok(type_obj.stg_info.align);
}
}
if obj.fast_isinstance(PyCStructure::static_type()) {
// Calculate alignment from _fields_
let cls = obj.class();

View File

@@ -1,13 +1,13 @@
use crate::atomic_func;
use crate::builtins::{PyBytes, PyInt};
use crate::convert::ToPyObject;
use crate::class::StaticType;
use crate::function::FuncArgs;
use crate::protocol::{
BufferDescriptor, BufferMethods, PyBuffer, PyNumberMethods, PySequenceMethods,
};
use crate::stdlib::ctypes::base::CDataObject;
use crate::stdlib::ctypes::util::StgInfo;
use crate::types::{AsBuffer, AsNumber, AsSequence, Callable};
use crate::types::{AsBuffer, AsNumber, AsSequence};
use crate::{AsObject, Py, PyObjectRef, PyPayload};
use crate::{
PyResult, VirtualMachine,
@@ -20,56 +20,49 @@ use rustpython_common::lock::PyRwLock;
use rustpython_vm::stdlib::ctypes::_ctypes::get_size;
use rustpython_vm::stdlib::ctypes::base::PyCData;
/// PyCArrayType - metatype for Array types
/// CPython stores array info (type, length) in StgInfo via type_data
#[pyclass(name = "PyCArrayType", base = PyType, module = "_ctypes")]
#[derive(PyPayload)]
pub struct PyCArrayType {
pub(super) stg_info: StgInfo,
pub(super) typ: PyRwLock<PyObjectRef>,
pub(super) length: AtomicCell<usize>,
pub(super) element_size: AtomicCell<usize>,
}
#[derive(Debug, Default, PyPayload)]
pub struct PyCArrayType {}
impl std::fmt::Debug for PyCArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyCArrayType")
.field("typ", &self.typ)
.field("length", &self.length)
.finish()
}
}
/// Create a new Array type with StgInfo stored in type_data (CPython style)
pub fn create_array_type_with_stg_info(stg_info: StgInfo, vm: &VirtualMachine) -> PyResult {
// Get PyCArrayType as metaclass
let metaclass = PyCArrayType::static_type().to_owned();
impl Callable for PyCArrayType {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult {
// Create an instance of the array
let element_type = zelf.typ.read().clone();
let length = zelf.length.load();
let element_size = zelf.element_size.load();
let total_size = element_size * length;
let mut buffer = vec![0u8; total_size];
// Create a unique name for the array type
let type_name = format!("Array_{}", stg_info.length);
// Initialize from positional arguments
for (i, value) in args.args.iter().enumerate() {
if i >= length {
break;
}
let offset = i * element_size;
if let Ok(int_val) = value.try_int(vm) {
let bytes = PyCArray::int_to_bytes(int_val.as_bigint(), element_size);
if offset + element_size <= buffer.len() {
buffer[offset..offset + element_size].copy_from_slice(&bytes);
}
}
// Create args for type(): (name, bases, dict)
let name = vm.ctx.new_str(type_name);
let bases = vm
.ctx
.new_tuple(vec![PyCArray::static_type().to_owned().into()]);
let dict = vm.ctx.new_dict();
let args = FuncArgs::new(
vec![name.into(), bases.into(), dict.into()],
crate::function::KwArgs::default(),
);
// Create the new type using PyType::slot_new with PyCArrayType as metaclass
let new_type = crate::builtins::type_::PyType::slot_new(metaclass, args, vm)?;
// Set StgInfo in type_data
let type_ref: PyTypeRef = new_type
.clone()
.downcast()
.map_err(|_| vm.new_type_error("Failed to create array type".to_owned()))?;
if type_ref.init_type_data(stg_info.clone()).is_err() {
// Type data already initialized - update it
if let Some(mut existing) = type_ref.get_type_data_mut::<StgInfo>() {
*existing = stg_info;
}
Ok(PyCArray {
typ: PyRwLock::new(element_type),
length: AtomicCell::new(length),
element_size: AtomicCell::new(element_size),
cdata: PyRwLock::new(CDataObject::from_bytes(buffer, None)),
}
.into_pyobject(vm))
}
Ok(new_type)
}
impl Constructor for PyCArrayType {
@@ -80,54 +73,62 @@ impl Constructor for PyCArrayType {
}
}
#[pyclass(flags(IMMUTABLETYPE), with(Callable, Constructor, AsNumber))]
#[pyclass(flags(IMMUTABLETYPE), with(Constructor, AsNumber))]
impl PyCArrayType {
#[pygetset(name = "_type_")]
fn typ(&self) -> PyObjectRef {
self.typ.read().clone()
fn typ(zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
zelf.downcast_ref::<PyType>()
.and_then(|t| t.get_type_data::<StgInfo>())
.and_then(|stg| stg.element_type.clone())
.unwrap_or_else(|| vm.ctx.none())
}
#[pygetset(name = "_length_")]
fn length(&self) -> usize {
self.length.load()
fn length(zelf: PyObjectRef) -> usize {
zelf.downcast_ref::<PyType>()
.and_then(|t| t.get_type_data::<StgInfo>())
.map(|stg| stg.length)
.unwrap_or(0)
}
#[pymethod]
fn __mul__(zelf: &Py<Self>, n: isize, vm: &VirtualMachine) -> PyResult {
fn __mul__(zelf: PyObjectRef, n: isize, vm: &VirtualMachine) -> PyResult {
if n < 0 {
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
}
// Create a nested array type: (inner_type * inner_length) * n
// The new array has n elements, each element is the current array type
// e.g., (c_int * 5) * 3 = Array of 3 elements, each is (c_int * 5)
let inner_length = zelf.length.load();
let inner_element_size = zelf.element_size.load();
// Get inner array info from TypeDataSlot
let type_ref = zelf.downcast_ref::<PyType>().unwrap();
let (_inner_length, inner_size) = type_ref
.get_type_data::<StgInfo>()
.map(|stg| (stg.length, stg.size))
.unwrap_or((0, 0));
// The element type of the new array is the current array type itself
let current_array_type: PyObjectRef = zelf.as_object().to_owned();
let current_array_type: PyObjectRef = zelf.clone();
// Element size is the total size of the inner array
let new_element_size = inner_length * inner_element_size;
let new_element_size = inner_size;
let total_size = new_element_size * (n as usize);
let stg_info = StgInfo::new(total_size, inner_element_size);
Ok(PyCArrayType {
stg_info,
typ: PyRwLock::new(current_array_type),
length: AtomicCell::new(n as usize),
element_size: AtomicCell::new(new_element_size),
}
.to_pyobject(vm))
let stg_info = StgInfo::new_array(
total_size,
new_element_size,
n as usize,
current_array_type,
new_element_size,
);
create_array_type_with_stg_info(stg_info, vm)
}
#[pyclassmethod]
fn in_dll(
zelf: &Py<Self>,
zelf: PyObjectRef,
dll: PyObjectRef,
name: crate::builtins::PyStrRef,
vm: &VirtualMachine,
) -> PyResult {
use crate::stdlib::ctypes::_ctypes::size_of;
use libloading::Symbol;
// Get the library handle from dll object
@@ -168,10 +169,18 @@ impl PyCArrayType {
return Err(vm.new_attribute_error("Library is closed".to_owned()));
};
// Get size from the array type
let element_type = zelf.typ.read().clone();
let length = zelf.length.load();
let element_size = size_of(element_type.clone(), vm)?;
// Get size from the array type via TypeDataSlot
let type_ref = zelf.downcast_ref::<PyType>().unwrap();
let (element_type, length, element_size) = type_ref
.get_type_data::<StgInfo>()
.map(|stg| {
(
stg.element_type.clone().unwrap_or_else(|| vm.ctx.none()),
stg.length,
stg.element_size,
)
})
.unwrap_or_else(|| (vm.ctx.none(), 0, 0));
let total_size = element_size * length;
// Read data from symbol address
@@ -206,15 +215,13 @@ impl AsNumber for PyCArrayType {
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
multiply: Some(|a, b, vm| {
let zelf = a
.downcast_ref::<PyCArrayType>()
.ok_or_else(|| vm.new_type_error("expected PyCArrayType".to_owned()))?;
// a is a type object whose metaclass is PyCArrayType (e.g., Array_5)
let n = b
.try_index(vm)?
.as_bigint()
.to_isize()
.ok_or_else(|| vm.new_overflow_error("array size too large".to_owned()))?;
PyCArrayType::__mul__(zelf, n, vm)
PyCArrayType::__mul__(a.to_owned(), n, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};

View File

@@ -1,8 +1,6 @@
use super::_ctypes::bytes_to_pyobject;
use super::array::PyCArrayType;
use super::util::StgInfo;
use crate::builtins::{PyBytes, PyFloat, PyInt, PyNone, PyStr, PyStrRef, PyType, PyTypeRef};
use crate::convert::ToPyObject;
use crate::function::{ArgBytesLike, Either, FuncArgs, KwArgs, OptionalArg};
use crate::protocol::{BufferDescriptor, BufferMethods, PyBuffer, PyNumberMethods};
use crate::stdlib::ctypes::_ctypes::new_simple_type;
@@ -231,10 +229,7 @@ impl PyCData {
#[pyclass(module = "_ctypes", name = "PyCSimpleType", base = PyType)]
#[derive(Debug, PyPayload, Default)]
pub struct PyCSimpleType {
#[allow(dead_code)]
pub stg_info: StgInfo,
}
pub struct PyCSimpleType {}
#[pyclass(flags(BASETYPE), with(AsNumber))]
impl PyCSimpleType {
@@ -747,6 +742,8 @@ impl PyCSimple {
#[pyclassmethod]
fn repeat(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
use super::_ctypes::get_size;
use super::array::create_array_type_with_stg_info;
if n < 0 {
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
}
@@ -766,14 +763,14 @@ impl PyCSimple {
std::mem::size_of::<usize>()
};
let total_size = element_size * (n as usize);
let stg_info = super::util::StgInfo::new(total_size, element_size);
Ok(PyCArrayType {
stg_info,
typ: PyRwLock::new(cls.clone().into()),
length: AtomicCell::new(n as usize),
element_size: AtomicCell::new(element_size),
}
.to_pyobject(vm))
let stg_info = super::util::StgInfo::new_array(
total_size,
element_size,
n as usize,
cls.clone().into(),
element_size,
);
create_array_type_with_stg_info(stg_info, vm)
}
#[pyclassmethod]

View File

@@ -1,44 +1,36 @@
use crossbeam_utils::atomic::AtomicCell;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use crate::builtins::{PyType, PyTypeRef};
use crate::convert::ToPyObject;
use crate::function::FuncArgs;
use crate::protocol::PyNumberMethods;
use crate::stdlib::ctypes::PyCData;
use crate::types::{AsNumber, Constructor};
use crate::{AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine};
use super::util::StgInfo;
#[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")]
#[derive(PyPayload, Debug)]
pub struct PyCPointerType {
#[allow(dead_code)]
pub stg_info: StgInfo,
}
#[derive(PyPayload, Debug, Default)]
pub struct PyCPointerType {}
#[pyclass(flags(IMMUTABLETYPE), with(AsNumber))]
impl PyCPointerType {
#[pymethod]
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
use super::array::PyCArrayType;
use super::array::create_array_type_with_stg_info;
if n < 0 {
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
}
// Pointer size
let element_size = std::mem::size_of::<usize>();
let total_size = element_size * (n as usize);
let mut stg_info = super::util::StgInfo::new(total_size, element_size);
stg_info.length = n as usize;
Ok(PyCArrayType {
stg_info,
typ: PyRwLock::new(cls.as_object().to_owned()),
length: AtomicCell::new(n as usize),
element_size: AtomicCell::new(element_size),
}
.to_pyobject(vm))
let stg_info = super::util::StgInfo::new_array(
total_size,
element_size,
n as usize,
cls.as_object().to_owned(),
element_size,
);
create_array_type_with_stg_info(stg_info, vm)
}
}

View File

@@ -8,7 +8,6 @@ use crate::protocol::{BufferDescriptor, BufferMethods, PyBuffer, PyNumberMethods
use crate::stdlib::ctypes::_ctypes::get_size;
use crate::types::{AsBuffer, AsNumber, Constructor};
use crate::{AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use indexmap::IndexMap;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
@@ -16,11 +15,8 @@ use std::fmt::Debug;
/// PyCStructType - metaclass for Structure
#[pyclass(name = "PyCStructType", base = PyType, module = "_ctypes")]
#[derive(Debug, PyPayload)]
pub struct PyCStructType {
#[allow(dead_code)]
pub stg_info: StgInfo,
}
#[derive(Debug, PyPayload, Default)]
pub struct PyCStructType {}
impl Constructor for PyCStructType {
type Args = FuncArgs;
@@ -161,7 +157,7 @@ impl PyCStructType {
#[pymethod]
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
use super::array::PyCArrayType;
use super::array::create_array_type_with_stg_info;
use crate::stdlib::ctypes::_ctypes::size_of;
if n < 0 {
@@ -174,15 +170,14 @@ impl PyCStructType {
let total_size = element_size
.checked_mul(n as usize)
.ok_or_else(|| vm.new_overflow_error("array size too large".to_owned()))?;
let mut stg_info = super::util::StgInfo::new(total_size, element_size);
stg_info.length = n as usize;
Ok(PyCArrayType {
stg_info,
typ: PyRwLock::new(cls.clone().into()),
length: AtomicCell::new(n as usize),
element_size: AtomicCell::new(element_size),
}
.to_pyobject(vm))
let stg_info = super::util::StgInfo::new_array(
total_size,
element_size,
n as usize,
cls.clone().into(),
element_size,
);
create_array_type_with_stg_info(stg_info, vm)
}
}

View File

@@ -13,18 +13,8 @@ use rustpython_common::lock::PyRwLock;
/// PyCUnionType - metaclass for Union
#[pyclass(name = "UnionType", base = PyType, module = "_ctypes")]
#[derive(Debug, PyPayload)]
pub struct PyCUnionType {
pub stg_info: StgInfo,
}
impl Default for PyCUnionType {
fn default() -> Self {
PyCUnionType {
stg_info: StgInfo::new(0, 1),
}
}
}
#[derive(Debug, PyPayload, Default)]
pub struct PyCUnionType {}
impl Constructor for PyCUnionType {
type Args = FuncArgs;

View File

@@ -1,17 +1,40 @@
use crate::PyObjectRef;
/// Storage information for ctypes types
#[derive(Debug, Clone)]
/// Stored in TypeDataSlot of heap types (PyType::init_type_data/get_type_data)
#[derive(Clone)]
pub struct StgInfo {
#[allow(dead_code)]
pub initialized: bool,
pub size: usize, // number of bytes
pub align: usize, // alignment requirements
pub length: usize, // number of fields (for arrays/structures)
#[allow(dead_code)]
pub size: usize, // number of bytes
pub align: usize, // alignment requirements
pub length: usize, // number of fields (for arrays/structures)
pub proto: Option<PyObjectRef>, // Only for Pointer/ArrayObject
#[allow(dead_code)]
pub flags: i32, // calling convention and such
pub flags: i32, // calling convention and such
// Array-specific fields (moved from PyCArrayType)
pub element_type: Option<PyObjectRef>, // _type_ for arrays
pub element_size: usize, // size of each element
}
// StgInfo is stored in type_data which requires Send + Sync.
// The PyObjectRef in proto/element_type fields is protected by the type system's locking mechanism.
// CPython: ctypes objects are not thread-safe by design; users must synchronize access.
unsafe impl Send for StgInfo {}
unsafe impl Sync for StgInfo {}
impl std::fmt::Debug for StgInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StgInfo")
.field("initialized", &self.initialized)
.field("size", &self.size)
.field("align", &self.align)
.field("length", &self.length)
.field("proto", &self.proto)
.field("flags", &self.flags)
.field("element_type", &self.element_type)
.field("element_size", &self.element_size)
.finish()
}
}
impl Default for StgInfo {
@@ -23,6 +46,8 @@ impl Default for StgInfo {
length: 0,
proto: None,
flags: 0,
element_type: None,
element_size: 0,
}
}
}
@@ -36,6 +61,28 @@ impl StgInfo {
length: 0,
proto: None,
flags: 0,
element_type: None,
element_size: 0,
}
}
/// Create StgInfo for an array type
pub fn new_array(
size: usize,
align: usize,
length: usize,
element_type: PyObjectRef,
element_size: usize,
) -> Self {
StgInfo {
initialized: true,
size,
align,
length,
proto: None,
flags: 0,
element_type: Some(element_type),
element_size,
}
}
}

View File

@@ -1,3 +1,6 @@
use crate::common::lock::{
PyMappedRwLockReadGuard, PyMappedRwLockWriteGuard, PyRwLockReadGuard, PyRwLockWriteGuard,
};
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyInt, PyStr, PyStrInterned, PyStrRef, PyType, PyTypeRef, type_::PointerSlot},
@@ -16,7 +19,96 @@ use crate::{
use crossbeam_utils::atomic::AtomicCell;
use malachite_bigint::BigInt;
use num_traits::{Signed, ToPrimitive};
use std::{borrow::Borrow, cmp::Ordering, ops::Deref};
use std::{any::Any, any::TypeId, borrow::Borrow, cmp::Ordering, ops::Deref};
/// Type-erased storage for extension module data attached to heap types.
pub struct TypeDataSlot {
// PyObject_GetTypeData
type_id: TypeId,
data: Box<dyn Any + Send + Sync>,
}
impl TypeDataSlot {
/// Create a new type data slot with the given data.
pub fn new<T: Any + Send + Sync + 'static>(data: T) -> Self {
Self {
type_id: TypeId::of::<T>(),
data: Box::new(data),
}
}
/// Get a reference to the data if the type matches.
pub fn get<T: Any + 'static>(&self) -> Option<&T> {
if self.type_id == TypeId::of::<T>() {
self.data.downcast_ref()
} else {
None
}
}
/// Get a mutable reference to the data if the type matches.
pub fn get_mut<T: Any + 'static>(&mut self) -> Option<&mut T> {
if self.type_id == TypeId::of::<T>() {
self.data.downcast_mut()
} else {
None
}
}
}
/// Read guard for type data access, using mapped guard for zero-cost deref.
pub struct TypeDataRef<'a, T: 'static> {
guard: PyMappedRwLockReadGuard<'a, T>,
}
impl<'a, T: Any + 'static> TypeDataRef<'a, T> {
/// Try to create a TypeDataRef from a read guard.
/// Returns None if the slot is empty or contains a different type.
pub fn try_new(guard: PyRwLockReadGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
PyRwLockReadGuard::try_map(guard, |opt| opt.as_ref().and_then(|slot| slot.get::<T>()))
.ok()
.map(|guard| Self { guard })
}
}
impl<T: Any + 'static> std::ops::Deref for TypeDataRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
/// Write guard for type data access, using mapped guard for zero-cost deref.
pub struct TypeDataRefMut<'a, T: 'static> {
guard: PyMappedRwLockWriteGuard<'a, T>,
}
impl<'a, T: Any + 'static> TypeDataRefMut<'a, T> {
/// Try to create a TypeDataRefMut from a write guard.
/// Returns None if the slot is empty or contains a different type.
pub fn try_new(guard: PyRwLockWriteGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
PyRwLockWriteGuard::try_map(guard, |opt| {
opt.as_mut().and_then(|slot| slot.get_mut::<T>())
})
.ok()
.map(|guard| Self { guard })
}
}
impl<T: Any + 'static> std::ops::Deref for TypeDataRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: Any + 'static> std::ops::DerefMut for TypeDataRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
#[macro_export]
macro_rules! atomic_func {