mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
TypeData (#6403)
* HeapTypeExt::type_data * Apply TypeDataSlot to ctypes
This commit is contained in:
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user