Skip to content

Commit 6d0fde8

Browse files
Add call functions to c-api (#7937)
* Add call functions to c-api * Review * Move conversion impl to c-api
1 parent 63bfc72 commit 6d0fde8

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

‎.cspell.dict/cpython.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ mult
133133
multibytecodec
134134
nameobj
135135
nameop
136+
nargsf
136137
ncells
137138
nconsts
138139
newargs

‎crates/capi/src/abstract_.rs‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,118 @@
11
use crate::{PyObject, pystate::with_vm};
2+
use alloc::slice;
23
use core::ffi::c_int;
4+
use rustpython_vm::builtins::{PyDict, PyStr, PyTuple};
5+
use rustpython_vm::function::{FuncArgs, KwArgs, PosArgs};
6+
use rustpython_vm::{AsObject, Py, PyObjectRef, PyResult, VirtualMachine};
7+
8+
const PY_VECTORCALL_ARGUMENTS_OFFSET: usize = 1usize << (usize::BITS as usize - 1);
9+
10+
fn tuple_to_args(tuple: &Py<PyTuple>) -> PosArgs {
11+
tuple.iter().cloned().collect::<Vec<_>>().into()
12+
}
13+
14+
fn dict_to_kwargs(vm: &VirtualMachine, dict: &Py<PyDict>) -> PyResult<KwArgs> {
15+
dict.items_vec()
16+
.into_iter()
17+
.map(|(key, value)| {
18+
let key = key
19+
.downcast_ref::<PyStr>()
20+
.map(|s| s.to_string())
21+
.ok_or_else(|| vm.new_type_error("keywords must be strings"))?;
22+
Ok((key, value))
23+
})
24+
.collect::<PyResult<_>>()
25+
.map(KwArgs::new)
26+
}
27+
28+
#[unsafe(no_mangle)]
29+
pub unsafe extern "C" fn PyObject_Call(
30+
callable: *mut PyObject,
31+
args: *mut PyObject,
32+
kwargs: *mut PyObject,
33+
) -> *mut PyObject {
34+
with_vm(|vm| {
35+
let callable = unsafe { &*callable };
36+
let args = tuple_to_args(unsafe { &*args }.try_downcast_ref::<PyTuple>(vm)?);
37+
38+
let kwargs: Option<KwArgs> = unsafe { kwargs.as_ref() }
39+
.map(|kwargs| dict_to_kwargs(vm, kwargs.try_downcast_ref::<PyDict>(vm)?))
40+
.transpose()?;
41+
42+
callable.call_with_args(FuncArgs::new(args, kwargs.unwrap_or_default()), vm)
43+
})
44+
}
45+
46+
#[unsafe(no_mangle)]
47+
pub unsafe extern "C" fn PyObject_CallNoArgs(callable: *mut PyObject) -> *mut PyObject {
48+
with_vm(|vm| unsafe { &*callable }.call((), vm))
49+
}
50+
51+
#[unsafe(no_mangle)]
52+
pub unsafe extern "C" fn PyObject_Vectorcall(
53+
callable: *mut PyObject,
54+
args: *const *mut PyObject,
55+
nargsf: usize,
56+
kwnames: *mut PyObject,
57+
) -> *mut PyObject {
58+
with_vm(|vm| {
59+
let num_positional_args = nargsf & !PY_VECTORCALL_ARGUMENTS_OFFSET;
60+
61+
let kwnames: Option<&[PyObjectRef]> = unsafe {
62+
kwnames
63+
.as_ref()
64+
.map(|tuple| Ok(&***tuple.try_downcast_ref::<PyTuple>(vm)?))
65+
.transpose()?
66+
};
67+
68+
let args_len = num_positional_args + kwnames.map_or(0, <[PyObjectRef]>::len);
69+
let args = if args_len == 0 {
70+
Vec::new()
71+
} else {
72+
unsafe { slice::from_raw_parts(args, args_len) }
73+
.iter()
74+
.map(|arg| unsafe { &**arg }.to_owned())
75+
.collect::<Vec<_>>()
76+
};
77+
78+
let callable = unsafe { &*callable };
79+
callable.vectorcall(args, num_positional_args, kwnames, vm)
80+
})
81+
}
82+
83+
#[unsafe(no_mangle)]
84+
pub unsafe extern "C" fn PyObject_VectorcallMethod(
85+
name: *mut PyObject,
86+
args: *const *mut PyObject,
87+
nargsf: usize,
88+
kwnames: *mut PyObject,
89+
) -> *mut PyObject {
90+
with_vm(|vm| {
91+
let args_len = nargsf & !PY_VECTORCALL_ARGUMENTS_OFFSET;
92+
93+
if args_len == 0 {
94+
return Err(
95+
vm.new_system_error("PyObject_VectorcallMethod called with no receiver".to_owned())
96+
);
97+
}
98+
99+
let (receiver, args) = unsafe { slice::from_raw_parts(args, args_len) }
100+
.split_first()
101+
.expect("args_len > 0 should guarantee a receiver");
102+
103+
let method_name = unsafe { (&*name).try_downcast_ref::<PyStr>(vm)? };
104+
let callable = unsafe { (&**receiver).get_attr(method_name, vm)? };
105+
106+
Ok(unsafe {
107+
PyObject_Vectorcall(
108+
callable.as_object().as_raw().cast_mut(),
109+
args.as_ptr(),
110+
nargsf - 1,
111+
kwnames,
112+
)
113+
})
114+
})
115+
}
3116

4117
#[unsafe(no_mangle)]
5118
pub unsafe extern "C" fn PyObject_GetItem(obj: *mut PyObject, key: *mut PyObject) -> *mut PyObject {

0 commit comments

Comments
 (0)