Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix ctypes import blockers
  • Loading branch information
youknowone committed Nov 28, 2025
commit 4bfbdff90552a06df390deeb13bbeea523f2c25f
79 changes: 70 additions & 9 deletions crates/vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ pub(crate) mod _ctypes {
}
}

#[cfg(windows)]
#[pyfunction(name = "LoadLibrary")]
fn load_library_windows(
name: String,
Expand All @@ -203,20 +204,33 @@ pub(crate) mod _ctypes {
Ok(id)
}

#[cfg(not(windows))]
#[pyfunction(name = "dlopen")]
fn load_library_unix(
name: String,
name: Option<String>,
_load_flags: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
// 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")]
Expand All @@ -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 {
Expand Down
7 changes: 4 additions & 3 deletions crates/vm/src/stdlib/ctypes/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
15 changes: 15 additions & 0 deletions crates/vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 4 additions & 3 deletions crates/vm/src/stdlib/ctypes/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))?
Expand Down Expand Up @@ -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),
Expand All @@ -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)
}
}

Expand Down