Skip to content

Commit 23ec5a5

Browse files
authored
Fix import ctypes and Lib/ctypes/__init__.py from cpython3.13.9 (#6304)
* Fix ctypes import blockers * Update Lib/ctypes/__init__.py from cpython3.13.9
1 parent fef9de2 commit 23ec5a5

File tree

5 files changed

+211
-77
lines changed

5 files changed

+211
-77
lines changed

Lib/ctypes/__init__.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,19 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None,
347347
use_errno=False,
348348
use_last_error=False,
349349
winmode=None):
350+
if name:
351+
name = _os.fspath(name)
352+
353+
# If the filename that has been provided is an iOS/tvOS/watchOS
354+
# .fwork file, dereference the location to the true origin of the
355+
# binary.
356+
if name.endswith(".fwork"):
357+
with open(name) as f:
358+
name = _os.path.join(
359+
_os.path.dirname(_sys.executable),
360+
f.read().strip()
361+
)
362+
350363
self._name = name
351364
flags = self._func_flags_
352365
if use_errno:
@@ -467,6 +480,8 @@ def LoadLibrary(self, name):
467480

468481
if _os.name == "nt":
469482
pythonapi = PyDLL("python dll", None, _sys.dllhandle)
483+
elif _sys.platform == "android":
484+
pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2])
470485
elif _sys.platform == "cygwin":
471486
pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
472487
else:
@@ -498,15 +513,14 @@ def WinError(code=None, descr=None):
498513
c_ssize_t = c_longlong
499514

500515
# functions
516+
501517
from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr
502518

503519
## void *memmove(void *, const void *, size_t);
504-
# XXX: RUSTPYTHON
505-
# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
520+
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
506521

507522
## void *memset(void *, int, size_t)
508-
# XXX: RUSTPYTHON
509-
# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
523+
memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
510524

511525
def PYFUNCTYPE(restype, *argtypes):
512526
class CFunctionType(_CFuncPtr):
@@ -515,30 +529,27 @@ class CFunctionType(_CFuncPtr):
515529
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
516530
return CFunctionType
517531

518-
# XXX: RUSTPYTHON
519-
# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
532+
_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
520533
def cast(obj, typ):
521534
return _cast(obj, obj, typ)
522535

523-
# XXX: RUSTPYTHON
524-
# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
536+
_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
525537
def string_at(ptr, size=-1):
526-
"""string_at(addr[, size]) -> string
538+
"""string_at(ptr[, size]) -> string
527539
528-
Return the string at addr."""
540+
Return the byte string at void *ptr."""
529541
return _string_at(ptr, size)
530542

531543
try:
532544
from _ctypes import _wstring_at_addr
533545
except ImportError:
534546
pass
535547
else:
536-
# XXX: RUSTPYTHON
537-
# _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
548+
_wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
538549
def wstring_at(ptr, size=-1):
539-
"""wstring_at(addr[, size]) -> string
550+
"""wstring_at(ptr[, size]) -> string
540551
541-
Return the string at addr."""
552+
Return the wide-character string at void *ptr."""
542553
return _wstring_at(ptr, size)
543554

544555

crates/vm/src/stdlib/ctypes.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ pub(crate) mod _ctypes {
189189
}
190190
}
191191

192+
#[cfg(windows)]
192193
#[pyfunction(name = "LoadLibrary")]
193194
fn load_library_windows(
194195
name: String,
@@ -203,20 +204,33 @@ pub(crate) mod _ctypes {
203204
Ok(id)
204205
}
205206

207+
#[cfg(not(windows))]
206208
#[pyfunction(name = "dlopen")]
207209
fn load_library_unix(
208-
name: String,
210+
name: Option<String>,
209211
_load_flags: OptionalArg<i32>,
210212
vm: &VirtualMachine,
211213
) -> PyResult<usize> {
212214
// TODO: audit functions first
213215
// TODO: load_flags
214-
let cache = library::libcache();
215-
let mut cache_write = cache.write();
216-
let (id, _) = cache_write
217-
.get_or_insert_lib(&name, vm)
218-
.map_err(|e| vm.new_os_error(e.to_string()))?;
219-
Ok(id)
216+
match name {
217+
Some(name) => {
218+
let cache = library::libcache();
219+
let mut cache_write = cache.write();
220+
let (id, _) = cache_write
221+
.get_or_insert_lib(&name, vm)
222+
.map_err(|e| vm.new_os_error(e.to_string()))?;
223+
Ok(id)
224+
}
225+
None => {
226+
// If None, call libc::dlopen(null, mode) to get the current process handle
227+
let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) };
228+
if handle.is_null() {
229+
return Err(vm.new_os_error("dlopen() error"));
230+
}
231+
Ok(handle as usize)
232+
}
233+
}
220234
}
221235

222236
#[pyfunction(name = "FreeLibrary")]
@@ -228,10 +242,57 @@ pub(crate) mod _ctypes {
228242
}
229243

