Skip to content

Commit eb2ffc6

Browse files
Use sequence repetition for ctypes array types (#9185)
Assisted-by: Codex:GPT-6 Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
1 parent 58b54c4 commit eb2ffc6

7 files changed

Lines changed: 103 additions & 137 deletions

File tree

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

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::{
1010
},
1111
class::StaticType,
1212
function::{ArgBytesLike, FuncArgs, PySetterValue},
13-
protocol::{BufferDescriptor, PyBuffer, PyMappingMethods, PyNumberMethods, PySequenceMethods},
14-
types::{AsBuffer, AsMapping, AsNumber, AsSequence, Constructor, Initializer},
13+
protocol::{BufferDescriptor, PyBuffer, PyMappingMethods, PySequenceMethods},
14+
types::{AsBuffer, AsMapping, AsSequence, Constructor, Initializer},
1515
};
1616
use alloc::borrow::Cow;
1717
use num_traits::{Signed, ToPrimitive};
@@ -44,6 +44,27 @@ fn get_size_from_format(fmt: &str) -> usize {
4444
code.map_or(1, |c| simple_type_size(&c).unwrap_or(1))
4545
}
4646

47+
pub(super) static CTYPE_SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
48+
repeat: Some(|seq, length, vm| repeat_type(seq.obj, length, vm)),
49+
..PySequenceMethods::NOT_IMPLEMENTED
50+
};
51+
52+
pub(super) fn repeat_type(ctype: &PyObject, length: isize, vm: &VirtualMachine) -> PyResult {
53+
if length < 0 {
54+
return Err(vm.new_value_error(format!("Array length must be >= 0, not {length}")));
55+
}
56+
let cls = ctype
57+
.downcast_ref::<PyType>()
58+
.ok_or_else(|| vm.new_type_error("expected type"))?;
59+
if let Some(info) = cls.stg_info_opt()
60+
&& info.size != 0
61+
&& length as usize > isize::MAX as usize / info.size
62+
{
63+
return Err(vm.new_overflow_error("array too large"));
64+
}
65+
array_type_from_ctype(ctype.to_owned(), length as usize, vm)
66+
}
67+
4768
/// Creates array type for (element_type, length)
4869
/// Uses _array_type_cache to ensure identical calls return the same type object
4970
pub(super) fn array_type_from_ctype(
@@ -305,7 +326,7 @@ impl Initializer for PyCArrayType {
305326
}
306327
}
307328

308-
#[pyclass(flags(IMMUTABLETYPE), with(Initializer, AsNumber))]
329+
#[pyclass(flags(IMMUTABLETYPE), with(Initializer, AsSequence))]
309330
impl PyCArrayType {
310331
#[pygetset(name = "__pointer_type__")]
311332
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -350,41 +371,9 @@ impl PyCArrayType {
350371
}
351372
}
352373

353-
impl AsNumber for PyCArrayType {
354-
fn as_number() -> &'static PyNumberMethods {
355-
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
356-
multiply: Some(|a, b, vm| {
357-
// a is a type object whose metaclass is PyCArrayType (e.g., Array_5)
358-
let n = b
359-
.try_index(vm)?
360-
.as_bigint()
361-
.to_isize()
362-
.ok_or_else(|| vm.new_overflow_error("array size too large"))?;
363-
364-
if n < 0 {
365-
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
366-
}
367-
368-
// Check for overflow before creating the new array type
369-
let zelf_type = a
370-
.downcast_ref::<PyType>()
371-
.ok_or_else(|| vm.new_type_error("Expected type"))?;
372-
373-
if let Some(stg_info) = zelf_type.stg_info_opt() {
374-
let current_size = stg_info.size;
375-
// Check if current_size * n would overflow
376-
if current_size != 0 && (n as usize) > isize::MAX as usize / current_size {
377-
return Err(vm.new_overflow_error("array too large"));
378-
}
379-
}
380-
381-
// Use cached array type creation
382-
// The element type of the new array is the current array type itself
383-
array_type_from_ctype(a.to_owned(), n as usize, vm)
384-
}),
385-
..PyNumberMethods::NOT_IMPLEMENTED
386-
};
387-
&AS_NUMBER
374+
impl AsSequence for PyCArrayType {
375+
fn as_sequence() -> &'static PySequenceMethods {
376+
&super::array::CTYPE_SEQUENCE_METHODS
388377
}
389378
}
390379

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212
builtins::{PyBytes, PyDict, PyInt, PyStr, PyTuple, PyType, PyTypeRef},
1313
class::StaticType,
1414
function::FuncArgs,
15-
protocol::{BufferDescriptor, PyBuffer, PyNumberMethods},
16-
types::{AsBuffer, AsNumber, Callable, Constructor, Initializer, Representable},
15+
protocol::{BufferDescriptor, PyBuffer, PyNumberMethods, PySequenceMethods},
16+
types::{AsBuffer, AsNumber, AsSequence, Callable, Constructor, Initializer, Representable},
1717
vm::thread::with_current_vm,
1818
};
1919
use alloc::borrow::Cow;
@@ -331,7 +331,7 @@ impl Initializer for PyCFuncPtrType {
331331
}
332332
}
333333

