Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ uname = "0.1.1"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rustyline = { workspace = true }
which = "6"
errno = "0.3"
widestring = { workspace = true }

[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies]
num_cpus = "1.13.1"

[target.'cfg(windows)'.dependencies]
junction = { workspace = true }
schannel = { workspace = true }
widestring = { workspace = true }
winreg = "0.52"

[target.'cfg(windows)'.dependencies.windows]
Expand Down
107 changes: 107 additions & 0 deletions vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
pub(crate) mod base;

use crate::builtins::PyModule;
use crate::{PyRef, VirtualMachine};

pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = _ctypes::make_module(vm);
base::extend_module_nodes(vm, &module);
module
}

#[pymodule]
pub(crate) mod _ctypes {
use super::base::PyCSimple;
use crate::builtins::PyTypeRef;
use crate::class::StaticType;
use crate::function::Either;
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use std::ffi::{
c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong,
c_ulonglong,
};
use std::mem;
use widestring::WideChar;

pub fn get_size(ty: &str) -> usize {
match ty {
"u" => mem::size_of::<WideChar>(),
"c" | "b" => mem::size_of::<c_schar>(),
"h" => mem::size_of::<c_short>(),
"H" => mem::size_of::<c_short>(),
"i" => mem::size_of::<c_int>(),
"I" => mem::size_of::<c_uint>(),
"l" => mem::size_of::<c_long>(),
"q" => mem::size_of::<c_longlong>(),
"L" => mem::size_of::<c_ulong>(),
"Q" => mem::size_of::<c_ulonglong>(),
"f" => mem::size_of::<c_float>(),
"d" | "g" => mem::size_of::<c_double>(),
"?" | "B" => mem::size_of::<c_uchar>(),
"P" | "z" | "Z" => mem::size_of::<usize>(),
_ => unreachable!(),
}
}

const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?";

pub fn new_simple_type(
cls: Either<&PyObjectRef, &PyTypeRef>,
vm: &VirtualMachine,
) -> PyResult<PyCSimple> {
let cls = match cls {
Either::A(obj) => obj,
Either::B(typ) => typ.as_object(),
};

if let Ok(_type_) = cls.get_attr("_type_", vm) {
if _type_.is_instance((&vm.ctx.types.str_type).as_ref(), vm)? {
let tp_str = _type_.str(vm)?.to_string();

if tp_str.len() != 1 {
Err(vm.new_value_error(
format!("class must define a '_type_' attribute which must be a string of length 1, str: {tp_str}"),
))
} else if !SIMPLE_TYPE_CHARS.contains(tp_str.as_str()) {
Err(vm.new_attribute_error(format!("class must define a '_type_' attribute which must be\n a single character string containing one of {SIMPLE_TYPE_CHARS}, currently it is {tp_str}.")))
} else {
Ok(PyCSimple {
_type_: tp_str,
_value: AtomicCell::new(vm.ctx.none()),
})
}
} else {
Err(vm.new_type_error("class must define a '_type_' string attribute".to_string()))
}
} else {
Err(vm.new_attribute_error("class must define a '_type_' attribute".to_string()))
}
}

#[pyfunction(name = "sizeof")]
pub fn size_of(tp: Either<PyTypeRef, PyObjectRef>, vm: &VirtualMachine) -> PyResult<usize> {
match tp {
Either::A(type_) if type_.fast_issubclass(PyCSimple::static_type()) => {
let zelf = new_simple_type(Either::B(&type_), vm)?;
Ok(get_size(zelf._type_.as_str()))
}
Either::B(obj) if obj.has_attr("size_of_instances", vm)? => {
let size_of_method = obj.get_attr("size_of_instances", vm)?;
let size_of_return = size_of_method.call(vec![], vm)?;
Ok(usize::try_from_object(vm, size_of_return)?)
}
_ => Err(vm.new_type_error("this type has no size".to_string())),
}
}

#[pyfunction]
fn get_errno() -> i32 {
errno::errno().0
}

#[pyfunction]
fn set_errno(value: i32) {
errno::set_errno(errno::Errno(value));
}
}
161 changes: 161 additions & 0 deletions vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::builtins::{PyBytes, PyFloat, PyInt, PyModule, PyNone, PyStr};
use crate::class::PyClassImpl;
use crate::{Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use std::fmt::Debug;

#[allow(dead_code)]
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
match _type_ {
"c" => {
if value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyInt>(vm)
.map_or(Ok(false), |v| {
let n = v.as_bigint().to_i64();
if let Some(n) = n {
Ok((0..=255).contains(&n))
} else {
Ok(false)
}
})?
{
Ok(value.clone())
} else {
Err(vm.new_type_error(
"one character bytes, bytearray or integer expected".to_string(),
))
}
}
"u" => {
if let Ok(b) = value.str(vm).map(|v| v.to_string().chars().count() == 1) {
if b {
Ok(value.clone())
} else {
Err(vm.new_type_error("one character unicode string expected".to_string()))
}
} else {
Err(vm.new_type_error(format!(
"unicode string expected instead of {} instance",
value.class().name()
)))
}
}
"b" | "h" | "H" | "i" | "I" | "l" | "q" | "L" | "Q" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"an integer is required (got type {})",
value.class().name()
)))
}
}
"f" | "d" | "g" => {
if value.clone().downcast_exact::<PyFloat>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!("must be real number, not {}", value.class().name())))
}
}
"?" => Ok(PyObjectRef::from(
vm.ctx.new_bool(value.clone().try_to_bool(vm)?),
)),
"B" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(vm.new_pyobj(u8::try_from_object(vm, value.clone())?))
} else {
Err(vm.new_type_error(format!("int expected instead of {}", value.class().name())))
}
}
"z" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyBytes>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"bytes or integer address expected instead of {} instance",
value.class().name()
)))
}
}
"Z" => {
if value.clone().downcast_exact::<PyStr>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"unicode string or integer address expected instead of {} instance",
value.class().name()
)))
}
}
_ => {
// "P"
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyNone>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error("cannot be converted to pointer".to_string()))
}
}
}
}