230244
#[pyfunction(name = "POINTER")]
231-
pub fn pointer(_cls: PyTypeRef) {}
245+
pub fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult {
246+
// Get the _pointer_type_cache
247+
let ctypes_module = vm.import("_ctypes", 0)?;
248+
let cache = ctypes_module.get_attr("_pointer_type_cache", vm)?;
249+
250+
// Check if already in cache using __getitem__
251+
if let Ok(cached) = vm.call_method(&cache, "__getitem__", (cls.clone(),))
252+
&& !vm.is_none(&cached)
253+
{
254+
return Ok(cached);
255+
}
256+
257+
// Get the _Pointer base class
258+
let pointer_base = ctypes_module.get_attr("_Pointer", vm)?;
259+
260+
// Create the name for the pointer type
261+
let name = if let Ok(type_obj) = cls.get_attr("__name__", vm) {
262+
format!("LP_{}", type_obj.str(vm)?)
263+
} else if let Ok(s) = cls.str(vm) {
264+
format!("LP_{}", s)
265+
} else {
266+
"LP_unknown".to_string()
267+
};
268+
269+
// Create a new type that inherits from _Pointer
270+
let type_type = &vm.ctx.types.type_type;
271+
let bases = vm.ctx.new_tuple(vec![pointer_base]);
272+
let dict = vm.ctx.new_dict();
273+
dict.set_item("_type_", cls.clone(), vm)?;
274+
275+
let new_type = type_type
276+
.as_object()
277+
.call((vm.ctx.new_str(name), bases, dict), vm)?;
278+
279+
// Store in cache using __setitem__
280+
vm.call_method(&cache, "__setitem__", (cls, new_type.clone()))?;
281+
282+
Ok(new_type)
283+
}
232284

233285
#[pyfunction(name = "pointer")]
234-
pub fn pointer_fn(_inst: PyObjectRef) {}
286+
pub fn create_pointer_inst(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
287+
// Get the type of the object
288+
let obj_type = obj.class().to_owned();
289+
290+
// Create pointer type for this object's type
291+
let ptr_type = create_pointer_type(obj_type.into(), vm)?;
292+
293+
// Create an instance of the pointer type with the object
294+
ptr_type.call((obj,), vm)
295+
}
235296

236297
#[pyfunction]
237298
fn _pointer_type_cache() -> PyObjectRef {

crates/vm/src/stdlib/ctypes/array.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,14 @@ impl std::fmt::Debug for PyCArray {
7272
impl Constructor for PyCArray {
7373
type Args = (PyTypeRef, usize);
7474

75-
fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
76-
Ok(Self {
75+
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
76+
Self {
7777
typ: PyRwLock::new(args.0),
7878
length: AtomicCell::new(args.1),
7979
value: PyRwLock::new(vm.ctx.none()),
8080
}
81-
.into_pyobject(vm))
81+
.into_ref_with_type(vm, cls)
82+
.map(Into::into)
8283
}
8384
}
8485

crates/vm/src/stdlib/ctypes/base.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,21 @@ impl PyCSimpleType {
171171
.clone(),
172172
))
173173
}
174+
175+
#[pyclassmethod]
176+
fn from_param(cls: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
177+
// If the value is already an instance of the requested type, return it
178+
if value.fast_isinstance(&cls) {
179+
return Ok(value);
180+
}
181+
182+
// Check for _as_parameter_ attribute
183+
let Ok(as_parameter) = value.get_attr("_as_parameter_", vm) else {
184+
return Err(vm.new_type_error("wrong type"));
185+
};
186+
187+
PyCSimpleType::from_param(cls, as_parameter, vm)
188+
}
174189
}
175190

176191
#[pyclass(

crates/vm/src/stdlib/ctypes/function.rs

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -120,60 +120,106 @@ impl Debug for PyCFuncPtr {
120120
}
121121

