diff --git a/Cargo.lock b/Cargo.lock index 0d9f4681061..5be177c82c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,9 +216,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "shlex", ] @@ -645,9 +645,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -903,7 +903,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -1026,9 +1026,9 @@ checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" [[package]] name = "lambert_w" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0c98033daa8d13aa2171722fd201bae337924189091f7988bdaff5301fa8c9" +checksum = "45bf98425154bfe790a47b72ac452914f6df9ebfb202bc59e089e29db00258cf" [[package]] name = "lazy_static" @@ -1097,6 +1097,16 @@ dependencies = [ "cc", ] +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libm" version = "0.2.11" @@ -1406,9 +1416,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1447,9 +1457,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1745,8 +1755,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.0", - "zerocopy 0.8.17", + "rand_core 0.9.1", + "zerocopy 0.8.18", ] [[package]] @@ -1766,7 +1776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.0", + "rand_core 0.9.1", ] [[package]] @@ -1780,12 +1790,12 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.17", + "zerocopy 0.8.18", ] [[package]] @@ -2271,6 +2281,7 @@ dependencies = [ "itertools 0.14.0", "junction", "libc", + "libloading", "log", "malachite-bigint", "memchr", @@ -2524,9 +2535,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -2552,15 +2563,15 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "strum" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1475c515a4f03a8a7129bb5228b81a781a86cb0b3fbbc19e1c556d491a401f" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" [[package]] name = "strum_macros" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9688894b43459159c82bfa5a5fa0435c19cbe3c9b427fa1dd7b1ce0c279b18a7" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck", "proc-macro2", @@ -2779,9 +2790,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd" @@ -2995,9 +3006,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" dependencies = [ "atomic", "getrandom 0.3.1", @@ -3185,11 +3196,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.57.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core 0.57.0", + "windows-core", "windows-targets 0.52.6", ] @@ -3202,49 +3213,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.36.1" @@ -3479,11 +3447,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2" dependencies = [ - "zerocopy-derive 0.8.17", + "zerocopy-derive 0.8.18", ] [[package]] @@ -3499,9 +3467,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" dependencies = [ "proc-macro2", "quote", diff --git a/extra_tests/snippets/builtins_ctypes.py b/extra_tests/snippets/builtins_ctypes.py index 5df259cf823..5bd6e5ef25c 100644 --- a/extra_tests/snippets/builtins_ctypes.py +++ b/extra_tests/snippets/builtins_ctypes.py @@ -1,7 +1,29 @@ +import os as _os, sys as _sys + from _ctypes import sizeof from _ctypes import _SimpleCData from struct import calcsize as _calcsize +def create_string_buffer(init, size=None): + """create_string_buffer(aBytes) -> character array + create_string_buffer(anInteger) -> character array + create_string_buffer(aBytes, anInteger) -> character array + """ + if isinstance(init, bytes): + if size is None: + size = len(init)+1 + _sys.audit("ctypes.create_string_buffer", init, size) + buftype = c_char * size + buf = buftype() + buf.value = init + return buf + elif isinstance(init, int): + _sys.audit("ctypes.create_string_buffer", None, init) + buftype = c_char * init + buf = buftype() + return buf + raise TypeError(init) + def _check_size(typ, typecode=None): # Check if sizeof(ctypes_type) against struct.calcsize. This # should protect somewhat against a misconfigured libffi. @@ -103,3 +125,9 @@ class c_void_p(_SimpleCData): class c_bool(_SimpleCData): _type_ = "?" _check_size(c_bool) + +i = c_int(42) +f = c_float(3.14) +# s = create_string_buffer(b'\000' * 32) +assert i.value == 42 +assert abs(f.value - 3.14) < 1e-06 diff --git a/vm/Cargo.toml b/vm/Cargo.toml index f092e153a03..14ee6378fd8 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" +libloading = "0.8" widestring = { workspace = true } [target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies] diff --git a/vm/src/stdlib/ctypes.rs b/vm/src/stdlib/ctypes.rs index d1fc26869f4..027a680951a 100644 --- a/vm/src/stdlib/ctypes.rs +++ b/vm/src/stdlib/ctypes.rs @@ -1,11 +1,34 @@ +pub(crate) mod array; pub(crate) mod base; +pub(crate) mod function; +pub(crate) mod library; +pub(crate) mod pointer; +pub(crate) mod structure; +pub(crate) mod union; use crate::builtins::PyModule; -use crate::{PyRef, VirtualMachine}; +use crate::class::PyClassImpl; +use crate::stdlib::ctypes::base::{PyCData, PyCSimple, PySimpleMeta}; +use crate::{Py, PyRef, VirtualMachine}; + +pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py) { + let ctx = &vm.ctx; + PySimpleMeta::make_class(ctx); + extend_module!(vm, module, { + "_CData" => PyCData::make_class(ctx), + "_SimpleCData" => PyCSimple::make_class(ctx), + "Array" => array::PyCArray::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), + "Union" => union::PyCUnion::make_class(ctx), + }) +} pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef { let module = _ctypes::make_module(vm); - base::extend_module_nodes(vm, &module); + extend_module_nodes(vm, &module); module } @@ -15,6 +38,7 @@ pub(crate) mod _ctypes { use crate::builtins::PyTypeRef; use crate::class::StaticType; use crate::function::Either; + use crate::stdlib::ctypes::library; use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use std::ffi::{ @@ -24,6 +48,57 @@ pub(crate) mod _ctypes { use std::mem; use widestring::WideChar; + #[pyattr(name = "__version__")] + const __VERSION__: &str = "1.1.0"; + + // TODO: get properly + #[pyattr(name = "RTLD_LOCAL")] + const RTLD_LOCAL: i32 = 0; + + // TODO: get properly + #[pyattr(name = "RTLD_GLOBAL")] + const RTLD_GLOBAL: i32 = 0; + + #[cfg(target_os = "windows")] + #[pyattr(name = "SIZEOF_TIME_T")] + pub const SIZEOF_TIME_T: usize = 8; + #[cfg(not(target_os = "windows"))] + #[pyattr(name = "SIZEOF_TIME_T")] + pub const SIZEOF_TIME_T: usize = 4; + + #[pyattr(name = "CTYPES_MAX_ARGCOUNT")] + pub const CTYPES_MAX_ARGCOUNT: usize = 1024; + + #[pyattr] + pub const FUNCFLAG_STDCALL: u32 = 0x0; + #[pyattr] + pub const FUNCFLAG_CDECL: u32 = 0x1; + #[pyattr] + pub const FUNCFLAG_HRESULT: u32 = 0x2; + #[pyattr] + pub const FUNCFLAG_PYTHONAPI: u32 = 0x4; + #[pyattr] + pub const FUNCFLAG_USE_ERRNO: u32 = 0x8; + #[pyattr] + pub const FUNCFLAG_USE_LASTERROR: u32 = 0x10; + + #[pyattr] + pub const TYPEFLAG_ISPOINTER: u32 = 0x100; + #[pyattr] + pub const TYPEFLAG_HASPOINTER: u32 = 0x200; + + #[pyattr] + pub const DICTFLAG_FINAL: u32 = 0x1000; + + #[pyattr(once)] + fn error(vm: &VirtualMachine) -> PyTypeRef { + vm.ctx.new_exception_type( + "_ctypes", + "ArgumentError", + Some(vec![vm.ctx.exceptions.exception_type.to_owned()]), + ) + } + pub fn get_size(ty: &str) -> usize { match ty { "u" => mem::size_of::(), @@ -68,7 +143,7 @@ pub(crate) mod _ctypes { } else { Ok(PyCSimple { _type_: tp_str, - _value: AtomicCell::new(vm.ctx.none()), + value: AtomicCell::new(vm.ctx.none()), }) } } else { @@ -95,6 +170,41 @@ pub(crate) mod _ctypes { } } + #[pyfunction(name = "LoadLibrary")] + fn load_library(name: String, vm: &VirtualMachine) -> PyResult { + // TODO: audit functions first + 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()) + } + + #[pyfunction(name = "FreeLibrary")] + fn free_library(handle: usize) -> PyResult<()> { + let cache = library::libcache(); + let mut cache_write = cache.write(); + cache_write.drop_lib(handle); + Ok(()) + } + + #[pyfunction(name = "POINTER")] + pub fn pointer(_cls: PyTypeRef) {} + + #[pyfunction] + pub fn pointer_fn(_inst: PyObjectRef) {} + + #[cfg(target_os = "windows")] + #[pyfunction(name = "_check_HRESULT")] + pub fn check_hresult(_self: PyObjectRef, hr: i32, _vm: &VirtualMachine) -> PyResult { + // TODO: fixme + if hr < 0 { + // vm.ctx.new_windows_error(hr) + todo!(); + } else { + Ok(hr) + } + } + #[pyfunction] fn get_errno() -> i32 { errno::errno().0 diff --git a/vm/src/stdlib/ctypes/array.rs b/vm/src/stdlib/ctypes/array.rs new file mode 100644 index 00000000000..8b023582c9c --- /dev/null +++ b/vm/src/stdlib/ctypes/array.rs @@ -0,0 +1,5 @@ +#[pyclass(name = "Array", module = "_ctypes")] +pub struct PyCArray {} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE))] +impl PyCArray {} diff --git a/vm/src/stdlib/ctypes/base.rs b/vm/src/stdlib/ctypes/base.rs index 0142c453453..5d0316352c1 100644 --- a/vm/src/stdlib/ctypes/base.rs +++ b/vm/src/stdlib/ctypes/base.rs @@ -1,6 +1,10 @@ -use crate::builtins::{PyBytes, PyFloat, PyInt, PyModule, PyNone, PyStr}; -use crate::class::PyClassImpl; -use crate::{Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine}; +use crate::builtins::PyType; +use crate::builtins::{PyBytes, PyFloat, PyInt, PyNone, PyStr, PyTypeRef}; +use crate::convert::ToPyObject; +use crate::function::{Either, OptionalArg}; +use crate::stdlib::ctypes::_ctypes::new_simple_type; +use crate::types::Constructor; +use crate::{AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use num_traits::ToPrimitive; use rustpython_common::lock::PyRwLock; @@ -129,16 +133,32 @@ pub struct PyCData { #[pyclass] impl PyCData {} +#[pyclass(module = "_ctypes", name = "PyCSimpleType", base = "PyType")] +pub struct PySimpleMeta {} + +#[pyclass(flags(BASETYPE))] +impl PySimpleMeta { + #[allow(clippy::new_ret_no_self)] + #[pymethod] + fn new(cls: PyTypeRef, _: OptionalArg, vm: &VirtualMachine) -> PyResult { + Ok(PyObjectRef::from( + new_simple_type(Either::B(&cls), vm)? + .into_ref_with_type(vm, cls)? + .clone(), + )) + } +} + #[pyclass( name = "_SimpleCData", base = "PyCData", - module = "_ctypes" - // TODO: metaclass + module = "_ctypes", + metaclass = "PySimpleMeta" )] #[derive(PyPayload)] pub struct PyCSimple { pub _type_: String, - pub _value: AtomicCell, + pub value: AtomicCell, } impl Debug for PyCSimple { @@ -149,13 +169,56 @@ impl Debug for PyCSimple { } } -#[pyclass(flags(BASETYPE))] -impl PyCSimple {} +impl Constructor for PyCSimple { + type Args = (OptionalArg,); -pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py) { - let ctx = &vm.ctx; - extend_module!(vm, module, { - "_CData" => PyCData::make_class(ctx), - "_SimpleCData" => PyCSimple::make_class(ctx), - }) + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + let attributes = cls.get_attributes(); + let _type_ = attributes + .iter() + .find(|(&k, _)| k.to_object().str(vm).unwrap().to_string() == *"_type_") + .unwrap() + .1 + .str(vm)? + .to_string(); + let value = if let Some(ref v) = args.0.into_option() { + set_primitive(_type_.as_str(), v, vm)? + } else { + match _type_.as_str() { + "c" | "u" => PyObjectRef::from(vm.ctx.new_bytes(vec![0])), + "b" | "B" | "h" | "H" | "i" | "I" | "l" | "q" | "L" | "Q" => { + PyObjectRef::from(vm.ctx.new_int(0)) + } + "f" | "d" | "g" => PyObjectRef::from(vm.ctx.new_float(0.0)), + "?" => PyObjectRef::from(vm.ctx.new_bool(false)), + _ => vm.ctx.none(), // "z" | "Z" | "P" + } + }; + Ok(PyCSimple { + _type_, + value: AtomicCell::new(value), + } + .to_pyobject(vm)) + } +} + +#[pyclass(flags(BASETYPE), with(Constructor))] +impl PyCSimple { + #[pygetset(name = "value")] + pub fn value(instance: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let zelf: &Py = instance + .downcast_ref() + .ok_or_else(|| vm.new_type_error("cannot get value of instance".to_string()))?; + Ok(unsafe { (*zelf.value.as_ptr()).clone() }) + } + + #[pygetset(name = "value", setter)] + fn set_value(instance: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let zelf: PyRef = instance + .downcast() + .map_err(|_| vm.new_type_error("cannot set value of instance".to_string()))?; + let content = set_primitive(zelf._type_.as_str(), &value, vm)?; + zelf.value.store(content); + Ok(()) + } } diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs new file mode 100644 index 00000000000..bf7f0b9adfb --- /dev/null +++ b/vm/src/stdlib/ctypes/function.rs @@ -0,0 +1,24 @@ +use crate::stdlib::ctypes::PyCData; +use crate::PyObjectRef; +use crossbeam_utils::atomic::AtomicCell; +use rustpython_common::lock::PyRwLock; +use std::ffi::c_void; + +#[derive(Debug)] +pub struct Function { + _pointer: *mut c_void, + _arguments: Vec<()>, + _return_type: Box<()>, +} + +#[pyclass(module = "_ctypes", name = "CFuncPtr", base = "PyCData")] +pub struct PyCFuncPtr { + pub _name_: String, + pub _argtypes_: AtomicCell>, + pub _restype_: AtomicCell, + _handle: PyObjectRef, + _f: PyRwLock, +} + +#[pyclass] +impl PyCFuncPtr {} diff --git a/vm/src/stdlib/ctypes/library.rs b/vm/src/stdlib/ctypes/library.rs new file mode 100644 index 00000000000..94b63274407 --- /dev/null +++ b/vm/src/stdlib/ctypes/library.rs @@ -0,0 +1,115 @@ +use crate::VirtualMachine; +use crossbeam_utils::atomic::AtomicCell; +use libloading::Library; +use rustpython_common::lock::PyRwLock; +use std::collections::HashMap; +use std::ffi::c_void; +use std::fmt; +use std::ptr::null; + +pub struct SharedLibrary { + lib: AtomicCell>, +} + +impl fmt::Debug for SharedLibrary { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SharedLibrary") + } +} + +impl SharedLibrary { + pub fn new(name: &str) -> Result { + Ok(SharedLibrary { + lib: AtomicCell::new(Some(unsafe { Library::new(name)? })), + }) + } + + #[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_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() } { + l as *const Library as usize + } else { + null::() as usize + } + } + + pub fn is_closed(&self) -> bool { + unsafe { &*self.lib.as_ptr() }.is_none() + } + + pub fn close(&self) { + let old = self.lib.take(); + self.lib.store(None); + drop(old); + } +} + +impl Drop for SharedLibrary { + fn drop(&mut self) { + self.close(); + } +} + +pub struct ExternalLibs { + libraries: HashMap, +} + +impl ExternalLibs { + pub fn new() -> Self { + Self { + libraries: HashMap::new(), + } + } + + #[allow(dead_code)] + pub fn get_lib(&self, key: usize) -> Option<&SharedLibrary> { + self.libraries.get(&key) + } + + pub fn get_or_insert_lib( + &mut self, + library_path: &str, + _vm: &VirtualMachine, + ) -> Result<&SharedLibrary, libloading::Error> { + let nlib = SharedLibrary::new(library_path)?; + let key = nlib.get_pointer(); + + match self.libraries.get(&key) { + Some(l) => { + if l.is_closed() { + self.libraries.insert(key, nlib); + } + } + _ => { + self.libraries.insert(key, nlib); + } + }; + + Ok(self.libraries.get(&key).unwrap()) + } + + pub fn drop_lib(&mut self, key: usize) { + self.libraries.remove(&key); + } +} + +rustpython_common::static_cell! { + static LIBCACHE: PyRwLock; +} + +pub fn libcache() -> &'static PyRwLock { + LIBCACHE.get_or_init(|| PyRwLock::new(ExternalLibs::new())) +} diff --git a/vm/src/stdlib/ctypes/pointer.rs b/vm/src/stdlib/ctypes/pointer.rs new file mode 100644 index 00000000000..d1360f9862e --- /dev/null +++ b/vm/src/stdlib/ctypes/pointer.rs @@ -0,0 +1,5 @@ +#[pyclass(name = "Pointer", module = "_ctypes")] +pub struct PyCPointer {} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE))] +impl PyCPointer {} diff --git a/vm/src/stdlib/ctypes/structure.rs b/vm/src/stdlib/ctypes/structure.rs new file mode 100644 index 00000000000..13cca6c2603 --- /dev/null +++ b/vm/src/stdlib/ctypes/structure.rs @@ -0,0 +1,5 @@ +#[pyclass(name = "Structure", module = "_ctypes")] +pub struct PyCStructure {} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE))] +impl PyCStructure {} diff --git a/vm/src/stdlib/ctypes/union.rs b/vm/src/stdlib/ctypes/union.rs new file mode 100644 index 00000000000..5a39d9062e2 --- /dev/null +++ b/vm/src/stdlib/ctypes/union.rs @@ -0,0 +1,5 @@ +#[pyclass(name = "Union", module = "_ctypes")] +pub struct PyCUnion {} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE))] +impl PyCUnion {}