From 4bfbdff90552a06df390deeb13bbeea523f2c25f Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 28 Nov 2025 23:39:18 +0900 Subject: [PATCH 1/2] Fix ctypes import blockers --- crates/vm/src/stdlib/ctypes.rs | 79 ++++++++++++++++++++++--- crates/vm/src/stdlib/ctypes/array.rs | 7 ++- crates/vm/src/stdlib/ctypes/base.rs | 15 +++++ crates/vm/src/stdlib/ctypes/function.rs | 7 ++- 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/crates/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs index 92629dcadb8..500ddbfb06b 100644 --- a/crates/vm/src/stdlib/ctypes.rs +++ b/crates/vm/src/stdlib/ctypes.rs @@ -189,6 +189,7 @@ pub(crate) mod _ctypes { } } + #[cfg(windows)] #[pyfunction(name = "LoadLibrary")] fn load_library_windows( name: String, @@ -203,20 +204,33 @@ pub(crate) mod _ctypes { Ok(id) } + #[cfg(not(windows))] #[pyfunction(name = "dlopen")] fn load_library_unix( - name: String, + name: Option, _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) - .map_err(|e| vm.new_os_error(e.to_string()))?; - Ok(id) + match name { + Some(name) => { + let cache = library::libcache(); + let mut cache_write = cache.write(); + let (id, _) = cache_write + .get_or_insert_lib(&name, vm) + .map_err(|e| vm.new_os_error(e.to_string()))?; + Ok(id) + } + None => { + // If None, call libc::dlopen(null, mode) to get the current process handle + let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) }; + if handle.is_null() { + return Err(vm.new_os_error("dlopen() error")); + } + Ok(handle as usize) + } + } } #[pyfunction(name = "FreeLibrary")] @@ -228,10 +242,57 @@ pub(crate) mod _ctypes { } #[pyfunction(name = "POINTER")] - pub fn pointer(_cls: PyTypeRef) {} + pub fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // Get the _pointer_type_cache + let ctypes_module = vm.import("_ctypes", 0)?; + let cache = ctypes_module.get_attr("_pointer_type_cache", vm)?; + + // Check if already in cache using __getitem__ + if let Ok(cached) = vm.call_method(&cache, "__getitem__", (cls.clone(),)) + && !vm.is_none(&cached) + { + return Ok(cached); + } + + // Get the _Pointer base class + let pointer_base = ctypes_module.get_attr("_Pointer", vm)?; + + // Create the name for the pointer type + let name = if let Ok(type_obj) = cls.get_attr("__name__", vm) { + format!("LP_{}", type_obj.str(vm)?) + } else if let Ok(s) = cls.str(vm) { + format!("LP_{}", s) + } else { + "LP_unknown".to_string() + }; + + // Create a new type that inherits from _Pointer + let type_type = &vm.ctx.types.type_type; + let bases = vm.ctx.new_tuple(vec![pointer_base]); + let dict = vm.ctx.new_dict(); + dict.set_item("_type_", cls.clone(), vm)?; + + let new_type = type_type + .as_object() + .call((vm.ctx.new_str(name), bases, dict), vm)?; + + // Store in cache using __setitem__ + vm.call_method(&cache, "__setitem__", (cls, new_type.clone()))?; + + Ok(new_type) + } #[pyfunction(name = "pointer")] - pub fn pointer_fn(_inst: PyObjectRef) {} + pub fn create_pointer_inst(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // Get the type of the object + let obj_type = obj.class().to_owned(); + + // Create pointer type for this object's type + let ptr_type = create_pointer_type(obj_type.into(), vm)?; + + // Create an instance of the pointer type with the object + ptr_type.call((obj,), vm) + } #[pyfunction] fn _pointer_type_cache() -> PyObjectRef { diff --git a/crates/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs index a1adf847a99..a90b5e611c3 100644 --- a/crates/vm/src/stdlib/ctypes/array.rs +++ b/crates/vm/src/stdlib/ctypes/array.rs @@ -72,13 +72,14 @@ impl std::fmt::Debug for PyCArray { impl Constructor for PyCArray { type Args = (PyTypeRef, usize); - fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { - Ok(Self { + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + Self { typ: PyRwLock::new(args.0), length: AtomicCell::new(args.1), value: PyRwLock::new(vm.ctx.none()), } - .into_pyobject(vm)) + .into_ref_with_type(vm, cls) + .map(Into::into) } } diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index 1b07d73f8d2..29f3b9da2ae 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -171,6 +171,21 @@ impl PyCSimpleType { .clone(), )) } + + #[pyclassmethod] + fn from_param(cls: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // If the value is already an instance of the requested type, return it + if value.fast_isinstance(&cls) { + return Ok(value); + } + + // Check for _as_parameter_ attribute + let Ok(as_parameter) = value.get_attr("_as_parameter_", vm) else { + return Err(vm.new_type_error("wrong type")); + }; + + PyCSimpleType::from_param(cls, as_parameter, vm) + } } #[pyclass( diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 64a230ad568..7aded296ea3 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -122,7 +122,7 @@ impl Debug for PyCFuncPtr { impl Constructor for PyCFuncPtr { type Args = (PyTupleRef, FuncArgs); - fn py_new(_cls: PyTypeRef, (tuple, _args): Self::Args, vm: &VirtualMachine) -> PyResult { + 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"))? @@ -164,7 +164,7 @@ impl Constructor for PyCFuncPtr { } else { None }; - Ok(Self { + Self { ptr: PyRwLock::new(code_ptr), needs_free: AtomicCell::new(false), arg_types: PyRwLock::new(None), @@ -173,7 +173,8 @@ impl Constructor for PyCFuncPtr { name: PyRwLock::new(Some(name)), handler, } - .to_pyobject(vm)) + .into_ref_with_type(vm, cls) + .map(Into::into) } } From 7c6612273d8468f423844812483eadc7b8c142e8 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 28 Nov 2025 22:22:56 +0900 Subject: [PATCH 2/2] Update Lib/ctypes/__init__.py from cpython3.13.9 --- Lib/ctypes/__init__.py | 39 ++++--- crates/vm/src/stdlib/ctypes/array.rs | 2 +- crates/vm/src/stdlib/ctypes/function.rs | 149 +++++++++++++++--------- 3 files changed, 123 insertions(+), 67 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index a5d27daff0b..3599e13ed28 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -347,6 +347,19 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): + if name: + name = _os.fspath(name) + + # If the filename that has been provided is an iOS/tvOS/watchOS + # .fwork file, dereference the location to the true origin of the + # binary. + if name.endswith(".fwork"): + with open(name) as f: + name = _os.path.join( + _os.path.dirname(_sys.executable), + f.read().strip() + ) + self._name = name flags = self._func_flags_ if use_errno: @@ -467,6 +480,8 @@ def LoadLibrary(self, name): if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) +elif _sys.platform == "android": + pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2]) elif _sys.platform == "cygwin": pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) else: @@ -498,15 +513,14 @@ def WinError(code=None, descr=None): c_ssize_t = c_longlong # functions + from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr ## void *memmove(void *, const void *, size_t); -# XXX: RUSTPYTHON -# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) +memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) ## void *memset(void *, int, size_t) -# XXX: RUSTPYTHON -# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) +memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) def PYFUNCTYPE(restype, *argtypes): class CFunctionType(_CFuncPtr): @@ -515,17 +529,15 @@ class CFunctionType(_CFuncPtr): _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI return CFunctionType -# XXX: RUSTPYTHON -# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) +_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) def cast(obj, typ): return _cast(obj, obj, typ) -# XXX: RUSTPYTHON -# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) +_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=-1): - """string_at(addr[, size]) -> string + """string_at(ptr[, size]) -> string - Return the string at addr.""" + Return the byte string at void *ptr.""" return _string_at(ptr, size) try: @@ -533,12 +545,11 @@ def string_at(ptr, size=-1): except ImportError: pass else: - # XXX: RUSTPYTHON - # _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) + _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) def wstring_at(ptr, size=-1): - """wstring_at(addr[, size]) -> string + """wstring_at(ptr[, size]) -> string - Return the string at addr.""" + Return the wide-character string at void *ptr.""" return _wstring_at(ptr, size) diff --git a/crates/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs index a90b5e611c3..a46322460a9 100644 --- a/crates/vm/src/stdlib/ctypes/array.rs +++ b/crates/vm/src/stdlib/ctypes/array.rs @@ -79,7 +79,7 @@ impl Constructor for PyCArray { value: PyRwLock::new(vm.ctx.none()), } .into_ref_with_type(vm, cls) - .map(Into::into) + .map(Into::into) } } diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 7aded296ea3..c1db4d58f8d 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -120,61 +120,106 @@ impl Debug for PyCFuncPtr { } impl Constructor for PyCFuncPtr { - type Args = (PyTupleRef, FuncArgs); + type Args = FuncArgs; - 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"))? - .downcast_ref::() - .ok_or(vm.new_type_error("Expected a string"))? - .to_string(); - let handler = tuple - .into_iter() - .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))? + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + // Handle different argument forms like CPython: + // 1. Empty args: create uninitialized + // 2. One integer argument: function address + // 3. Tuple argument: (name, dll) form + + if args.args.is_empty() { + return Self { + ptr: PyRwLock::new(None), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(None), + handler: vm.ctx.none(), + } + .into_ref_with_type(vm, cls) + .map(Into::into); + } + + let first_arg = &args.args[0]; + + // Check if first argument is an integer (function address) + if let Ok(addr) = first_arg.try_int(vm) { + let ptr_val = addr.as_bigint().to_usize().unwrap_or(0); + return Self { + ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))), + handler: vm.ctx.new_int(ptr_val).into(), + } + .into_ref_with_type(vm, cls) + .map(Into::into); + } + + // Check if first argument is a tuple (name, dll) form + if let Some(tuple) = first_arg.downcast_ref::() { + let name = tuple + .first() + .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? + .downcast_ref::() + .ok_or(vm.new_type_error("Expected a string"))? + .to_string(); + let handler = tuple + .iter() + .nth(1) + .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? + .clone(); + + // Get library handle and load function + 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(), }; - Some(CodePtr(*pointer as *mut _)) - } else { - None - }; - Self { - ptr: PyRwLock::new(code_ptr), - needs_free: AtomicCell::new(false), - arg_types: PyRwLock::new(None), - _flags_: AtomicCell::new(0), - res_type: PyRwLock::new(None), - name: PyRwLock::new(Some(name)), - handler, + 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 + }; + + return Self { + ptr: PyRwLock::new(code_ptr), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(name)), + handler, + } + .into_ref_with_type(vm, cls) + .map(Into::into); } - .into_ref_with_type(vm, cls) - .map(Into::into) + + Err(vm.new_type_error("Expected an integer address or a tuple")) } }