122122
impl Constructor for PyCFuncPtr {
123-
type Args = (PyTupleRef, FuncArgs);
123+
type Args = FuncArgs;
124124

125-
fn py_new(_cls: PyTypeRef, (tuple, _args): Self::Args, vm: &VirtualMachine) -> PyResult {
126-
let name = tuple
127-
.first()
128-
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
129-
.downcast_ref::<PyStr>()
130-
.ok_or(vm.new_type_error("Expected a string"))?
131-
.to_string();
132-
let handler = tuple
133-
.into_iter()
134-
.nth(1)
135-
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
136-
.clone();
137-
let handle = handler.try_int(vm);
138-
let handle = match handle {
139-
Ok(handle) => handle.as_bigint().clone(),
140-
Err(_) => handler
141-
.get_attr("_handle", vm)?
142-
.try_int(vm)?
143-
.as_bigint()
144-
.clone(),
145-
};
146-
let library_cache = crate::stdlib::ctypes::library::libcache().read();
147-
let library = library_cache
148-
.get_lib(
149-
handle
150-
.to_usize()
151-
.ok_or(vm.new_value_error("Invalid handle".to_string()))?,
152-
)
153-
.ok_or_else(|| vm.new_value_error("Library not found".to_string()))?;
154-
let inner_lib = library.lib.lock();
155-
156-
let terminated = format!("{}\0", &name);
157-
let code_ptr = if let Some(lib) = &*inner_lib {
158-
let pointer: Symbol<'_, FP> = unsafe {
159-
lib.get(terminated.as_bytes())
160-
.map_err(|err| err.to_string())
161-
.map_err(|err| vm.new_attribute_error(err))?
125+
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
126+
// Handle different argument forms like CPython:
127+
// 1. Empty args: create uninitialized
128+
// 2. One integer argument: function address
129+
// 3. Tuple argument: (name, dll) form
130+
131+
if args.args.is_empty() {
132+
return Self {
133+
ptr: PyRwLock::new(None),
134+
needs_free: AtomicCell::new(false),
135+
arg_types: PyRwLock::new(None),
136+
_flags_: AtomicCell::new(0),
137+
res_type: PyRwLock::new(None),
138+
name: PyRwLock::new(None),
139+
handler: vm.ctx.none(),
140+
}
141+
.into_ref_with_type(vm, cls)
142+
.map(Into::into);
143+
}
144+
145+
let first_arg = &args.args[0];
146+
147+
// Check if first argument is an integer (function address)
148+
if let Ok(addr) = first_arg.try_int(vm) {
149+
let ptr_val = addr.as_bigint().to_usize().unwrap_or(0);
150+
return Self {
151+
ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))),
152+
needs_free: AtomicCell::new(false),
153+
arg_types: PyRwLock::new(None),
154+
_flags_: AtomicCell::new(0),
155+
res_type: PyRwLock::new(None),
156+
name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))),
157+
handler: vm.ctx.new_int(ptr_val).into(),
158+
}
159+
.into_ref_with_type(vm, cls)
160+
.map(Into::into);
161+
}
162+
163+
// Check if first argument is a tuple (name, dll) form
164+
if let Some(tuple) = first_arg.downcast_ref::<PyTuple>() {
165+
let name = tuple
166+
.first()
167+
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
168+
.downcast_ref::<PyStr>()
169+
.ok_or(vm.new_type_error("Expected a string"))?
170+
.to_string();
171+
let handler = tuple
172+
.iter()
173+
.nth(1)
174+
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
175+
.clone();
176+
177+
// Get library handle and load function
178+
let handle = handler.try_int(vm);
179+
let handle = match handle {
180+
Ok(handle) => handle.as_bigint().clone(),
181+
Err(_) => handler
182+
.get_attr("_handle", vm)?
183+
.try_int(vm)?
184+
.as_bigint()
185+
.clone(),
162186
};
163-
Some(CodePtr(*pointer as *mut _))
164-
} else {
165-
None
166-
};
167-
Ok(Self {
168-
ptr: PyRwLock::new(code_ptr),
169-
needs_free: AtomicCell::new(false),
170-
arg_types: PyRwLock::new(None),
171-
_flags_: AtomicCell::new(0),
172-
res_type: PyRwLock::new(None),
173-
name: PyRwLock::new(Some(name)),
174-
handler,
187+
let library_cache = crate::stdlib::ctypes::library::libcache().read();
188+
let library = library_cache
189+
.get_lib(
190+
handle
191+
.to_usize()
192+
.ok_or(vm.new_value_error("Invalid handle".to_string()))?,
193+
)
194+
.ok_or_else(|| vm.new_value_error("Library not found".to_string()))?;
195+
let inner_lib = library.lib.lock();
196+
197+
let terminated = format!("{}\0", &name);
198+
let code_ptr = if let Some(lib) = &*inner_lib {
199+
let pointer: Symbol<'_, FP> = unsafe {
200+
lib.get(terminated.as_bytes())
201+
.map_err(|err| err.to_string())
202+
.map_err(|err| vm.new_attribute_error(err))?
203+
};
204+
Some(CodePtr(*pointer as *mut _))
205+
} else {
206+
None
207+
};
208+
209+
return Self {
210+
ptr: PyRwLock::new(code_ptr),
211+
needs_free: AtomicCell::new(false),
212+
arg_types: PyRwLock::new(None),
213+
_flags_: AtomicCell::new(0),
214+
res_type: PyRwLock::new(None),
215+
name: PyRwLock::new(Some(name)),
216+
handler,
217+
}
218+
.into_ref_with_type(vm, cls)
219+
.map(Into::into);
175220
}
176-
.to_pyobject(vm))
221+
222+
Err(vm.new_type_error("Expected an integer address or a tuple"))
177223
}
178224
}
179225

0 commit comments

Comments
 (0)