From 0dfff5fd21b437406c255d659b0b304cca47be64 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Wed, 8 Jul 2026 20:16:21 +0200 Subject: [PATCH 1/3] Add more eval functions to c-api --- crates/capi/src/ceval.rs | 114 ++++++++++++++++++++++++++++++++++++- crates/capi/src/pyframe.rs | 17 +++--- crates/capi/src/util.rs | 10 +++- 3 files changed, 128 insertions(+), 13 deletions(-) diff --git a/crates/capi/src/ceval.rs b/crates/capi/src/ceval.rs index d137cb17dab..22ef5a8eb8d 100644 --- a/crates/capi/src/ceval.rs +++ b/crates/capi/src/ceval.rs @@ -1,12 +1,13 @@ +use crate::pyframe::PyFrameObject; use crate::pystate::with_vm; use crate::unicodeobject::decode_fsdefault_and_size; use core::ffi::{CStr, c_char, c_int}; use core::ptr::NonNull; -use rustpython_vm::builtins::{PyCode, PyDict}; +use rustpython_vm::builtins::{PyCode, PyDict, PyStr}; use rustpython_vm::function::ArgMapping; use rustpython_vm::scope::Scope; -use rustpython_vm::version; use rustpython_vm::{AsObject, PyObject, TryFromObject}; +use rustpython_vm::{PyObjectRef, version}; #[unsafe(no_mangle)] pub unsafe extern "C" fn Py_CompileString( @@ -42,6 +43,16 @@ pub unsafe extern "C" fn PyEval_EvalCode( }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyEval_EvalFrame(f: *mut PyFrameObject) -> *mut PyObject { + unsafe { PyEval_EvalFrameEx(f, 0) } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyEval_EvalFrameEx(f: *mut PyFrameObject, _exc: c_int) -> *mut PyObject { + with_vm(|vm| vm.run_frame(unsafe { &*f }.to_owned())) +} + #[unsafe(no_mangle)] pub extern "C" fn PyEval_GetBuiltins() -> *mut PyObject { with_vm(|vm| { @@ -52,6 +63,105 @@ pub extern "C" fn PyEval_GetBuiltins() -> *mut PyObject { }) } +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetFrame() -> *mut PyObject { + with_vm(|vm| { + vm.current_frame() + .map(|frame| frame.as_object().as_raw()) + .unwrap_or_default() + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetFrameBuiltins() -> *mut PyObject { + with_vm(|vm| { + vm.current_frame().map_or_else( + || vm.builtins.as_object().to_owned(), + |frame| frame.builtins.as_object().to_owned(), + ) + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetFrameGlobals() -> *mut PyObject { + with_vm(|vm| { + vm.current_frame() + .map(|frame| frame.globals.as_object().to_owned().into_raw().as_ptr()) + .unwrap_or_default() + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetFrameLocals() -> *mut PyObject { + with_vm(|vm| { + let Some(frame) = vm.current_frame() else { + return Ok(core::ptr::null_mut()); + }; + let locals: PyObjectRef = frame.locals(vm)?.into(); + Ok(locals.into_raw().as_ptr()) + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetGlobals() -> *mut PyObject { + with_vm(|vm| { + vm.current_frame() + .map(|frame| frame.globals.as_object().as_raw()) + .unwrap_or_default() + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyEval_GetLocals() -> *mut PyObject { + with_vm(|vm| { + vm.current_frame() + .map(|frame| frame.locals.as_object(vm).as_raw()) + .unwrap_or_default() + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyEval_GetFuncName(func: *mut PyObject) -> *const c_char { + with_vm(|vm| { + let func = unsafe { &*func }; + let cls = func.class(); + + if cls.is(vm.ctx.types.bound_method_type) { + let function = func.get_attr("__func__", vm)?; + return Ok(unsafe { PyEval_GetFuncName(function.as_object().as_raw().cast_mut()) }); + } + + let name = if cls.is(vm.ctx.types.function_type) + || cls.is(vm.ctx.types.builtin_function_or_method_type) + { + func.get_attr(rustpython_vm::identifier!(vm, __name__), vm)? + .downcast_ref::() + .and_then(|s| s.to_str()) + .map_or_else(|| cls.name().as_ptr(), |s| s.as_ptr()) + } else { + cls.name().as_ptr() + }; + + Ok(name.cast()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyEval_GetFuncDesc(func: *mut PyObject) -> *const c_char { + with_vm(|vm| { + let func = unsafe { &*func }; + let cls = func.class(); + if cls.is(vm.ctx.types.bound_method_type) + || cls.is(vm.ctx.types.function_type) + || cls.is(vm.ctx.types.builtin_function_or_method_type) + { + c"()" + } else { + c" object" + } + }) +} + #[cfg(test)] mod tests { use pyo3::exceptions::PyException; diff --git a/crates/capi/src/pyframe.rs b/crates/capi/src/pyframe.rs index d8e9d124bb0..cfe8b7b6e27 100644 --- a/crates/capi/src/pyframe.rs +++ b/crates/capi/src/pyframe.rs @@ -1,19 +1,16 @@ use crate::PyObject; use crate::pystate::with_vm; +use rustpython_vm::Py; use rustpython_vm::frame::Frame; +pub type PyFrameObject = Py; + #[unsafe(no_mangle)] -pub unsafe extern "C" fn PyFrame_GetCode(frame: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let frame = unsafe { &*frame }.try_downcast_ref::(vm)?; - Ok(frame.f_code()) - }) +pub unsafe extern "C" fn PyFrame_GetCode(frame: *mut PyFrameObject) -> *mut PyObject { + with_vm(|_vm| Ok(unsafe { &*frame }.f_code())) } #[unsafe(no_mangle)] -pub unsafe extern "C" fn PyFrame_GetLineNumber(frame: *mut PyObject) -> core::ffi::c_int { - with_vm(|vm| { - let frame = unsafe { &*frame }.try_downcast_ref::(vm)?; - Ok(frame.f_lineno() as core::ffi::c_int) - }) +pub unsafe extern "C" fn PyFrame_GetLineNumber(frame: *mut PyFrameObject) -> core::ffi::c_int { + with_vm(|_vm| Ok(unsafe { &*frame }.f_lineno() as core::ffi::c_int)) } diff --git a/crates/capi/src/util.rs b/crates/capi/src/util.rs index ccb8ee254af..518113d0594 100644 --- a/crates/capi/src/util.rs +++ b/crates/capi/src/util.rs @@ -1,6 +1,6 @@ use crate::PyObject; use core::convert::Infallible; -use core::ffi::{c_char, c_double, c_int, c_long, c_ulong, c_void}; +use core::ffi::{CStr, c_char, c_double, c_int, c_long, c_ulong, c_void}; use rustpython_vm::{PyObjectRef, PyRef, PyResult, VirtualMachine}; pub(crate) trait FfiResult { @@ -92,6 +92,14 @@ impl FfiResult for *const c_char { } } +impl FfiResult<*const c_char> for &CStr { + const ERR_VALUE: *const c_char = core::ptr::null_mut(); + + fn into_output(self, _vm: &VirtualMachine) -> *const c_char { + self.as_ptr() + } +} + impl FfiResult for usize { const ERR_VALUE: isize = -1; From ddf148d12faf5f3673b56628b85a395bfd9a7cb9 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Wed, 8 Jul 2026 21:20:43 +0200 Subject: [PATCH 2/3] Review --- crates/capi/src/ceval.rs | 15 +++++++++------ crates/capi/src/pyframe.rs | 13 +++++++++---- crates/capi/src/util.rs | 13 ++++++++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/capi/src/ceval.rs b/crates/capi/src/ceval.rs index 22ef5a8eb8d..173e398c930 100644 --- a/crates/capi/src/ceval.rs +++ b/crates/capi/src/ceval.rs @@ -64,12 +64,13 @@ pub extern "C" fn PyEval_GetBuiltins() -> *mut PyObject { } #[unsafe(no_mangle)] -pub extern "C" fn PyEval_GetFrame() -> *mut PyObject { - with_vm(|vm| { +pub extern "C" fn PyEval_GetFrame() -> *mut PyFrameObject { + with_vm(|vm| -> *mut PyObject { vm.current_frame() - .map(|frame| frame.as_object().as_raw()) + .map(|frame| frame.as_object().as_raw().cast_mut()) .unwrap_or_default() }) + .cast() } #[unsafe(no_mangle)] @@ -114,9 +115,11 @@ pub extern "C" fn PyEval_GetGlobals() -> *mut PyObject { #[unsafe(no_mangle)] pub extern "C" fn PyEval_GetLocals() -> *mut PyObject { with_vm(|vm| { - vm.current_frame() - .map(|frame| frame.locals.as_object(vm).as_raw()) - .unwrap_or_default() + let Some(frame) = vm.current_frame() else { + return Ok(core::ptr::null_mut()); + }; + let _ = frame.locals(vm)?; + Ok(frame.locals.as_object(vm).as_raw().cast_mut()) }) } diff --git a/crates/capi/src/pyframe.rs b/crates/capi/src/pyframe.rs index cfe8b7b6e27..5c9ad371708 100644 --- a/crates/capi/src/pyframe.rs +++ b/crates/capi/src/pyframe.rs @@ -1,16 +1,21 @@ -use crate::PyObject; use crate::pystate::with_vm; +use core::ffi::c_int; use rustpython_vm::Py; +use rustpython_vm::builtins::PyCode; use rustpython_vm::frame::Frame; pub type PyFrameObject = Py; +pub type PyCodeObject = Py; #[unsafe(no_mangle)] -pub unsafe extern "C" fn PyFrame_GetCode(frame: *mut PyFrameObject) -> *mut PyObject { +pub unsafe extern "C" fn PyFrame_GetCode(frame: *mut PyFrameObject) -> *mut PyCodeObject { with_vm(|_vm| Ok(unsafe { &*frame }.f_code())) } #[unsafe(no_mangle)] -pub unsafe extern "C" fn PyFrame_GetLineNumber(frame: *mut PyFrameObject) -> core::ffi::c_int { - with_vm(|_vm| Ok(unsafe { &*frame }.f_lineno() as core::ffi::c_int)) +pub unsafe extern "C" fn PyFrame_GetLineNumber(frame: *mut PyFrameObject) -> c_int { + with_vm(|_vm| { + let lineno = unsafe { &*frame }.f_lineno(); + Ok(lineno.try_into().unwrap_or(c_int::MAX)) + }) } diff --git a/crates/capi/src/util.rs b/crates/capi/src/util.rs index 518113d0594..6bbda7654fe 100644 --- a/crates/capi/src/util.rs +++ b/crates/capi/src/util.rs @@ -1,7 +1,7 @@ use crate::PyObject; use core::convert::Infallible; use core::ffi::{CStr, c_char, c_double, c_int, c_long, c_ulong, c_void}; -use rustpython_vm::{PyObjectRef, PyRef, PyResult, VirtualMachine}; +use rustpython_vm::{Py, PyObjectRef, PyRef, PyResult, VirtualMachine}; pub(crate) trait FfiResult { const ERR_VALUE: Output; @@ -36,6 +36,17 @@ where } } +impl FfiResult<*mut Py> for PyRef +where + Self: Into, +{ + const ERR_VALUE: *mut Py = core::ptr::null_mut(); + + fn into_output(self, _vm: &VirtualMachine) -> *mut Py { + self.into().into_raw().as_ptr().cast() + } +} + impl FfiResult<*mut PyObject> for PyObjectRef { const ERR_VALUE: *mut PyObject = core::ptr::null_mut(); From a01eae79cedad0b76982454a894195bcd7404277 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Wed, 8 Jul 2026 21:25:59 +0200 Subject: [PATCH 3/3] Remove `PyEval_GetFuncName` --- crates/capi/src/ceval.rs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/crates/capi/src/ceval.rs b/crates/capi/src/ceval.rs index 173e398c930..366cb0071bb 100644 --- a/crates/capi/src/ceval.rs +++ b/crates/capi/src/ceval.rs @@ -3,7 +3,7 @@ use crate::pystate::with_vm; use crate::unicodeobject::decode_fsdefault_and_size; use core::ffi::{CStr, c_char, c_int}; use core::ptr::NonNull; -use rustpython_vm::builtins::{PyCode, PyDict, PyStr}; +use rustpython_vm::builtins::{PyCode, PyDict}; use rustpython_vm::function::ArgMapping; use rustpython_vm::scope::Scope; use rustpython_vm::{AsObject, PyObject, TryFromObject}; @@ -123,32 +123,6 @@ pub extern "C" fn PyEval_GetLocals() -> *mut PyObject { }) } -#[unsafe(no_mangle)] -pub unsafe extern "C" fn PyEval_GetFuncName(func: *mut PyObject) -> *const c_char { - with_vm(|vm| { - let func = unsafe { &*func }; - let cls = func.class(); - - if cls.is(vm.ctx.types.bound_method_type) { - let function = func.get_attr("__func__", vm)?; - return Ok(unsafe { PyEval_GetFuncName(function.as_object().as_raw().cast_mut()) }); - } - - let name = if cls.is(vm.ctx.types.function_type) - || cls.is(vm.ctx.types.builtin_function_or_method_type) - { - func.get_attr(rustpython_vm::identifier!(vm, __name__), vm)? - .downcast_ref::() - .and_then(|s| s.to_str()) - .map_or_else(|| cls.name().as_ptr(), |s| s.as_ptr()) - } else { - cls.name().as_ptr() - }; - - Ok(name.cast()) - }) -} - #[unsafe(no_mangle)] pub unsafe extern "C" fn PyEval_GetFuncDesc(func: *mut PyObject) -> *const c_char { with_vm(|vm| {