mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
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
This commit is contained in:
1 parent
0e6e256f8e
commit
f37ea52565
9 files changed
+494
-164
No files matched your search
@@ -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<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 +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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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!();
|
||||
|
||||
@@ -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<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
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
|
||||
}
|
||||
@@ -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<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>;
|
||||
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>>,
|
||||
pub needs_free: AtomicCell<bool>,
|
||||
pub arg_types: PyRwLock<Option<Vec<PyTypeRef>>>,
|
||||
pub res_type: PyRwLock<Option<PyObjectRef>>,
|
||||
pub _flags_: AtomicCell<i32>,
|
||||
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<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()
|
||||
.map(|t| ReturnType::to_ffi_type(&t.clone().downcast::<PyType>().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::<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();
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let index = index - 0x1000;
|
||||
return Ok(format!("<COM method offset {index:#x} {type_name}>"));
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -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<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(())
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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<PyObjectRef>, // Only for simple objects
|
||||
pub getfunc: Option<PyObjectRef>, // Only for simple objects
|
||||
pub paramfunc: Option<PyObjectRef>,
|
||||
|
||||
/* Following fields only used by PyCFuncPtrType_Type instances */
|
||||
pub argtypes: Option<PyObjectRef>, // tuple of CDataObjects
|
||||
pub converters: Option<PyObjectRef>, // tuple([t.from_param for t in argtypes])
|
||||
pub restype: Option<PyObjectRef>, // CDataObject or NULL
|
||||
pub checker: Option<PyObjectRef>,
|
||||
pub module: Option<PyObjectRef>,
|
||||
pub flags: i32, // calling convention and such
|
||||
pub dict_final: u8,
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user