From f37ea525650d975a8a87fa8052e1785612684ab1 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 31 Mar 2025 21:42:54 -0700 Subject: [PATCH] Ctypes more pointer implementation fix import add more classes ctypes overhall updates fix build fix warnings, pass test fix panic on improper library load test on macos formatting tmp minor updates --- crates/vm/src/stdlib/ctypes.rs | 11 +- crates/vm/src/stdlib/ctypes/array.rs | 1 + crates/vm/src/stdlib/ctypes/base.rs | 22 +- crates/vm/src/stdlib/ctypes/field.rs | 134 ++++++++ crates/vm/src/stdlib/ctypes/function.rs | 392 +++++++++++++++--------- crates/vm/src/stdlib/ctypes/pointer.rs | 41 ++- crates/vm/src/stdlib/ctypes/thunk.rs | 22 ++ crates/vm/src/stdlib/ctypes/util.rs | 24 ++ extra_tests/snippets/stdlib_ctypes.py | 11 +- 9 files changed, 494 insertions(+), 164 deletions(-) create mode 100644 crates/vm/src/stdlib/ctypes/field.rs create mode 100644 crates/vm/src/stdlib/ctypes/thunk.rs create mode 100644 crates/vm/src/stdlib/ctypes/util.rs diff --git a/crates/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs index 8ea4dd165..ac7441835 100644 --- a/crates/vm/src/stdlib/ctypes.rs +++ b/crates/vm/src/stdlib/ctypes.rs @@ -2,11 +2,14 @@ pub(crate) mod array; pub(crate) mod base; +pub(crate) mod field; pub(crate) mod function; pub(crate) mod library; pub(crate) mod pointer; pub(crate) mod structure; +pub(crate) mod thunk; pub(crate) mod union; +pub(crate) mod util; use crate::builtins::PyModule; use crate::class::PyClassImpl; @@ -17,14 +20,18 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py) { let ctx = &vm.ctx; PyCSimpleType::make_class(ctx); array::PyCArrayType::make_class(ctx); + field::PyCFieldType::make_class(ctx); + pointer::PyCPointerType::make_class(ctx); extend_module!(vm, module, { "_CData" => PyCData::make_class(ctx), "_SimpleCData" => PyCSimple::make_class(ctx), "Array" => array::PyCArray::make_class(ctx), + "CField" => field::PyCField::make_class(ctx), "CFuncPtr" => function::PyCFuncPtr::make_class(ctx), "_Pointer" => pointer::PyCPointer::make_class(ctx), "_pointer_type_cache" => ctx.new_dict(), "Structure" => structure::PyCStructure::make_class(ctx), + "CThunkObject" => thunk::PyCThunk::make_class(ctx), "Union" => union::PyCUnion::make_class(ctx), }) } @@ -207,7 +214,9 @@ pub(crate) mod _ctypes { // TODO: load_flags let cache = library::libcache(); let mut cache_write = cache.write(); - let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap(); + let (id, _) = cache_write + .get_or_insert_lib(&name, vm) + .map_err(|e| vm.new_os_error(e.to_string()))?; Ok(id) } diff --git a/crates/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs index 5290ec42f..a1adf847a 100644 --- a/crates/vm/src/stdlib/ctypes/array.rs +++ b/crates/vm/src/stdlib/ctypes/array.rs @@ -106,6 +106,7 @@ impl PyCArray { } impl PyCArray { + #[allow(unused)] pub fn to_arg(&self, _vm: &VirtualMachine) -> PyResult { let value = self.value.read(); let py_bytes = value.downcast_ref::().unwrap(); diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index 23cb505ad..f5a25ad74 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -276,25 +276,25 @@ impl PyCSimple { let value = unsafe { (*self.value.as_ptr()).clone() }; if let Ok(i) = value.try_int(vm) { let i = i.as_bigint(); - if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u8().as_raw_ptr()) { - return i.to_u8().map(|r: u8| libffi::middle::Arg::new(&r)); + return if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u8().as_raw_ptr()) { + i.to_u8().map(|r: u8| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i8().as_raw_ptr()) { - return i.to_i8().map(|r: i8| libffi::middle::Arg::new(&r)); + i.to_i8().map(|r: i8| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u16().as_raw_ptr()) { - return i.to_u16().map(|r: u16| libffi::middle::Arg::new(&r)); + i.to_u16().map(|r: u16| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i16().as_raw_ptr()) { - return i.to_i16().map(|r: i16| libffi::middle::Arg::new(&r)); + i.to_i16().map(|r: i16| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u32().as_raw_ptr()) { - return i.to_u32().map(|r: u32| libffi::middle::Arg::new(&r)); + i.to_u32().map(|r: u32| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i32().as_raw_ptr()) { - return i.to_i32().map(|r: i32| libffi::middle::Arg::new(&r)); + i.to_i32().map(|r: i32| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u64().as_raw_ptr()) { - return i.to_u64().map(|r: u64| libffi::middle::Arg::new(&r)); + i.to_u64().map(|r: u64| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i64().as_raw_ptr()) { - return i.to_i64().map(|r: i64| libffi::middle::Arg::new(&r)); + i.to_i64().map(|r: i64| libffi::middle::Arg::new(&r)) } else { - return None; - } + None + }; } if let Ok(_f) = value.try_float(vm) { todo!(); diff --git a/crates/vm/src/stdlib/ctypes/field.rs b/crates/vm/src/stdlib/ctypes/field.rs new file mode 100644 index 000000000..b50b8b54a --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/field.rs @@ -0,0 +1,134 @@ +use crate::builtins::PyType; +use crate::builtins::PyTypeRef; +use crate::stdlib::ctypes::PyCData; +use crate::types::Constructor; +use crate::types::Representable; +use crate::{Py, PyResult, VirtualMachine}; + +#[pyclass(name = "PyCFieldType", base = PyType, module = "_ctypes")] +#[derive(PyPayload, Debug)] +pub struct PyCFieldType { + pub(super) inner: PyCField, +} + +#[pyclass] +impl PyCFieldType {} + +#[pyclass( + name = "CField", + base = PyCData, + metaclass = "PyCFieldType", + module = "_ctypes" +)] +#[derive(Debug, PyPayload)] +pub struct PyCField { + byte_offset: usize, + byte_size: usize, + #[allow(unused)] + index: usize, + proto: PyTypeRef, + anonymous: bool, + bitfield_size: bool, + bit_offset: u8, + name: String, +} + +impl Representable for PyCField { + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + let tp_name = zelf.proto.name().to_string(); + if zelf.bitfield_size != false { + Ok(format!( + "<{} type={}, ofs={byte_offset}, bit_size={bitfield_size}, bit_offset={bit_offset}", + zelf.name, + tp_name, + byte_offset = zelf.byte_offset, + bitfield_size = zelf.bitfield_size, + bit_offset = zelf.bit_offset + )) + } else { + Ok(format!( + "<{} type={tp_name}, ofs={}, size={}", + zelf.name, zelf.byte_offset, zelf.byte_size + )) + } + } +} + +#[derive(Debug, FromArgs)] +pub struct PyCFieldConstructorArgs { + // PyObject *name, PyObject *proto, + // Py_ssize_t byte_size, Py_ssize_t byte_offset, + // Py_ssize_t index, int _internal_use, + // PyObject *bit_size_obj, PyObject *bit_offset_obj +} + +impl Constructor for PyCField { + type Args = PyCFieldConstructorArgs; + + fn py_new(_cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot instantiate a PyCField".to_string())) + } +} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor, Representable))] +impl PyCField { + #[pygetset] + fn size(&self) -> usize { + self.byte_size + } + + #[pygetset] + fn bit_size(&self) -> bool { + self.bitfield_size + } + + #[pygetset] + fn is_bitfield(&self) -> bool { + self.bitfield_size + } + + #[pygetset] + fn is_anonymous(&self) -> bool { + self.anonymous + } + + #[pygetset] + fn name(&self) -> String { + self.name.clone() + } + + #[pygetset(name = "type")] + fn type_(&self) -> PyTypeRef { + self.proto.clone() + } + + #[pygetset] + fn offset(&self) -> usize { + self.byte_offset + } + + #[pygetset] + fn byte_offset(&self) -> usize { + self.byte_offset + } + + #[pygetset] + fn byte_size(&self) -> usize { + self.byte_size + } + + #[pygetset] + fn bit_offset(&self) -> u8 { + self.bit_offset + } +} + +#[inline(always)] +pub const fn low_bit(offset: usize) -> usize { + offset & 0xFFFF +} + +#[inline(always)] +pub const fn high_bit(offset: usize) -> usize { + offset >> 16 +} diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 6703dcc0f..88d0fbb35 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -1,142 +1,117 @@ // spell-checker:disable -use crate::builtins::{PyStr, PyTupleRef, PyTypeRef}; +use crate::builtins::{PyNone, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef}; use crate::convert::ToPyObject; use crate::function::FuncArgs; use crate::stdlib::ctypes::PyCData; -use crate::stdlib::ctypes::array::PyCArray; use crate::stdlib::ctypes::base::{PyCSimple, ffi_type_from_str}; +use crate::types::Representable; use crate::types::{Callable, Constructor}; -use crate::{Py, PyObjectRef, PyResult, VirtualMachine}; +use crate::{AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use libffi::middle::{Arg, Cif, CodePtr, Type}; use libloading::Symbol; use num_traits::ToPrimitive; use rustpython_common::lock::PyRwLock; -use std::ffi::CString; +use std::ffi::{self, CString, c_void}; use std::fmt::Debug; -// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 - -#[derive(Debug)] -pub struct Function { - args: Vec, - // TODO: no protection from use-after-free - pointer: CodePtr, - cif: Cif, -} - -unsafe impl Send for Function {} -unsafe impl Sync for Function {} +// See also: https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 type FP = unsafe extern "C" fn(); -impl Function { - pub unsafe fn load( - library: &libloading::Library, - function: &str, - args: &[PyObjectRef], - ret_type: &Option, - vm: &VirtualMachine, - ) -> PyResult { - // map each arg to a PyCSimple - let args = args - .iter() - .map(|arg| { - if let Some(arg) = arg.downcast_ref::() { - let converted = ffi_type_from_str(&arg._type_); - return match converted { - Some(t) => Ok(t), - None => Err(vm.new_type_error("Invalid type")), // TODO: add type name - }; - } - if let Some(arg) = arg.downcast_ref::() { - let t = arg.typ.read(); - let ty_attributes = t.attributes.read(); - let ty_pystr = ty_attributes - .get(vm.ctx.intern_str("_type_")) - .ok_or_else(|| vm.new_type_error("Expected a ctypes simple type"))?; - let ty_str = ty_pystr - .downcast_ref::() - .ok_or_else(|| vm.new_type_error("Expected a ctypes simple type"))? - .to_string(); - let converted = ffi_type_from_str(&ty_str); - match converted { - Some(_t) => { - // TODO: Use - Ok(Type::void()) - } - None => Err(vm.new_type_error("Invalid type")), // TODO: add type name - } - } else { - Err(vm.new_type_error("Expected a ctypes simple type")) - } - }) - .collect::>>()?; - let c_function_name = CString::new(function) - .map_err(|_| vm.new_value_error("Function name contains null bytes"))?; - let pointer: Symbol<'_, FP> = unsafe { - library - .get(c_function_name.as_bytes()) - .map_err(|err| err.to_string()) - .map_err(|err| vm.new_attribute_error(err))? - }; - let code_ptr = CodePtr(*pointer as *mut _); - let return_type = match ret_type { - // TODO: Fix this - Some(_t) => { - return Err(vm.new_not_implemented_error("Return type not implemented")); - } - None => Type::c_int(), - }; - let cif = Cif::new(args.clone(), return_type); - Ok(Function { - args, - cif, - pointer: code_ptr, - }) +pub trait ArgumentType { + fn to_ffi_type(&self, vm: &VirtualMachine) -> PyResult; + fn convert_object(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult; +} + +impl ArgumentType for PyTypeRef { + fn to_ffi_type(&self, vm: &VirtualMachine) -> PyResult { + let typ = self + .get_class_attr(vm.ctx.intern_str("_type_")) + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + let typ = typ + .downcast_ref::() + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + let typ = typ.to_string(); + let typ = typ.as_str(); + let converted_typ = ffi_type_from_str(typ); + if let Some(typ) = converted_typ { + Ok(typ) + } else { + Err(vm.new_type_error(format!("Unsupported argument type: {}", typ))) + } } - pub unsafe fn call( + fn convert_object(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // if self.fast_isinstance::(vm) { + // let array = value.downcast::()?; + // return Ok(Arg::from(array.as_ptr())); + // } + if let Ok(simple) = value.downcast::() { + let typ = ArgumentType::to_ffi_type(self, vm)?; + let arg = simple + .to_arg(typ, vm) + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + return Ok(arg); + } + Err(vm.new_type_error("Unsupported argument type".to_string())) + } +} + +pub trait ReturnType { + fn to_ffi_type(&self) -> Option; + fn from_ffi_type( &self, - args: Vec, + value: *mut ffi::c_void, vm: &VirtualMachine, - ) -> PyResult { - let args = args - .into_iter() - .enumerate() - .map(|(count, arg)| { - // none type check - if let Some(d) = arg.downcast_ref::() { - return Ok(d.to_arg(self.args[count].clone(), vm).unwrap()); - } - if let Some(d) = arg.downcast_ref::() { - return Ok(d.to_arg(vm).unwrap()); - } - Err(vm.new_type_error("Expected a ctypes simple type")) - }) - .collect::>>()?; - // TODO: FIX return - let result: i32 = unsafe { self.cif.call(self.pointer, &args) }; - Ok(vm.ctx.new_int(result).into()) + ) -> PyResult>; +} + +impl ReturnType for PyTypeRef { + fn to_ffi_type(&self) -> Option { + ffi_type_from_str(self.name().to_string().as_str()) + } + + fn from_ffi_type( + &self, + _value: *mut ffi::c_void, + _vm: &VirtualMachine, + ) -> PyResult> { + todo!() + } +} + +impl ReturnType for PyNone { + fn to_ffi_type(&self) -> Option { + ffi_type_from_str("void") + } + + fn from_ffi_type( + &self, + _value: *mut ffi::c_void, + _vm: &VirtualMachine, + ) -> PyResult> { + Ok(None) } } #[pyclass(module = "_ctypes", name = "CFuncPtr", base = PyCData)] #[derive(PyPayload)] pub struct PyCFuncPtr { - pub name: PyRwLock, - pub _flags_: AtomicCell, - // FIXME(arihant2math): This shouldn't be an option, setting the default as the none type should work - // This is a workaround for now and I'll fix it later - pub _restype_: PyRwLock>, + pub name: PyRwLock>, + pub ptr: PyRwLock>, + pub needs_free: AtomicCell, + pub arg_types: PyRwLock>>, + pub res_type: PyRwLock>, + pub _flags_: AtomicCell, pub handler: PyObjectRef, } impl Debug for PyCFuncPtr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PyCFuncPtr") - .field("name", &self.name) + .field("flags", &self._flags_) .finish() } } @@ -156,10 +131,43 @@ impl Constructor for PyCFuncPtr { .nth(1) .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? .clone(); + let handle = handler.try_int(vm); + let handle = match handle { + Ok(handle) => handle.as_bigint().clone(), + Err(_) => handler + .get_attr("_handle", vm)? + .try_int(vm)? + .as_bigint() + .clone(), + }; + let library_cache = crate::stdlib::ctypes::library::libcache().read(); + let library = library_cache + .get_lib( + handle + .to_usize() + .ok_or(vm.new_value_error("Invalid handle".to_string()))?, + ) + .ok_or_else(|| vm.new_value_error("Library not found".to_string()))?; + let inner_lib = library.lib.lock(); + + let terminated = format!("{}\0", &name); + let code_ptr = if let Some(lib) = &*inner_lib { + let pointer: Symbol<'_, FP> = unsafe { + lib.get(terminated.as_bytes()) + .map_err(|err| err.to_string()) + .map_err(|err| vm.new_attribute_error(err))? + }; + Some(CodePtr(*pointer as *mut _)) + } else { + None + }; Ok(Self { + ptr: PyRwLock::new(code_ptr), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), _flags_: AtomicCell::new(0), - name: PyRwLock::new(name), - _restype_: PyRwLock::new(None), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(name)), handler, } .to_pyobject(vm)) @@ -169,53 +177,145 @@ impl Constructor for PyCFuncPtr { impl Callable for PyCFuncPtr { type Args = FuncArgs; fn call(zelf: &Py, args: Self::Args, vm: &VirtualMachine) -> PyResult { - unsafe { - let handle = zelf.handler.get_attr("_handle", vm)?; - let handle = handle.try_int(vm)?.as_bigint().clone(); - let library_cache = crate::stdlib::ctypes::library::libcache().read(); - let library = library_cache - .get_lib( - handle - .to_usize() - .ok_or(vm.new_value_error("Invalid handle"))?, - ) - .ok_or_else(|| vm.new_value_error("Library not found"))?; - let inner_lib = library.lib.lock(); - let name = zelf.name.read(); - let res_type = zelf._restype_.read(); - let func = Function::load( - inner_lib - .as_ref() - .ok_or_else(|| vm.new_value_error("Library not found"))?, - &name, - &args.args, - &res_type, - vm, - )?; - func.call(args.args, vm) + // This is completely seperate from the C python implementation + + // Cif init + let arg_types: Vec<_> = match zelf.arg_types.read().clone() { + Some(tys) => tys, + None => args + .args + .clone() + .into_iter() + .map(|a| a.class().as_object().to_pyobject(vm).downcast().unwrap()) + .collect(), + }; + let ffi_arg_types = arg_types + .clone() + .iter() + .map(|t| ArgumentType::to_ffi_type(t, vm)) + .collect::>>()?; + let return_type = zelf.res_type.read(); + let ffi_return_type = return_type + .as_ref() + .map(|t| ReturnType::to_ffi_type(&t.clone().downcast::().unwrap())) + .flatten() + .unwrap_or_else(|| Type::i32()); + let cif = Cif::new(ffi_arg_types, ffi_return_type); + + // Call the function + let ffi_args = args + .args + .into_iter() + .enumerate() + .map(|(n, arg)| { + let arg_type = arg_types + .get(n) + .ok_or_else(|| vm.new_type_error("argument amount mismatch".to_string()))?; + arg_type.convert_object(arg, vm) + }) + .collect::, _>>()?; + let pointer = zelf.ptr.read(); + let code_ptr = pointer + .as_ref() + .ok_or_else(|| vm.new_type_error("Function pointer not set".to_string()))?; + let mut output: c_void = unsafe { cif.call(*code_ptr, &ffi_args) }; + let return_type = return_type + .as_ref() + .map(|f| { + f.clone() + .downcast::() + .unwrap() + .from_ffi_type(&mut output, vm) + .ok() + .flatten() + }) + .unwrap_or_else(|| Some(vm.ctx.new_int(output as i32).as_object().to_pyobject(vm))); + if let Some(return_type) = return_type { + Ok(return_type) + } else { + Ok(vm.ctx.none()) } } } -#[pyclass(flags(BASETYPE), with(Callable, Constructor))] +impl Representable for PyCFuncPtr { + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + let index = zelf.ptr.read(); + let index = index.map(|ptr| ptr.0 as usize).unwrap_or(0); + let type_name = zelf.class().name(); + #[cfg(windows)] + { + let index = index - 0x1000; + return Ok(format!("")); + } + Ok(format!("<{type_name} object at {index:#x}>")) + } +} + +// TODO: fix +unsafe impl Send for PyCFuncPtr {} +unsafe impl Sync for PyCFuncPtr {} + +#[pyclass(flags(BASETYPE), with(Callable, Constructor, Representable))] impl PyCFuncPtr { + #[pygetset(name = "_restype_")] + fn restype(&self) -> Option { + self.res_type.read().as_ref().cloned() + } + + #[pygetset(name = "_restype_", setter)] + fn set_restype(&self, restype: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + // has to be type, callable, or none + // TODO: Callable support + if vm.is_none(&restype) || restype.downcast_ref::().is_some() { + *self.res_type.write() = Some(restype); + Ok(()) + } else { + Err(vm.new_type_error("restype must be a type, a callable, or None".to_string())) + } + } + + #[pygetset(name = "argtypes")] + fn argtypes(&self, vm: &VirtualMachine) -> PyTupleRef { + PyTuple::new_ref( + self.arg_types + .read() + .clone() + .unwrap_or_default() + .into_iter() + .map(|t| t.to_pyobject(vm)) + .collect(), + &vm.ctx, + ) + } + + #[pygetset(name = "argtypes", setter)] + fn set_argtypes(&self, argtypes: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let none = vm.is_none(&argtypes); + if none { + *self.arg_types.write() = None; + Ok(()) + } else { + let tuple = argtypes.downcast::().unwrap(); + *self.arg_types.write() = Some( + tuple + .iter() + .map(|obj| obj.clone().downcast::().unwrap()) + .collect::>(), + ); + Ok(()) + } + } + #[pygetset] - fn __name__(&self) -> String { + fn __name__(&self) -> Option { self.name.read().clone() } #[pygetset(setter)] - fn set___name__(&self, name: String) { - *self.name.write() = name; - } - - #[pygetset(name = "_restype_")] - fn restype(&self) -> Option { - self._restype_.read().as_ref().cloned() - } - - #[pygetset(name = "_restype_", setter)] - fn set_restype(&self, restype: PyTypeRef) { - *self._restype_.write() = Some(restype); + fn set___name__(&self, name: String) -> PyResult<()> { + *self.name.write() = Some(name); + // TODO: update handle and stuff + Ok(()) } } diff --git a/crates/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs index d1360f986..e07231256 100644 --- a/crates/vm/src/stdlib/ctypes/pointer.rs +++ b/crates/vm/src/stdlib/ctypes/pointer.rs @@ -1,5 +1,40 @@ -#[pyclass(name = "Pointer", module = "_ctypes")] -pub struct PyCPointer {} +use rustpython_common::lock::PyRwLock; + +use crate::builtins::PyType; +use crate::stdlib::ctypes::PyCData; +use crate::{PyObjectRef, PyResult}; + +#[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")] +#[derive(PyPayload, Debug)] +pub struct PyCPointerType { + pub inner: PyCPointer, +} + +#[pyclass] +impl PyCPointerType {} + +#[pyclass( + name = "_Pointer", + base = PyCData, + metaclass = "PyCPointerType", + module = "_ctypes" +)] +#[derive(Debug, PyPayload)] +pub struct PyCPointer { + contents: PyRwLock, +} #[pyclass(flags(BASETYPE, IMMUTABLETYPE))] -impl PyCPointer {} +impl PyCPointer { + // TODO: not correct + #[pygetset] + fn contents(&self) -> PyResult { + let contents = self.contents.read().clone(); + Ok(contents) + } + #[pygetset(setter)] + fn set_contents(&self, contents: PyObjectRef) -> PyResult<()> { + *self.contents.write() = contents; + Ok(()) + } +} diff --git a/crates/vm/src/stdlib/ctypes/thunk.rs b/crates/vm/src/stdlib/ctypes/thunk.rs new file mode 100644 index 000000000..a65b04684 --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/thunk.rs @@ -0,0 +1,22 @@ +//! Yes, really, this is not a typo. + +// typedef struct { +// PyObject_VAR_HEAD +// ffi_closure *pcl_write; /* the C callable, writeable */ +// void *pcl_exec; /* the C callable, executable */ +// ffi_cif cif; +// int flags; +// PyObject *converters; +// PyObject *callable; +// PyObject *restype; +// SETFUNC setfunc; +// ffi_type *ffi_restype; +// ffi_type *atypes[1]; +// } CThunkObject; + +#[pyclass(name = "CThunkObject", module = "_ctypes")] +#[derive(Debug, PyPayload)] +pub struct PyCThunk {} + +#[pyclass] +impl PyCThunk {} diff --git a/crates/vm/src/stdlib/ctypes/util.rs b/crates/vm/src/stdlib/ctypes/util.rs new file mode 100644 index 000000000..df5d31866 --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/util.rs @@ -0,0 +1,24 @@ +use crate::PyObjectRef; + +#[pyclass(name, module = "_ctypes")] +#[derive(Debug, PyPayload)] +pub struct StgInfo { + pub initialized: i32, + pub size: usize, // number of bytes + pub align: usize, // alignment requirements + pub length: usize, // number of fields + // ffi_type_pointer: ffi::ffi_type, + pub proto: PyObjectRef, // Only for Pointer/ArrayObject + pub setfunc: Option, // Only for simple objects + pub getfunc: Option, // Only for simple objects + pub paramfunc: Option, + + /* Following fields only used by PyCFuncPtrType_Type instances */ + pub argtypes: Option, // tuple of CDataObjects + pub converters: Option, // tuple([t.from_param for t in argtypes]) + pub restype: Option, // CDataObject or NULL + pub checker: Option, + pub module: Option, + pub flags: i32, // calling convention and such + pub dict_final: u8, +} diff --git a/extra_tests/snippets/stdlib_ctypes.py b/extra_tests/snippets/stdlib_ctypes.py index 32ed17d19..3ef6df689 100644 --- a/extra_tests/snippets/stdlib_ctypes.py +++ b/extra_tests/snippets/stdlib_ctypes.py @@ -40,7 +40,6 @@ def create_string_buffer(init, size=None): size = len(init) + 1 _sys.audit("ctypes.create_string_buffer", init, size) buftype = c_char.__mul__(size) - print(type(c_char.__mul__(size))) # buftype = c_char * size buf = buftype() buf.value = init @@ -334,8 +333,14 @@ cdll = LibraryLoader(CDLL) test_byte_array = create_string_buffer(b"Hello, World!\n") assert test_byte_array._length_ == 15 -if _os.name == "posix" or _sys.platform == "darwin": - pass +if _os.name == "posix": + if _sys.platform == "darwin": + libc = cdll.LoadLibrary("libc.dylib") + libc.rand() + i = c_int(1) + print("start srand") + print(libc.srand(i)) + print(test_byte_array) else: import os