334-
#[pyclass(flags(IMMUTABLETYPE), with(Initializer))]
334+
#[pyclass(flags(IMMUTABLETYPE), with(AsSequence, Initializer))]
335335
impl PyCFuncPtrType {
336336
#[pygetset(name = "__pointer_type__")]
337337
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -2032,6 +2032,12 @@ impl Py<PyCThunk> {
20322032
}
20332033
}
20342034

2035+
impl AsSequence for PyCFuncPtrType {
2036+
fn as_sequence() -> &'static PySequenceMethods {
2037+
&super::array::CTYPE_SEQUENCE_METHODS
2038+
}
2039+
}
2040+
20352041
#[cfg(test)]
20362042
mod tests {
20372043
use super::*;

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

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use super::base::CDATA_BUFFER_METHODS;
22
use super::{PyCArray, PyCData, PyCSimple, PyCStructure, StgInfo, StgInfoFlags};
33
use crate::atomic_func;
4-
use crate::protocol::{BufferDescriptor, PyBuffer, PyMappingMethods, PyNumberMethods};
5-
use crate::types::{AsBuffer, AsMapping, AsNumber, Constructor, Initializer};
4+
use crate::protocol::{
5+
BufferDescriptor, PyBuffer, PyMappingMethods, PyNumberMethods, PySequenceMethods,
6+
};
7+
use crate::types::{AsBuffer, AsMapping, AsNumber, AsSequence, Constructor, Initializer};
68
use crate::{
79
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
810
builtins::{PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef},
@@ -87,7 +89,7 @@ impl Initializer for PyCPointerType {
8789
}
8890
}
8991

90-
#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(AsNumber, Initializer))]
92+
#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(AsSequence, Initializer))]
9193
impl PyCPointerType {
9294
#[pygetset(name = "__pointer_type__")]
9395
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -175,16 +177,6 @@ impl PyCPointerType {
175177
)))
176178
}
177179

178-
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
179-
use super::array::array_type_from_ctype;
180-
181-
if n < 0 {
182-
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
183-
}
184-
// Use cached array type creation
185-
array_type_from_ctype(cls.into(), n as usize, vm)
186-
}
187-
188180
// PyCPointerType_set_type: Complete an incomplete pointer type
189181
#[pymethod]
190182
fn set_type(zelf: PyTypeRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
@@ -229,23 +221,9 @@ impl PyCPointerType {
229221
}
230222
}
231223

232-
impl AsNumber for PyCPointerType {
233-
fn as_number() -> &'static PyNumberMethods {
234-
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
235-
multiply: Some(|a, b, vm| {
236-
let cls = a
237-
.downcast_ref::<PyType>()
238-
.ok_or_else(|| vm.new_type_error("expected type"))?;
239-
let n = b
240-
.try_index(vm)?
241-
.as_bigint()
242-
.to_isize()
243-
.ok_or_else(|| vm.new_overflow_error("array size too large"))?;
244-
PyCPointerType::__mul__(cls.to_owned(), n, vm)
245-
}),
246-
..PyNumberMethods::NOT_IMPLEMENTED
247-
};
248-
&AS_NUMBER
224+
impl AsSequence for PyCPointerType {
225+
fn as_sequence() -> &'static PySequenceMethods {
226+
&super::array::CTYPE_SEQUENCE_METHODS
249227
}
250228
}
251229

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

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use super::pointer::PyCPointer;
88
use crate::builtins::{PyByteArray, PyBytes, PyInt, PyNone, PyStr, PyType, PyTypeRef};
99
use crate::convert::ToPyObject;
1010
use crate::function::{Either, FuncArgs, OptionalArg};
11-
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
12-
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer, Representable};
11+
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods, PySequenceMethods};
12+
use crate::types::{AsBuffer, AsNumber, AsSequence, Constructor, Initializer, Representable};
1313
use crate::{
1414
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
1515
class::PyClassDef, set_attrs,
@@ -226,7 +226,7 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul
226226
#[repr(transparent)]
227227
pub(crate) struct PyCSimpleType(PyType);
228228

229-
#[pyclass(flags(BASETYPE), with(AsNumber, Initializer))]
229+
#[pyclass(flags(BASETYPE), with(AsSequence, Initializer))]
230230
impl PyCSimpleType {
231231
#[pygetset(name = "__pointer_type__")]
232232
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -494,31 +494,11 @@ impl PyCSimpleType {
494494
_ => Err(vm.new_type_error("wrong type")),
495495
}
496496
}
497-
498-
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
499-
Py::<PyCSimple>::repeat(cls, n, vm)
500-
}
501497
}
502498

