8000 _ctypes pt. 3 by arihant2math · Pull Request #5530 · RustPython/RustPython · GitHub
[go: up one dir, main page]

Skip to content

_ctypes pt. 3 #5530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
error handling
Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
  • Loading branch information
arihant2math committed Mar 2, 2025
commit aeba7ada5e90e82cbc0a08f3dd35f8cd935c287b
25 changes: 25 additions & 0 deletions vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use std::fmt::Debug;

pub fn ffi_type_from_str(_type_: &str) -> Option<libffi::middle::Type> {
match _type_ {
"c" => Some(libffi::middle::Type::u8()),
"u" => Some(libffi::middle::Type::u32()),
"b" => Some(libffi::middle::Type::i8()),
"B" => Some(libffi::middle::Type::u8()),
"h" => Some(libffi::middle::Type::i16()),
"H" => Some(libffi::middle::Type::u16()),
"i" => Some(libffi::middle::Type::i32()),
"I" => Some(libffi::middle::Type::u32()),
"l" => Some(libffi::middle::Type::i32()),
"L" => Some(libffi::middle::Type::u32()),
"q" => Some(libffi::middle::Type::i64()),
"Q" => Some(libffi::middle::Type::u64()),
"f" => Some(libffi::middle::Type::f32()),
"d" => Some(libffi::middle::Type::f64()),
"g" => Some(libffi::middle::Type::f64()),
"?" => Some(libffi::middle::Type::u8()),
"z" => Some(libffi::middle::Type::u64()),
"Z" => Some(libffi::middle::Type::u64()),
"P" => Some(libffi::middle::Type::u64()),
_ => None,
}
}

#[allow(dead_code)]
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
match _type_ {
Expand Down
117 changes: 81 additions & 36 deletions vm/src/stdlib/ctypes/function.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
use crate::builtins::{PyStr, PyTuple, PyTypeRef};
use crate::class::StaticType;
use crate::convert::ToPyObject;
use crate::function::FuncArgs;
use crate::stdlib::ctypes::PyCData;
use crate::stdlib::ctypes::base::{PyCSimple, ffi_type_from_str};
use crate::types::{Callable, Constructor};
use crate::{AsObject, Py, PyObjectRef, PyResult, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use std::fmt::Debug;
use crate::class::StaticType;
use crate::stdlib::ctypes::base::PyCSimple;
use libffi::middle::{Arg, Cif, CodePtr, Type};
use libloading::Symbol;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use crate::convert::ToPyObject;
use crate::function::FuncArgs;
// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15
use std::fmt::Debug;

// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15

#[derive(Debug)]
pub struct Function {
// TODO: no protection from use-after-free
pointer: CodePtr,
cif: Cif
cif: Cif,
}

unsafe impl Send for Function {}
unsafe impl Sync for Function {}

type FP = unsafe extern "C" fn ();
type FP = unsafe extern "C" fn();

impl Function {
pub unsafe fn load(
Expand All @@ -36,25 +36,29 @@ impl Function {
vm: &VirtualMachine,
) -> PyResult<Self> {
// map each arg to a PyCSimple
let args = args.into_iter().map(|arg| {
if arg.is_subclass(PyCSimple::static_type().as_object(), vm).unwrap() {
let arg_type = arg.get_attr("_type_", vm).unwrap().str(vm).unwrap().to_string();
let _value = arg.get_attr("value", vm).unwrap();
match &*arg_type {
_ => todo!("HANDLE ARG TYPE")
let args = args
.into_iter()
.map(|arg| {
if let Some(data) = arg.downcast_ref::<PyCSimple>() {
Ok(ffi_type_from_str(&data._type_).unwrap())
} else {
Err(vm.new_type_error("Expected a ctypes simple type".to_string()))
}
} else {
todo!("HANDLE ERROR")
}
}).collect::<Vec<Type>>();
})
.collect::<PyResult<Vec<Type>>>()?;
let terminated = format!("{}\0", function);
let pointer: Symbol<FP> = unsafe { library
.get(terminated.as_bytes())
.map_err(|err| err.to_string())
.unwrap() };
let pointer: Symbol<FP> = unsafe {
library
.get(terminated.as_bytes())
.map_err(|err| err.to_string())
.map_err(|err| vm.new_value_error(err))?
};
let code_ptr = CodePtr(*pointer as *mut _);
let return_type = match ret_type {
Some(_t) => todo!("HANDLE RETURN TYPE"),
// TODO: Fix this
Some(_t) => {
return Err(vm.new_not_implemented_error("Return type not implemented".to_string()));
}
None => Type::c_int(),
};
let cif = Cif::new(args.into_iter(), return_type);
Expand All @@ -64,11 +68,25 @@ impl Function {
})
}

pub unsafe fn call(&self, _args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyObjectRef {
let args: Vec<Arg> = vec![];
pub unsafe fn call(
&self,
args: Vec<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
let args = args
.into_iter()
.map(|arg| {
if let Some(data) = arg.downcast_ref::<PyCSimple>() {
dbg!(&data);
todo!("HANDLE ARGUMENTS")
} else {
Err(vm.new_type_error("Expected a ctypes simple type".to_string()))
}
})
.collect::<PyResult<Vec<Arg>>>()?;
// TODO: FIX return type
let result: i32 = unsafe { self.cif.call(self.pointer, &args) };
vm.ctx.new_int(result).into()
Ok(vm.ctx.new_int(result).into())
}
}

Expand All @@ -80,7 +98,7 @@ pub struct PyCFuncPtr {
// 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 handler: PyObjectRef
pub handler: PyObjectRef,
}

impl Debug for PyCFuncPtr {
Expand All @@ -95,16 +113,31 @@ impl Constructor for PyCFuncPtr {
type Args = FuncArgs;

fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
let tuple = args.args.first().unwrap();
let tuple: &Py<PyTuple> = tuple.downcast_ref().unwrap();
let name = tuple.first().unwrap().downcast_ref::<PyStr>().unwrap().to_string();
let handler = tuple.into_iter().nth(1).unwrap().clone();
let tuple = args.args.first().ok_or_else(|| {
vm.new_type_error("CFuncPtr() takes exactly 1 argument (0 given)".to_string())
})?;
let tuple: &Py<PyTuple> = tuple
.downcast_ref()
.ok_or(vm.new_type_error("Expected a tuple".to_string()))?;
let name = tuple
.first()
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements".to_string()))?
.downcast_ref::<PyStr>()
.ok_or(vm.new_type_error("Expected a string".to_string()))?
.to_string();
let handler = tuple
.into_iter()
.nth(1)
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements".to_string()))?
.to_object()
.clone();
Ok(Self {
_flags_: AtomicCell::new(0),
name: PyRwLock::new(name),
_restype_: PyRwLock::new(None),
handler
}.to_pyobject(vm))
handler,
}
.to_pyobject(vm))
}
}

Expand All @@ -115,12 +148,24 @@ impl Callable for PyCFuncPtr {
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().unwrap()).unwrap();
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 name = zelf.name.read();
let res_type = zelf._restype_.read();
let func = Function::load(inner_lib.as_ref().unwrap(), &name, &args.args, &res_type, vm)?;
Ok(func.call(args.args, vm))
let func = Function::load(
inner_lib.as_ref().unwrap(),
&name,
&args.args,
&res_type,
vm,
)?;
Ok(func.call(args.args, vm)?)
}
}
}
Expand Down
0