Skip to content

Commit cdb3939

Browse files
authored
Fix ctypes scalar access, bitfields, and cyclic collection (#9390)
* Fix ctypes scalar access, bitfields, and cyclic collection Assisted-by: Codex:GPT-6 * Format ctypes pyclass attributes Assisted-by: Codex:GPT-6
1 parent 9848c78 commit cdb3939

12 files changed

Lines changed: 490 additions & 151 deletions

File tree

‎crates/host_env/src/ctypes.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,12 @@ impl<U: 'static> CallbackThunk<U> {
18601860
}
18611861
}
18621862

1863+
/// Borrow the userdata owned by this callback closure.
1864+
pub fn userdata(&self) -> &U {
1865+
// SAFETY: new stores Box::into_raw here; Drop owns its only reclamation.
1866+
unsafe { &*self.userdata_ptr }
1867+
}
1868+
18631869
pub fn code_ptr(&self) -> CodePtr {
18641870
self.code_ptr
18651871
}

‎crates/vm/src/builtins/type.rs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ unsafe impl crate::object::Traverse for PyType {
306306
self.attributes.traverse(tracer_fn);
307307
if let Some(ext) = self.heaptype_ext.as_ref() {
308308
ext.specialization_cache.traverse(tracer_fn);
309+
ext.type_data.traverse(tracer_fn);
309310
}
310311
}
311312

@@ -336,6 +337,11 @@ unsafe impl crate::object::Traverse for PyType {
336337
self.attributes.drain_into(out);
337338
if let Some(ext) = self.heaptype_ext.as_ref() {
338339
ext.specialization_cache.clear_into(out);
340+
if let Some(mut data) = ext.type_data.try_write()
341+
&& let Some(data) = data.as_mut()
342+
{
343+
data.clear(out);
344+
}
339345
}
340346
}
341347
}
@@ -1700,6 +1706,18 @@ impl PyType {
17001706
/// Initialize type data for this type. Can only be called once.
17011707
/// Returns an error if the type is not a heap type or if data is already initialized.
17021708
pub fn init_type_data<T: Any + Send + Sync + 'static>(&self, data: T) -> Result<(), String> {
1709+
self.init_type_data_slot(TypeDataSlot::new(data))
1710+
}
1711+
1712+
/// Initialize extension storage with GC traversal and clearing callbacks.
1713+
pub fn init_traced_type_data<T: Any + Send + Sync + Traverse>(
1714+
&self,
1715+
data: T,
1716+
) -> Result<(), String> {
1717+
self.init_type_data_slot(TypeDataSlot::new_traced(data))
1718+
}
1719+
1720+
fn init_type_data_slot(&self, data: TypeDataSlot) -> Result<(), String> {
17031721
let ext = self
17041722
.heaptype_ext
17051723
.as_ref()
@@ -1709,7 +1727,7 @@ impl PyType {
17091727
if type_data.is_some() {
17101728
return Err("Type data already initialized".to_string());
17111729
}
1712-
*type_data = Some(TypeDataSlot::new(data));
1730+
*type_data = Some(data);
17131731
Ok(())
17141732
}
17151733

‎crates/vm/src/stdlib/_ctypes.rs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ impl Py<PyType> {
4646
.filter(|info| info.initialized)
4747
}
4848

49-
/// Get _type_ attribute as String (type code like "i", "d", etc.)
49+
/// Get the scalar converter selected when the storage type was initialized.
5050
fn type_code(&self, vm: &VirtualMachine) -> Option<String> {
51+
if let Some(info) = self.stg_info_opt() {
52+
return info.scalar_code.map(|code| code.to_string());
53+
}
5154
self.as_object()
5255
.get_attr("_type_", vm)
5356
.ok()
@@ -62,7 +65,7 @@ impl Py<PyType> {
6265
} else {
6366
let mut stg = StgInfo::default();
6467
stg.flags |= StgInfoFlags::DICTFLAG_FINAL;
65-
let _ = base.init_type_data(stg);
68+
let _ = base.init_traced_type_data(stg);
6669
}
6770
}
6871
}

‎crates/vm/src/stdlib/_ctypes/array.rs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,12 +381,23 @@ impl AsSequence for PyCArrayType {
381381
name = "Array",
382382
base = PyCData,
383383
metaclass = "PyCArrayType",
384-
module = "_ctypes"
384+
module = "_ctypes",
385+
traverse = "manual"
385386
)]
386387
#[derive(Debug)]
387388
#[repr(transparent)]
388389
pub(crate) struct PyCArray(pub PyCData);
389390

391+
// SAFETY: this transparent wrapper owns exactly the references in its base.
392+
unsafe impl crate::object::Traverse for PyCArray {
393+
fn traverse(&self, tracer: &mut crate::object::TraverseFn<'_>) {
394+
self.0.traverse(tracer);
395+
}
396+
fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
397+
self.0.clear(out);
398+
}
399+
}
400+
390401
impl PyCArray {
391402
/// Get the type code of array element type (e.g., "c" for c_char, "u" for c_wchar)
392403
fn get_element_type_code(zelf: &Py<Self>, vm: &VirtualMachine) -> Option<String> {

0 commit comments

Comments
 (0)