From b42520ed4aad2623915a48a61c74a28fdc0a94fe Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 20 Feb 2025 10:37:13 -0800 Subject: [PATCH 01/10] Initial CFuncPtr implementation Signed-off-by: Ashwin Naren --- Cargo.lock | 34 +++--- extra_tests/snippets/builtins_ctypes.py | 132 ++++++++++++++++++++++++ vm/src/stdlib/ctypes.rs | 9 +- vm/src/stdlib/ctypes/function.rs | 96 +++++++++++++++-- vm/src/stdlib/ctypes/library.rs | 17 ++- 5 files changed, 261 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67e0813b7b..2ebf3a5e3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "approx" @@ -1757,7 +1757,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.1", - "zerocopy 0.8.18", + "zerocopy 0.8.20", ] [[package]] @@ -1796,7 +1796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.18", + "zerocopy 0.8.20", ] [[package]] @@ -2421,9 +2421,9 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -2452,9 +2452,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -2463,9 +2463,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -2937,9 +2937,9 @@ checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-normalization" @@ -3449,11 +3449,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.18" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2" +checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" dependencies = [ - "zerocopy-derive 0.8.18", + "zerocopy-derive 0.8.20", ] [[package]] @@ -3469,9 +3469,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.18" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" +checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" dependencies = [ "proc-macro2", "quote", diff --git a/extra_tests/snippets/builtins_ctypes.py b/extra_tests/snippets/builtins_ctypes.py index 5bd6e5ef25..2b9b1856c5 100644 --- a/extra_tests/snippets/builtins_ctypes.py +++ b/extra_tests/snippets/builtins_ctypes.py @@ -1,9 +1,29 @@ import os as _os, sys as _sys +import types as _types +from _ctypes import RTLD_LOCAL, RTLD_GLOBAL from _ctypes import sizeof from _ctypes import _SimpleCData +from _ctypes import CFuncPtr as _CFuncPtr + from struct import calcsize as _calcsize + +DEFAULT_MODE = RTLD_LOCAL +if _os.name == "posix" and _sys.platform == "darwin": + # On OS X 10.3, we use RTLD_GLOBAL as default mode + # because RTLD_LOCAL does not work at least on some + # libraries. OS X 10.3 is Darwin 7, so we check for + # that. + + if int(_os.uname().release.split('.')[0]) < 8: + DEFAULT_MODE = RTLD_GLOBAL + +from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ + FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \ + FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \ + FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR + def create_string_buffer(init, size=None): """create_string_buffer(aBytes) -> character array create_string_buffer(anInteger) -> character array @@ -131,3 +151,115 @@ class c_bool(_SimpleCData): # s = create_string_buffer(b'\000' * 32) assert i.value == 42 assert abs(f.value - 3.14) < 1e-06 + +if _os.name == "nt": + from _ctypes import LoadLibrary as _dlopen + from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL + +class CDLL(object): + """An instance of this class represents a loaded dll/shared + library, exporting functions using the standard C calling + convention (named 'cdecl' on Windows). + + The exported functions can be accessed as attributes, or by + indexing with the function name. Examples: + + .qsort -> callable object + ['qsort'] -> callable object + + Calling the functions releases the Python GIL during the call and + reacquires it afterwards. + """ + _func_flags_ = _FUNCFLAG_CDECL + _func_restype_ = c_int + # default values for repr + _name = '' + _handle = 0 + _FuncPtr = None + + def __init__(self, name, mode=DEFAULT_MODE, handle=None, + use_errno=False, + use_last_error=False, + winmode=None): + self._name = name + flags = self._func_flags_ + if use_errno: + flags |= _FUNCFLAG_USE_ERRNO + if use_last_error: + flags |= _FUNCFLAG_USE_LASTERROR + if _sys.platform.startswith("aix"): + """When the name contains ".a(" and ends with ")", + e.g., "libFOO.a(libFOO.so)" - this is taken to be an + archive(member) syntax for dlopen(), and the mode is adjusted. + Otherwise, name is presented to dlopen() as a file argument. + """ + if name and name.endswith(")") and ".a(" in name: + mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW ) + if _os.name == "nt": + if winmode is not None: + mode = winmode + else: + import nt + mode = 4096 + if '/' in name or '\\' in name: + self._name = nt._getfullpathname(self._name) + mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR + + class _FuncPtr(_CFuncPtr): + _flags_ = flags + _restype_ = self._func_restype_ + self._FuncPtr = _FuncPtr + + if handle is None: + self._handle = _dlopen(self._name, mode) + else: + self._handle = handle + + def __repr__(self): + return "<%s '%s', handle %x at %#x>" % \ + (self.__class__.__name__, self._name, + (self._handle & (_sys.maxsize*2 + 1)), + id(self) & (_sys.maxsize*2 + 1)) + + def __getattr__(self, name): + if name.startswith('__') and name.endswith('__'): + raise AttributeError(name) + func = self.__getitem__(name) + setattr(self, name, func) + return func + + def __getitem__(self, name_or_ordinal): + func = self._FuncPtr((name_or_ordinal, self)) + if not isinstance(name_or_ordinal, int): + func.__name__ = name_or_ordinal + return func + +class LibraryLoader(object): + def __init__(self, dlltype): + self._dlltype = dlltype + + def __getattr__(self, name): + if name[0] == '_': + raise AttributeError(name) + try: + dll = self._dlltype(name) + except OSError: + raise AttributeError(name) + setattr(self, name, dll) + return dll + + def __getitem__(self, name): + return getattr(self, name) + + def LoadLibrary(self, name): + return self._dlltype(name) + + __class_getitem__ = classmethod(_types.GenericAlias) + +cdll = LibraryLoader(CDLL) + +if _os.name == "posix" and _sys.platform == "darwin": + pass +else: + libc = cdll.msvcrt + print("rand", libc.rand()) diff --git a/vm/src/stdlib/ctypes.rs b/vm/src/stdlib/ctypes.rs index 701094a375..43273fcbfd 100644 --- a/vm/src/stdlib/ctypes.rs +++ b/vm/src/stdlib/ctypes.rs @@ -37,7 +37,7 @@ pub(crate) mod _ctypes { use super::base::PyCSimple; use crate::builtins::PyTypeRef; use crate::class::StaticType; - use crate::function::Either; + use crate::function::{Either, OptionalArg}; use crate::stdlib::ctypes::library; use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; @@ -180,8 +180,13 @@ pub(crate) mod _ctypes { } #[pyfunction(name = "LoadLibrary")] - fn load_library(name: String, vm: &VirtualMachine) -> PyResult { + fn load_library( + name: String, + load_flags: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { // TODO: audit functions first + // TODO: load_flags let cache = library::libcache(); let mut cache_write = cache.write(); let lib_ref = cache_write.get_or_insert_lib(&name, vm).unwrap(); diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index a7ee07744b..ac1dfe9c41 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -1,17 +1,77 @@ -use crate::PyObjectRef; +use crate::builtins::PyTypeRef; use crate::stdlib::ctypes::PyCData; +use crate::types::{Callable, Constructor}; +use crate::{Py, PyObjectRef, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; -use rustpython_common::lock::PyRwLock; +use rustpython_common::lock::{PyMutex, PyRwLock}; use std::ffi::c_void; +use std::fmt::Debug; +use std::sync::Arc; + +// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 + +#[derive(Debug)] +pub enum FunctionArgument { + Float(std::ffi::c_float), + Double(std::ffi::c_double), + // TODO: Duplicate char stuff + UChar(std::ffi::c_uchar), + SChar(std::ffi::c_schar), + Char(std::ffi::c_char), + UShort(std::ffi::c_ushort), + Short(std::ffi::c_short), + UInt(std::ffi::c_uint), + Int(std::ffi::c_int), + ULong(std::ffi::c_ulong), + Long(std::ffi::c_long), + ULongLong(std::ffi::c_ulonglong), + LongLong(std::ffi::c_longlong), +} #[derive(Debug)] pub struct Function { - _pointer: *mut c_void, - _arguments: Vec<()>, - _return_type: Box<()>, + // TODO: no protection from use-after-free + pointer: Arc>, + arguments: Vec, + return_type: PyTypeRef, +} + +unsafe impl Send for Function {} +unsafe impl Sync for Function {} + +impl Function { + pub unsafe fn load( + library: &libloading::Library, + function: &str, + args: Vec, + return_type: PyTypeRef, + ) -> PyResult { + let terminated = format!("{}\0", function); + let pointer = library + .get(terminated.as_bytes()) + .map_err(|err| err.to_string()) + .unwrap(); + Ok(Function { + pointer: Arc::new(PyMutex::new(*pointer)), + arguments: args + .iter() + .map(|arg| todo!("convert PyObjectRef to FunctionArgument")) + .collect(), + return_type, + }) + } + + pub unsafe fn call(&self, vm: &VirtualMachine) -> PyObjectRef { + // assemble function type signature + let pointer = self.pointer.lock(); + let f: extern "C" fn() = std::mem::transmute(*pointer); + f(); + vm.ctx.none() + } } #[pyclass(module = "_ctypes", name = "CFuncPtr", base = "PyCData")] +#[derive(PyPayload)] pub struct PyCFuncPtr { pub _name_: String, pub _argtypes_: AtomicCell>, @@ -20,5 +80,29 @@ pub struct PyCFuncPtr { _f: PyRwLock, } -#[pyclass] +impl Debug for PyCFuncPtr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PyCFuncPtr") + .field("_name_", &self._name_) + .finish() + } +} + +impl Constructor for PyCFuncPtr { + type Args = (); + + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + todo!("PyCFuncPtr::py_new") + } +} + +impl Callable for PyCFuncPtr { + type Args = Vec; + + fn call(zelf: &Py, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + todo!() + } +} + +#[pyclass(flags(BASETYPE), with(Callable, Constructor))] impl PyCFuncPtr {} diff --git a/vm/src/stdlib/ctypes/library.rs b/vm/src/stdlib/ctypes/library.rs index f777d26bc0..8d2803df21 100644 --- a/vm/src/stdlib/ctypes/library.rs +++ b/vm/src/stdlib/ctypes/library.rs @@ -1,6 +1,9 @@ use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; -use libloading::Library; +#[cfg(not(target_os = "windows"))] +use libloading::os::unix::{Library, Symbol}; +#[cfg(target_os = "windows")] +use libloading::os::windows::{Library, Symbol}; use rustpython_common::lock::PyRwLock; use std::collections::HashMap; use std::ffi::c_void; @@ -8,6 +11,9 @@ use std::fmt; use std::ptr::null; pub struct SharedLibrary { + #[cfg(target_os = "windows")] + lib: AtomicCell>, + #[cfg(not(target_os = "windows"))] lib: AtomicCell>, } @@ -24,13 +30,20 @@ impl SharedLibrary { }) } + #[cfg(target_os = "windows")] + pub fn new_with_flags(name: &str, flags: u32) -> Result { + Ok(SharedLibrary { + lib: AtomicCell::new(Some(unsafe { Library::load_with_flags(name, flags)? })), + }) + } + #[allow(dead_code)] pub fn get_sym(&self, name: &str) -> Result<*mut c_void, String> { if let Some(inner) = unsafe { &*self.lib.as_ptr() } { unsafe { inner .get(name.as_bytes()) - .map(|f: libloading::Symbol<'_, *mut c_void>| *f) + .map(|f: Symbol<'_, *mut c_void>| *f) .map_err(|err| err.to_string()) } } else { From 885a7ab176f308029617f398199b3803db81ab0a Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 27 Feb 2025 14:06:17 -0800 Subject: [PATCH 02/10] function calling via libffi Signed-off-by: Ashwin Naren --- Cargo.lock | 1 + vm/Cargo.toml | 1 + vm/src/stdlib/ctypes/function.rs | 76 ++++++++++++++++---------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ebf3a5e3e..ec6e5f0295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2282,6 +2282,7 @@ dependencies = [ "itertools 0.14.0", "junction", "libc", + "libffi", "libloading", "log", "malachite-bigint", diff --git a/vm/Cargo.toml b/vm/Cargo.toml index acc645bb74..2479b56557 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -100,6 +100,7 @@ uname = "0.1.1" rustyline = { workspace = true } which = "6" errno = "0.3" +libffi = "3.2" libloading = "0.8" widestring = { workspace = true } diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index ac1dfe9c41..67d0009ce9 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -1,71 +1,71 @@ use crate::builtins::PyTypeRef; use crate::stdlib::ctypes::PyCData; use crate::types::{Callable, Constructor}; -use crate::{Py, PyObjectRef, PyResult, VirtualMachine}; +use crate::{AsObject, Py, PyObjectRef, PyRef, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use rustpython_common::lock::{PyMutex, PyRwLock}; use std::ffi::c_void; use std::fmt::Debug; use std::sync::Arc; - +use crate::class::StaticType; +use crate::stdlib::ctypes::base::PyCSimple; +use libffi::middle::{Arg, Cif, CodePtr, Type}; +use libloading::Symbol; // https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 -#[derive(Debug)] -pub enum FunctionArgument { - Float(std::ffi::c_float), - Double(std::ffi::c_double), - // TODO: Duplicate char stuff - UChar(std::ffi::c_uchar), - SChar(std::ffi::c_schar), - Char(std::ffi::c_char), - UShort(std::ffi::c_ushort), - Short(std::ffi::c_short), - UInt(std::ffi::c_uint), - Int(std::ffi::c_int), - ULong(std::ffi::c_ulong), - Long(std::ffi::c_long), - ULongLong(std::ffi::c_ulonglong), - LongLong(std::ffi::c_longlong), -} #[derive(Debug)] pub struct Function { // TODO: no protection from use-after-free - pointer: Arc>, - arguments: Vec, - return_type: PyTypeRef, + pointer: CodePtr, + cif: Cif } unsafe impl Send for Function {} unsafe impl Sync for Function {} +type FP = unsafe extern "C" fn (); + impl Function { pub unsafe fn load( library: &libloading::Library, function: &str, - args: Vec, - return_type: PyTypeRef, + args: &[PyObjectRef], + ret_type: Option, + vm: &VirtualMachine, ) -> PyResult { + // 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") + } + } else { + todo!("HANDLE ERROR") + } + }).collect::>(); let terminated = format!("{}\0", function); - let pointer = library + let pointer: Symbol = library .get(terminated.as_bytes()) .map_err(|err| err.to_string()) .unwrap(); + let code_ptr = CodePtr(*pointer as *mut _); + let return_type = match ret_type { + Some(t) => todo!("HANDLE RETURN TYPE"), + None => Type::c_int(), + }; + let cif = Cif::new(args.into_iter(), return_type); Ok(Function { - pointer: Arc::new(PyMutex::new(*pointer)), - arguments: args - .iter() - .map(|arg| todo!("convert PyObjectRef to FunctionArgument")) - .collect(), - return_type, + cif, + pointer: code_ptr, }) } - pub unsafe fn call(&self, vm: &VirtualMachine) -> PyObjectRef { - // assemble function type signature - let pointer = self.pointer.lock(); - let f: extern "C" fn() = std::mem::transmute(*pointer); - f(); + pub unsafe fn call(&self, args: Vec, vm: &VirtualMachine) -> PyObjectRef { + let args: Vec = vec![]; + let result = self.cif.call(self.pointer, &args); vm.ctx.none() } } @@ -105,4 +105,6 @@ impl Callable for PyCFuncPtr { } #[pyclass(flags(BASETYPE), with(Callable, Constructor))] -impl PyCFuncPtr {} +impl PyCFuncPtr { + +} From 5bfcccd3db2f442fce72cf70b552997a3949c9c0 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 27 Feb 2025 15:44:33 -0800 Subject: [PATCH 03/10] working no-arg function call Signed-off-by: Ashwin Naren --- vm/src/stdlib/ctypes.rs | 4 +- vm/src/stdlib/ctypes/function.rs | 73 ++++++++++++++++++++++---------- vm/src/stdlib/ctypes/library.rs | 50 +++++----------------- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/vm/src/stdlib/ctypes.rs b/vm/src/stdlib/ctypes.rs index 43273fcbfd..1993fb02a3 100644 --- a/vm/src/stdlib/ctypes.rs +++ b/vm/src/stdlib/ctypes.rs @@ -189,8 +189,8 @@ pub(crate) mod _ctypes { // TODO: load_flags let cache = library::libcache(); let mut cache_write = cache.write(); - let lib_ref = cache_write.get_or_insert_lib(&name, vm).unwrap(); - Ok(lib_ref.get_pointer()) + let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap(); + Ok(id) } #[pyfunction(name = "FreeLibrary")] diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index 67d0009ce9..aa8a01a686 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -1,16 +1,17 @@ -use crate::builtins::PyTypeRef; +use crate::builtins::{PyStr, PyTuple, PyTypeRef}; use crate::stdlib::ctypes::PyCData; use crate::types::{Callable, Constructor}; -use crate::{AsObject, Py, PyObjectRef, PyRef, PyResult, VirtualMachine}; +use crate::{AsObject, Py, PyObjectRef, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; -use rustpython_common::lock::{PyMutex, PyRwLock}; -use std::ffi::c_void; use std::fmt::Debug; -use std::sync::Arc; 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 @@ -31,7 +32,7 @@ impl Function { library: &libloading::Library, function: &str, args: &[PyObjectRef], - ret_type: Option, + ret_type: &Option, vm: &VirtualMachine, ) -> PyResult { // map each arg to a PyCSimple @@ -47,10 +48,10 @@ impl Function { } }).collect::>(); let terminated = format!("{}\0", function); - let pointer: Symbol = library + let pointer: Symbol = unsafe { library .get(terminated.as_bytes()) .map_err(|err| err.to_string()) - .unwrap(); + .unwrap() }; let code_ptr = CodePtr(*pointer as *mut _); let return_type = match ret_type { Some(t) => todo!("HANDLE RETURN TYPE"), @@ -65,46 +66,74 @@ impl Function { pub unsafe fn call(&self, args: Vec, vm: &VirtualMachine) -> PyObjectRef { let args: Vec = vec![]; - let result = self.cif.call(self.pointer, &args); - vm.ctx.none() + // TODO: FIX return type + let result: i32 = unsafe { self.cif.call(self.pointer, &args) }; + vm.ctx.new_int(result).into() } } #[pyclass(module = "_ctypes", name = "CFuncPtr", base = "PyCData")] #[derive(PyPayload)] pub struct PyCFuncPtr { - pub _name_: String, - pub _argtypes_: AtomicCell>, - pub _restype_: AtomicCell, - _handle: PyObjectRef, - _f: PyRwLock, + 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 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("name", &self.name) .finish() } } impl Constructor for PyCFuncPtr { - type Args = (); + type Args = FuncArgs; fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { - todo!("PyCFuncPtr::py_new") + let tuple = args.args.first().unwrap(); + let tuple: &Py = tuple.downcast_ref().unwrap(); + let name = tuple.first().unwrap().downcast_ref::().unwrap().to_string(); + let handler = tuple.into_iter().nth(1).unwrap().clone(); + Ok(Self { + _flags_: AtomicCell::new(0), + name: PyRwLock::new(name), + _restype_: PyRwLock::new(None), + handler + }.to_pyobject(vm)) } } impl Callable for PyCFuncPtr { - type Args = Vec; - - fn call(zelf: &Py, _args: Self::Args, vm: &VirtualMachine) -> PyResult { - todo!() + 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().unwrap()).unwrap(); + 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)) + } } } #[pyclass(flags(BASETYPE), with(Callable, Constructor))] impl PyCFuncPtr { + #[pygetset(magic)] + fn name(&self) -> String { + self.name.read().clone() + } + #[pygetset(setter, magic)] + fn set_name(&self, name: String) { + *self.name.write() = name; + } } diff --git a/vm/src/stdlib/ctypes/library.rs b/vm/src/stdlib/ctypes/library.rs index 8d2803df21..aefbc9cd9c 100644 --- a/vm/src/stdlib/ctypes/library.rs +++ b/vm/src/stdlib/ctypes/library.rs @@ -1,20 +1,13 @@ use crate::VirtualMachine; -use crossbeam_utils::atomic::AtomicCell; -#[cfg(not(target_os = "windows"))] -use libloading::os::unix::{Library, Symbol}; -#[cfg(target_os = "windows")] -use libloading::os::windows::{Library, Symbol}; -use rustpython_common::lock::PyRwLock; +use libloading::Library; +use rustpython_common::lock::{PyMutex, PyRwLock}; use std::collections::HashMap; use std::ffi::c_void; use std::fmt; use std::ptr::null; pub struct SharedLibrary { - #[cfg(target_os = "windows")] - lib: AtomicCell>, - #[cfg(not(target_os = "windows"))] - lib: AtomicCell>, + pub(crate) lib: PyMutex>, } impl fmt::Debug for SharedLibrary { @@ -26,33 +19,13 @@ impl fmt::Debug for SharedLibrary { impl SharedLibrary { pub fn new(name: &str) -> Result { Ok(SharedLibrary { - lib: AtomicCell::new(Some(unsafe { Library::new(name)? })), + lib: PyMutex::new(unsafe { Some(Library::new(name)?) }), }) } - #[cfg(target_os = "windows")] - pub fn new_with_flags(name: &str, flags: u32) -> Result { - Ok(SharedLibrary { - lib: AtomicCell::new(Some(unsafe { Library::load_with_flags(name, flags)? })), - }) - } - - #[allow(dead_code)] - pub fn get_sym(&self, name: &str) -> Result<*mut c_void, String> { - if let Some(inner) = unsafe { &*self.lib.as_ptr() } { - unsafe { - inner - .get(name.as_bytes()) - .map(|f: Symbol<'_, *mut c_void>| *f) - .map_err(|err| err.to_string()) - } - } else { - Err("The library has been closed".to_string()) - } - } - pub fn get_pointer(&self) -> usize { - if let Some(l) = unsafe { &*self.lib.as_ptr() } { + let lib_lock = self.lib.lock(); + if let Some(l) = &*lib_lock { l as *const Library as usize } else { null::() as usize @@ -60,13 +33,12 @@ impl SharedLibrary { } pub fn is_closed(&self) -> bool { - unsafe { &*self.lib.as_ptr() }.is_none() + let lib_lock = self.lib.lock(); + &*lib_lock.is_none() } pub fn close(&self) { - let old = self.lib.take(); - self.lib.store(None); - drop(old); + *self.lib.lock() = None; } } @@ -96,7 +68,7 @@ impl ExternalLibs { &mut self, library_path: &str, _vm: &VirtualMachine, - ) -> Result<&SharedLibrary, libloading::Error> { + ) -> Result<(usize, &SharedLibrary), libloading::Error> { let nlib = SharedLibrary::new(library_path)?; let key = nlib.get_pointer(); @@ -111,7 +83,7 @@ impl ExternalLibs { } }; - Ok(self.libraries.get(&key).unwrap()) + Ok((key, self.libraries.get(&key).unwrap())) } pub fn drop_lib(&mut self, key: usize) { From afd1f64b88dcbbb700c611e1fbe6991e0863289b Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 27 Feb 2025 15:51:27 -0800 Subject: [PATCH 04/10] fix warnings Signed-off-by: Ashwin Naren --- vm/src/stdlib/ctypes.rs | 2 +- vm/src/stdlib/ctypes/function.rs | 8 ++++---- vm/src/stdlib/ctypes/library.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vm/src/stdlib/ctypes.rs b/vm/src/stdlib/ctypes.rs index 1993fb02a3..ea4a85b757 100644 --- a/vm/src/stdlib/ctypes.rs +++ b/vm/src/stdlib/ctypes.rs @@ -182,7 +182,7 @@ pub(crate) mod _ctypes { #[pyfunction(name = "LoadLibrary")] fn load_library( name: String, - load_flags: OptionalArg, + _load_flags: OptionalArg, vm: &VirtualMachine, ) -> PyResult { // TODO: audit functions first diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index aa8a01a686..3812fd0ecd 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -39,7 +39,7 @@ impl Function { 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(); + let _value = arg.get_attr("value", vm).unwrap(); match &*arg_type { _ => todo!("HANDLE ARG TYPE") } @@ -54,7 +54,7 @@ impl Function { .unwrap() }; let code_ptr = CodePtr(*pointer as *mut _); let return_type = match ret_type { - Some(t) => todo!("HANDLE RETURN TYPE"), + Some(_t) => todo!("HANDLE RETURN TYPE"), None => Type::c_int(), }; let cif = Cif::new(args.into_iter(), return_type); @@ -64,7 +64,7 @@ impl Function { }) } - pub unsafe fn call(&self, args: Vec, vm: &VirtualMachine) -> PyObjectRef { + pub unsafe fn call(&self, _args: Vec, vm: &VirtualMachine) -> PyObjectRef { let args: Vec = vec![]; // TODO: FIX return type let result: i32 = unsafe { self.cif.call(self.pointer, &args) }; @@ -94,7 +94,7 @@ impl Debug for PyCFuncPtr { impl Constructor for PyCFuncPtr { type Args = FuncArgs; - fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { let tuple = args.args.first().unwrap(); let tuple: &Py = tuple.downcast_ref().unwrap(); let name = tuple.first().unwrap().downcast_ref::().unwrap().to_string(); diff --git a/vm/src/stdlib/ctypes/library.rs b/vm/src/stdlib/ctypes/library.rs index aefbc9cd9c..74a601a488 100644 --- a/vm/src/stdlib/ctypes/library.rs +++ b/vm/src/stdlib/ctypes/library.rs @@ -34,7 +34,7 @@ impl SharedLibrary { pub fn is_closed(&self) -> bool { let lib_lock = self.lib.lock(); - &*lib_lock.is_none() + lib_lock.is_none() } pub fn close(&self) { From aeba7ada5e90e82cbc0a08f3dd35f8cd935c287b Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 27 Feb 2025 17:22:04 -0800 Subject: [PATCH 05/10] error handling Signed-off-by: Ashwin Naren --- vm/src/stdlib/ctypes/base.rs | 25 +++++++ vm/src/stdlib/ctypes/function.rs | 117 +++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 36 deletions(-) diff --git a/vm/src/stdlib/ctypes/base.rs b/vm/src/stdlib/ctypes/base.rs index a4147c62b2..5c5396be29 100644 --- a/vm/src/stdlib/ctypes/base.rs +++ b/vm/src/stdlib/ctypes/base.rs @@ -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 { + 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_ { diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index 3812fd0ecd..2fedd924d0 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -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( @@ -36,25 +36,29 @@ impl Function { vm: &VirtualMachine, ) -> PyResult { // 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::() { + 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::>(); + }) + .collect::>>()?; let terminated = format!("{}\0", function); - let pointer: Symbol = unsafe { library - .get(terminated.as_bytes()) - .map_err(|err| err.to_string()) - .unwrap() }; + let pointer: Symbol = 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); @@ -64,11 +68,25 @@ impl Function { }) } - pub unsafe fn call(&self, _args: Vec, vm: &VirtualMachine) -> PyObjectRef { - let args: Vec = vec![]; + pub unsafe fn call( + &self, + args: Vec, + vm: &VirtualMachine, + ) -> PyResult { + let args = args + .into_iter() + .map(|arg| { + if let Some(data) = arg.downcast_ref::() { + dbg!(&data); + todo!("HANDLE ARGUMENTS") + } else { + Err(vm.new_type_error("Expected a ctypes simple type".to_string())) + } + }) + .collect::>>()?; // 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()) } } @@ -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>, - pub handler: PyObjectRef + pub handler: PyObjectRef, } impl Debug for PyCFuncPtr { @@ -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 = tuple.downcast_ref().unwrap(); - let name = tuple.first().unwrap().downcast_ref::().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 = 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::() + .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)) } } @@ -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)?) } } } From 3871150bce2aa42d85def2755d6c06abf943183b Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 27 Feb 2025 17:24:50 -0800 Subject: [PATCH 06/10] final CI fixes Signed-off-by: Ashwin Naren --- extra_tests/snippets/builtins_ctypes.py | 4 +- vm/Cargo.toml | 4 +- vm/src/stdlib/ctypes.rs | 16 +++- vm/src/stdlib/ctypes/function.rs | 12 +-- vm/src/stdlib/mod.rs | 122 +++++++++++++----------- 5 files changed, 90 insertions(+), 68 deletions(-) diff --git a/extra_tests/snippets/builtins_ctypes.py b/extra_tests/snippets/builtins_ctypes.py index 2b9b1856c5..c5c563a48e 100644 --- a/extra_tests/snippets/builtins_ctypes.py +++ b/extra_tests/snippets/builtins_ctypes.py @@ -155,6 +155,8 @@ class c_bool(_SimpleCData): if _os.name == "nt": from _ctypes import LoadLibrary as _dlopen from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL +elif _os.name == "posix": + from _ctypes import dlopen as _dlopen class CDLL(object): """An instance of this class represents a loaded dll/shared @@ -258,7 +260,7 @@ def LoadLibrary(self, name): cdll = LibraryLoader(CDLL) -if _os.name == "posix" and _sys.platform == "darwin": +if _os.name == "posix" or _sys.platform == "darwin": pass else: libc = cdll.msvcrt diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 2479b56557..330a2beab5 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -100,9 +100,11 @@ uname = "0.1.1" rustyline = { workspace = true } which = "6" errno = "0.3" +widestring = { workspace = true } + +[target.'cfg(all(any(target_os = "linux", target_os = "macos", target_os = "windows"), not(any(target_env = "musl", target_env = "sgx"))))'.dependencies] libffi = "3.2" libloading = "0.8" -widestring = { workspace = true } [target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies] num_cpus = "1.13.1" diff --git a/vm/src/stdlib/ctypes.rs b/vm/src/stdlib/ctypes.rs index ea4a85b757..99866bae70 100644 --- a/vm/src/stdlib/ctypes.rs +++ b/vm/src/stdlib/ctypes.rs @@ -180,7 +180,21 @@ pub(crate) mod _ctypes { } #[pyfunction(name = "LoadLibrary")] - fn load_library( + fn load_library_windows( + name: String, + _load_flags: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + // TODO: audit functions first + // TODO: load_flags + let cache = library::libcache(); + let mut cache_write = cache.write(); + let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap(); + Ok(id) + } + + #[pyfunction(name = "dlopen")] + fn load_library_unix( name: String, _load_flags: OptionalArg, vm: &VirtualMachine, diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index 2fedd924d0..e862f2f75b 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -1,11 +1,10 @@ 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 crate::{Py, PyObjectRef, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use libffi::middle::{Arg, Cif, CodePtr, Type}; use libloading::Symbol; @@ -37,7 +36,7 @@ impl Function { ) -> PyResult { // map each arg to a PyCSimple let args = args - .into_iter() + .iter() .map(|arg| { if let Some(data) = arg.downcast_ref::() { Ok(ffi_type_from_str(&data._type_).unwrap()) @@ -47,7 +46,7 @@ impl Function { }) .collect::>>()?; let terminated = format!("{}\0", function); - let pointer: Symbol = unsafe { + let pointer: Symbol<'_, FP> = unsafe { library .get(terminated.as_bytes()) .map_err(|err| err.to_string()) @@ -61,7 +60,7 @@ impl Function { } None => Type::c_int(), }; - let cif = Cif::new(args.into_iter(), return_type); + let cif = Cif::new(args, return_type); Ok(Function { cif, pointer: code_ptr, @@ -129,7 +128,6 @@ impl Constructor for PyCFuncPtr { .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), @@ -165,7 +163,7 @@ impl Callable for PyCFuncPtr { &res_type, vm, )?; - Ok(func.call(args.args, vm)?) + func.call(args.args, vm) } } } diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 529a40e861..4f7cb492af 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -37,7 +37,10 @@ pub mod posix; #[path = "posix_compat.rs"] pub mod posix; -#[cfg(any(target_family = "unix", target_family = "windows"))] +#[cfg(all( + any(target_os = "linux", target_os = "macos", target_os = "windows"), + not(any(target_env = "musl", target_env = "sgx")) +))] mod ctypes; #[cfg(windows)] pub(crate) mod msvcrt; @@ -73,62 +76,65 @@ pub fn get_module_inits() -> StdlibMap { }}; } modules! { - #[cfg(all())] - { - "atexit" => atexit::make_module, - "_codecs" => codecs::make_module, - "_collections" => collections::make_module, - "errno" => errno::make_module, - "_functools" => functools::make_module, - "itertools" => itertools::make_module, - "_io" => io::make_module, - "marshal" => marshal::make_module, - "_operator" => operator::make_module, - "_signal" => signal::make_module, - "_sre" => sre::make_module, - "_string" => string::make_module, - "time" => time::make_module, - "_typing" => typing::make_module, - "_weakref" => weakref::make_module, - "_imp" => imp::make_module, - "_warnings" => warnings::make_module, - sys::sysconfigdata_name() => sysconfigdata::make_module, - } - // parser related modules: - #[cfg(feature = "rustpython-ast")] - { - "_ast" => ast::make_module, - } - // compiler related modules: - #[cfg(feature = "rustpython-compiler")] - { - "symtable" => symtable::make_module, - } - #[cfg(any(unix, target_os = "wasi"))] - { - "posix" => posix::make_module, - // "fcntl" => fcntl::make_module, - } - #[cfg(feature = "threading")] - { - "_thread" => thread::make_module, - } - // Unix-only - #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] - { - "pwd" => pwd::make_module, - } - // Windows-only - #[cfg(windows)] - { - "nt" => nt::make_module, - "msvcrt" => msvcrt::make_module, - "_winapi" => winapi::make_module, - "winreg" => winreg::make_module, - } - #[cfg(any(target_family = "unix", target_family = "windows"))] - { - "_ctypes" => ctypes::make_module, - } + #[cfg(all())] + { + "atexit" => atexit::make_module, + "_codecs" => codecs::make_module, + "_collections" => collections::make_module, + "errno" => errno::make_module, + "_functools" => functools::make_module, + "itertools" => itertools::make_module, + "_io" => io::make_module, + "marshal" => marshal::make_module, + "_operator" => operator::make_module, + "_signal" => signal::make_module, + "_sre" => sre::make_module, + "_string" => string::make_module, + "time" => time::make_module, + "_typing" => typing::make_module, + "_weakref" => weakref::make_module, + "_imp" => imp::make_module, + "_warnings" => warnings::make_module, + sys::sysconfigdata_name() => sysconfigdata::make_module, + } + // parser related modules: + #[cfg(feature = "rustpython-ast")] + { + "_ast" => ast::make_module, + } + // compiler related modules: + #[cfg(feature = "rustpython-compiler")] + { + "symtable" => symtable::make_module, + } + #[cfg(any(unix, target_os = "wasi"))] + { + "posix" => posix::make_module, + // "fcntl" => fcntl::make_module, + } + #[cfg(feature = "threading")] + { + "_thread" => thread::make_module, + } + // Unix-only + #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] + { + "pwd" => pwd::make_module, + } + // Windows-only + #[cfg(windows)] + { + "nt" => nt::make_module, + "msvcrt" => msvcrt::make_module, + "_winapi" => winapi::make_module, + "winreg" => winreg::make_module, + } + #[cfg(all( + any(target_os = "linux", target_os = "macos", target_os = "windows"), + not(any(target_env = "musl", target_env = "sgx")) + ))] + { + "_ctypes" => ctypes::make_module, + } } } From 2174d76e2ca7f207eb5457f99b3082e2f674f498 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 2 Mar 2025 20:12:16 -0800 Subject: [PATCH 07/10] fix overindent --- vm/src/stdlib/mod.rs | 120 +++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 4f7cb492af..db02aff26c 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -76,65 +76,65 @@ pub fn get_module_inits() -> StdlibMap { }}; } modules! { - #[cfg(all())] - { - "atexit" => atexit::make_module, - "_codecs" => codecs::make_module, - "_collections" => collections::make_module, - "errno" => errno::make_module, - "_functools" => functools::make_module, - "itertools" => itertools::make_module, - "_io" => io::make_module, - "marshal" => marshal::make_module, - "_operator" => operator::make_module, - "_signal" => signal::make_module, - "_sre" => sre::make_module, - "_string" => string::make_module, - "time" => time::make_module, - "_typing" => typing::make_module, - "_weakref" => weakref::make_module, - "_imp" => imp::make_module, - "_warnings" => warnings::make_module, - sys::sysconfigdata_name() => sysconfigdata::make_module, - } - // parser related modules: - #[cfg(feature = "rustpython-ast")] - { - "_ast" => ast::make_module, - } - // compiler related modules: - #[cfg(feature = "rustpython-compiler")] - { - "symtable" => symtable::make_module, - } - #[cfg(any(unix, target_os = "wasi"))] - { - "posix" => posix::make_module, - // "fcntl" => fcntl::make_module, - } - #[cfg(feature = "threading")] - { - "_thread" => thread::make_module, - } - // Unix-only - #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] - { - "pwd" => pwd::make_module, - } - // Windows-only - #[cfg(windows)] - { - "nt" => nt::make_module, - "msvcrt" => msvcrt::make_module, - "_winapi" => winapi::make_module, - "winreg" => winreg::make_module, - } - #[cfg(all( - any(target_os = "linux", target_os = "macos", target_os = "windows"), - not(any(target_env = "musl", target_env = "sgx")) - ))] - { - "_ctypes" => ctypes::make_module, - } + #[cfg(all())] + { + "atexit" => atexit::make_module, + "_codecs" => codecs::make_module, + "_collections" => collections::make_module, + "errno" => errno::make_module, + "_functools" => functools::make_module, + "itertools" => itertools::make_module, + "_io" => io::make_module, + "marshal" => marshal::make_module, + "_operator" => operator::make_module, + "_signal" => signal::make_module, + "_sre" => sre::make_module, + "_string" => string::make_module, + "time" => time::make_module, + "_typing" => typing::make_module, + "_weakref" => weakref::make_module, + "_imp" => imp::make_module, + "_warnings" => warnings::make_module, + sys::sysconfigdata_name() => sysconfigdata::make_module, + } + // parser related modules: + #[cfg(feature = "rustpython-ast")] + { + "_ast" => ast::make_module, + } + // compiler related modules: + #[cfg(feature = "rustpython-compiler")] + { + "symtable" => symtable::make_module, + } + #[cfg(any(unix, target_os = "wasi"))] + { + "posix" => posix::make_module, + // "fcntl" => fcntl::make_module, + } + #[cfg(feature = "threading")] + { + "_thread" => thread::make_module, + } + // Unix-only + #[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))] + { + "pwd" => pwd::make_module, + } + // Windows-only + #[cfg(windows)] + { + "nt" => nt::make_module, + "msvcrt" => msvcrt::make_module, + "_winapi" => winapi::make_module, + "winreg" => winreg::make_module, + } + #[cfg(all( + any(target_os = "linux", target_os = "macos", target_os = "windows"), + not(any(target_env = "musl", target_env = "sgx")) + ))] + { + "_ctypes" => ctypes::make_module, + } } } From b14dba601e241bd55a9e9fbdc858059d9083d120 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 2 Mar 2025 20:12:56 -0800 Subject: [PATCH 08/10] formatting --- vm/src/stdlib/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index db02aff26c..a3180c1c0f 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -134,7 +134,7 @@ pub fn get_module_inits() -> StdlibMap { not(any(target_env = "musl", target_env = "sgx")) ))] { - "_ctypes" => ctypes::make_module, + "_ctypes" => ctypes::make_module, } } } From 00bed7176928f4c56514c2aad36eca7d82ec4de2 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 2 Mar 2025 21:01:59 -0800 Subject: [PATCH 09/10] Update vm/src/stdlib/ctypes/function.rs Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com> --- vm/src/stdlib/ctypes/function.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index e862f2f75b..265a27a08e 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -109,15 +109,9 @@ impl Debug for PyCFuncPtr { } impl Constructor for PyCFuncPtr { - type Args = FuncArgs; + type Args = (PyTupleRef, FuncArgs); - fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { - let tuple = args.args.first().ok_or_else(|| { - vm.new_type_error("CFuncPtr() takes exactly 1 argument (0 given)".to_string()) - })?; - let tuple: &Py = tuple - .downcast_ref() - .ok_or(vm.new_type_error("Expected a tuple".to_string()))?; + fn py_new(_cls: PyTypeRef, (tuple, _args): Self::Args, vm: &VirtualMachine) -> PyResult { let name = tuple .first() .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements".to_string()))? From 9e9c0419aaf27aceda38bd41cb6332d8958c6ebe Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Sun, 2 Mar 2025 21:26:28 -0800 Subject: [PATCH 10/10] fix clippy --- vm/src/stdlib/ctypes/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index 265a27a08e..7d8dc0386a 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -1,4 +1,4 @@ -use crate::builtins::{PyStr, PyTuple, PyTypeRef}; +use crate::builtins::{PyStr, PyTupleRef, PyTypeRef}; use crate::convert::ToPyObject; use crate::function::FuncArgs; use crate::stdlib::ctypes::PyCData;