Merge pull request #5653 from arihant2math/ctypes-pt5

_ctypes pt. 5
This commit is contained in:
Jeong, YunWon
2025-11-29 00:24:21 +09:00
committed by GitHub
8 changed files with 469 additions and 169 deletions

View File

@@ -2,10 +2,12 @@
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;
use crate::builtins::PyModule;
@@ -17,14 +19,18 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
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 +213,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)
}

View File

@@ -106,6 +106,7 @@ impl PyCArray {
}
impl PyCArray {
#[allow(unused)]
pub fn to_arg(&self, _vm: &VirtualMachine) -> PyResult<libffi::middle::Arg> {
let value = self.value.read();
let py_bytes = value.downcast_ref::<PyBytes>().unwrap();

View File

@@ -218,11 +218,12 @@ impl Constructor for PyCSimple {
_ => vm.ctx.none(), // "z" | "Z" | "P"
}
};
Ok(PyCSimple {
PyCSimple {
_type_,
value: AtomicCell::new(value),
}
.to_pyobject(vm))
.into_ref_with_type(vm, cls)
.map(Into::into)
}
}
@@ -276,25 +277,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!();

View File

@@ -0,0 +1,125 @@
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 {
#[allow(dead_code)]
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<Self>, _vm: &VirtualMachine) -> PyResult<String> {
let tp_name = zelf.proto.name().to_string();
if zelf.bitfield_size {
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
}
}

View File

@@ -1,142 +1,120 @@
// 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, 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<Type>,
// 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<PyTypeRef>,
vm: &VirtualMachine,
) -> PyResult<Self> {
// map each arg to a PyCSimple
let args = args
.iter()
.map(|arg| {
if let Some(arg) = arg.downcast_ref::<PyCSimple>() {
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::<PyCArray>() {
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::<PyStr>()
.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::<PyResult<Vec<Type>>>()?;
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<Type>;
fn convert_object(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<Arg>;
}
impl ArgumentType for PyTypeRef {
fn to_ffi_type(&self, vm: &VirtualMachine) -> PyResult<Type> {
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::<PyStr>()
.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<Arg> {
// if self.fast_isinstance::<PyCArray>(vm) {
// let array = value.downcast::<PyCArray>()?;
// return Ok(Arg::from(array.as_ptr()));
// }
if let Ok(simple) = value.downcast::<PyCSimple>() {
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<Type>;
#[allow(clippy::wrong_self_convention)]
fn from_ffi_type(
&self,
args: Vec<PyObjectRef>,
value: *mut ffi::c_void,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
let args = args
.into_iter()
.enumerate()
.map(|(count, arg)| {
// none type check
if let Some(d) = arg.downcast_ref::<PyCSimple>() {
return Ok(d.to_arg(self.args[count].clone(), vm).unwrap());
}
if let Some(d) = arg.downcast_ref::<PyCArray>() {
return Ok(d.to_arg(vm).unwrap());
}
Err(vm.new_type_error("Expected a ctypes simple type"))
})
.collect::<PyResult<Vec<Arg>>>()?;
// TODO: FIX return
let result: i32 = unsafe { self.cif.call(self.pointer, &args) };
Ok(vm.ctx.new_int(result).into())
) -> PyResult<Option<PyObjectRef>>;
}
impl ReturnType for PyTypeRef {
fn to_ffi_type(&self) -> Option<Type> {
ffi_type_from_str(self.name().to_string().as_str())
}
fn from_ffi_type(
&self,
_value: *mut ffi::c_void,
_vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
todo!()
}
}
impl ReturnType for PyNone {
fn to_ffi_type(&self) -> Option<Type> {
ffi_type_from_str("void")
}
fn from_ffi_type(
&self,
_value: *mut ffi::c_void,
_vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
Ok(None)
}
}
#[pyclass(module = "_ctypes", name = "CFuncPtr", base = PyCData)]
#[derive(PyPayload)]
pub struct PyCFuncPtr {
pub name: PyRwLock<String>,
pub _flags_: AtomicCell<u32>,
// 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<Option<PyTypeRef>>,
pub name: PyRwLock<Option<String>>,
pub ptr: PyRwLock<Option<CodePtr>>,
#[allow(dead_code)]
pub needs_free: AtomicCell<bool>,
pub arg_types: PyRwLock<Option<Vec<PyTypeRef>>>,
pub res_type: PyRwLock<Option<PyObjectRef>>,
pub _flags_: AtomicCell<i32>,
#[allow(dead_code)]
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 +134,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 +180,144 @@ impl Constructor for PyCFuncPtr {
impl Callable for PyCFuncPtr {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, 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::<PyResult<Vec<_>>>()?;
let return_type = zelf.res_type.read();
let ffi_return_type = return_type
.as_ref()
.and_then(|t| ReturnType::to_ffi_type(&t.clone().downcast::<PyType>().unwrap()))
.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::<Result<Vec<_>, _>>()?;
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::<PyType>()
.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<Self>, _vm: &VirtualMachine) -> PyResult<String> {
let index = zelf.ptr.read();
let index = index.map(|ptr| ptr.0 as usize).unwrap_or(0);
let type_name = zelf.class().name();
if cfg!(windows) {
let index = index - 0x1000;
Ok(format!("<COM method offset {index:#x} {type_name}>"))
} else {
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<PyObjectRef> {
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::<PyType>().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::<PyTuple>().unwrap();
*self.arg_types.write() = Some(
tuple
.iter()
.map(|obj| obj.clone().downcast::<PyType>().unwrap())
.collect::<Vec<_>>(),
);
Ok(())
}
}
#[pygetset]
fn __name__(&self) -> String {
fn __name__(&self) -> Option<String> {
self.name.read().clone()
}
#[pygetset(setter)]
fn set___name__(&self, name: String) {
*self.name.write() = name;
}
#[pygetset(name = "_restype_")]
fn restype(&self) -> Option<PyTypeRef> {
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(())
}
}

View File

@@ -1,5 +1,41 @@
#[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 {
#[allow(dead_code)]
pub(crate) inner: PyCPointer,
}
#[pyclass]
impl PyCPointerType {}
#[pyclass(
name = "_Pointer",
base = PyCData,
metaclass = "PyCPointerType",
module = "_ctypes"
)]
#[derive(Debug, PyPayload)]
pub struct PyCPointer {
contents: PyRwLock<PyObjectRef>,
}
#[pyclass(flags(BASETYPE, IMMUTABLETYPE))]
impl PyCPointer {}
impl PyCPointer {
// TODO: not correct
#[pygetset]
fn contents(&self) -> PyResult<PyObjectRef> {
let contents = self.contents.read().clone();
Ok(contents)
}
#[pygetset(setter)]
fn set_contents(&self, contents: PyObjectRef) -> PyResult<()> {
*self.contents.write() = contents;
Ok(())
}
}

View File

@@ -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 {}

View File

@@ -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
@@ -343,9 +348,9 @@ else:
libc.rand()
i = c_int(1)
print("start srand")
print(libc.srand(i))
print(test_byte_array)
print(test_byte_array._type_)
# print(libc.srand(i))
# print(test_byte_array)
# print(test_byte_array._type_)
# print("start printf")
# libc.printf(test_byte_array)