503-
impl AsNumber for PyCSimpleType {
504-
fn as_number() -> &'static PyNumberMethods {
505-
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
506-
multiply: Some(|a, b, vm| {
507-
// a is a PyCSimpleType instance (type object like c_char)
508-
// b is int (array size)
509-
let cls = a
510-
.downcast_ref::<PyType>()
511-
.ok_or_else(|| vm.new_type_error("expected type"))?;
512-
let n = b
513-
.try_index(vm)?
514-
.as_bigint()
515-
.to_isize()
516-
.ok_or_else(|| vm.new_overflow_error("array size too large"))?;
517-
Py::<PyCSimple>::repeat(cls.to_owned(), n, vm)
518-
}),
519-
..PyNumberMethods::NOT_IMPLEMENTED
520-
};
521-
&AS_NUMBER
499+
impl AsSequence for PyCSimpleType {
500+
fn as_sequence() -> &'static PySequenceMethods {
501+
&super::array::CTYPE_SEQUENCE_METHODS
522502
}
523503
}
524504

@@ -1229,13 +1209,7 @@ impl Py<PyCSimple> {
12291209

12301210
#[pyclassmethod]
12311211
fn repeat(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
1232-
use super::array::array_type_from_ctype;
1233-
1234-
if n < 0 {
1235-
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
1236-
}
1237-
// Use cached array type creation
1238-
array_type_from_ctype(cls.into(), n as usize, vm)
1212+
super::array::repeat_type(cls.as_object(), n, vm)
12391213
}
12401214

12411215
// Simple_from_outparm - convert output parameter back to Python value

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

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use super::base::{CDATA_BUFFER_METHODS, PyCData, PyCField, StgInfo, StgInfoFlags
22
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef, PyUtf8Str};
33
use crate::convert::ToPyObject;
44
use crate::function::{ArgStrictInt, FuncArgs, OptionalArg, PySetterValue};
5-
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
5+
use crate::protocol::{BufferDescriptor, PyBuffer, PySequenceMethods};
66
use crate::stdlib::_warnings;
7-
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer, SetAttr};
7+
use crate::types::{AsBuffer, AsSequence, Constructor, Initializer, SetAttr};
88
use crate::{AsObject, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine};
99
use alloc::borrow::Cow;
1010
use core::fmt::Debug;
@@ -129,7 +129,7 @@ impl Initializer for PyCStructType {
129129
}
130130
}
131131

132-
#[pyclass(flags(BASETYPE), with(AsNumber, Constructor, Initializer, SetAttr))]
132+
#[pyclass(flags(BASETYPE), with(AsSequence, Constructor, Initializer, SetAttr))]
133133
impl PyCStructType {
134134
#[pygetset(name = "__pointer_type__")]
135135
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -565,35 +565,11 @@ impl PyCStructType {
565565

566566
Ok(())
567567
}
568-
569-
fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
570-
use super::array::array_type_from_ctype;
571-
572-
if n < 0 {
573-
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
574-
}
575-
// Use cached array type creation
576-
array_type_from_ctype(cls.into(), n as usize, vm)
577-
}
578568
}
579569

580-
impl AsNumber for PyCStructType {
581-
fn as_number() -> &'static PyNumberMethods {
582-
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
583-
multiply: Some(|a, b, vm| {
584-
let cls = a
585-
.downcast_ref::<PyType>()
586-
.ok_or_else(|| vm.new_type_error("expected type"))?;
587-
let n = b
588-
.try_index(vm)?
589-
.as_bigint()
590-
.to_isize()
591-
.ok_or_else(|| vm.new_overflow_error("array size too large"))?;
592-
PyCStructType::__mul__(cls.to_owned(), n, vm)
593-
}),
594-
..PyNumberMethods::NOT_IMPLEMENTED
595-
};
596-
&AS_NUMBER
570+
impl AsSequence for PyCStructType {
571+
fn as_sequence() -> &'static PySequenceMethods {
572+
&super::array::CTYPE_SEQUENCE_METHODS
597573
}
598574
}
599575

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use super::{PyCData, PyCField, StgInfo};
33
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef, PyUtf8Str};
44
use crate::convert::ToPyObject;
55
use crate::function::{ArgBytesLike, ArgStrictInt, FuncArgs, OptionalArg, PySetterValue};
6-
use crate::protocol::{BufferDescriptor, PyBuffer};
6+
use crate::protocol::{BufferDescriptor, PyBuffer, PySequenceMethods};
77
use crate::stdlib::_warnings;
8-
use crate::types::{AsBuffer, Constructor, Initializer, SetAttr};
8+
use crate::types::{AsBuffer, AsSequence, Constructor, Initializer, SetAttr};
99
use crate::{AsObject, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine};
1010
use alloc::borrow::Cow;
1111
use num_traits::ToPrimitive;
@@ -356,7 +356,7 @@ impl PyCUnionType {
356356
}
357357
}
358358

359-
#[pyclass(flags(BASETYPE), with(Constructor, Initializer, SetAttr))]
359+
#[pyclass(flags(BASETYPE), with(AsSequence, Constructor, Initializer, SetAttr))]
360360
impl PyCUnionType {
361361
#[pygetset(name = "__pointer_type__")]
362362
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
@@ -697,3 +697,9 @@ impl AsBuffer for PyCUnion {
697697
Ok(buf)
698698
}
699699
}
700+
701+
impl AsSequence for PyCUnionType {
702+
fn as_sequence() -> &'static PySequenceMethods {
703+
&super::array::CTYPE_SEQUENCE_METHODS
704+
}
705+
}

0 commit comments

Comments
 (0)