pub struct RawBuffer {
#[allow(dead_code)]
pub inner: Box<[u8]>,
#[allow(dead_code)]
pub size: usize,
}

#[pyclass(name = "_CData", module = "_ctypes")]
pub struct PyCData {
_objects: AtomicCell<Vec<PyObjectRef>>,
_buffer: PyRwLock<RawBuffer>,
}

#[pyclass]
impl PyCData {}

#[pyclass(
name = "_SimpleCData",
base = "PyCData",
module = "_ctypes"
// TODO: metaclass
)]
#[derive(PyPayload)]
pub struct PyCSimple {
pub _type_: String,
pub _value: AtomicCell<PyObjectRef>,
}

impl Debug for PyCSimple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyCSimple")
.field("_type_", &self._type_)
.finish()
}
}

#[pyclass(flags(BASETYPE))]
impl PyCSimple {}

pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
let ctx = &vm.ctx;
extend_module!(vm, module, {
"_CData" => PyCData::make_class(ctx),
"_SimpleCData" => PyCSimple::make_class(ctx),
})
}
6 changes: 6 additions & 0 deletions vm/src/stdlib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub mod posix;
#[path = "posix_compat.rs"]
pub mod posix;

#[cfg(any(target_family = "unix", target_family = "windows"))]
mod ctypes;
#[cfg(windows)]
pub(crate) mod msvcrt;
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
Expand Down Expand Up @@ -124,5 +126,9 @@ pub fn get_module_inits() -> StdlibMap {
"_winapi" => winapi::make_module,
"winreg" => winreg::make_module,
}
#[cfg(any(target_family = "unix", target_family = "windows"))]
{
"_ctypes" => ctypes::make_module,
}
}
}