diff --git a/crates/vm/src/builtins/builtin_func.rs b/crates/vm/src/builtins/builtin_func.rs index 422f922df94..7ca2cc09e3e 100644 --- a/crates/vm/src/builtins/builtin_func.rs +++ b/crates/vm/src/builtins/builtin_func.rs @@ -10,6 +10,7 @@ use crate::{ use alloc::fmt; // PyCFunctionObject in CPython +#[repr(C)] #[pyclass(name = "builtin_function_or_method", module = false)] pub struct PyNativeFunction { pub(crate) value: &'static PyMethodDef, @@ -146,7 +147,9 @@ impl Representable for PyNativeFunction { } // `PyCMethodObject` in CPython -#[pyclass(name = "builtin_method", module = false, base = PyNativeFunction, ctx = "builtin_method_type")] +// repr(C) ensures `func` is at offset 0, allowing safe cast from PyNativeMethod to PyNativeFunction +#[repr(C)] +#[pyclass(name = "builtin_function_or_method", module = false, base = PyNativeFunction, ctx = "builtin_function_or_method_type")] pub struct PyNativeMethod { pub(crate) func: PyNativeFunction, pub(crate) class: &'static Py, // TODO: the actual life is &'self @@ -157,34 +160,8 @@ pub struct PyNativeMethod { flags(HAS_DICT, DISALLOW_INSTANTIATION) )] impl PyNativeMethod { - #[pygetset] - fn __qualname__(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let prefix = zelf.class.name().to_string(); - Ok(vm - .ctx - .new_str(format!("{}.{}", prefix, &zelf.func.value.name))) - } - - #[pymethod] - fn __reduce__( - &self, - vm: &VirtualMachine, - ) -> PyResult<(PyObjectRef, (PyObjectRef, &'static str))> { - // TODO: return (getattr, (self.object, self.name)) if this is a method - let getattr = vm.builtins.get_attr("getattr", vm)?; - let target = self - .func - .zelf - .clone() - .unwrap_or_else(|| self.class.to_owned().into()); - let name = self.func.value.name; - Ok((getattr, (target, name))) - } - - #[pygetset] - fn __self__(zelf: PyRef, _vm: &VirtualMachine) -> Option { - zelf.func.zelf.clone() - } + // __qualname__, __self__, and __reduce__ are inherited from PyNativeFunction + // via NativeFunctionOrMethod wrapper since we share the same Python type. } impl fmt::Debug for PyNativeMethod { @@ -246,15 +223,20 @@ impl Representable for PyNativeMethod { pub fn init(context: &Context) { PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type); - PyNativeMethod::extend_class(context, context.types.builtin_method_type); + PyNativeMethod::extend_class(context, context.types.builtin_function_or_method_type); } +/// Wrapper that provides access to the common PyNativeFunction data +/// for both PyNativeFunction and PyNativeMethod (which has func as its first field). struct NativeFunctionOrMethod(PyRef); impl TryFromObject for NativeFunctionOrMethod { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { let class = vm.ctx.types.builtin_function_or_method_type; if obj.fast_isinstance(class) { + // Both PyNativeFunction and PyNativeMethod share the same type now. + // PyNativeMethod has `func: PyNativeFunction` as its first field, + // so we can safely treat the data pointer as PyNativeFunction for reading. Ok(Self(unsafe { obj.downcast_unchecked() })) } else { Err(vm.new_downcast_type_error(class, &obj)) diff --git a/crates/vm/src/builtins/capsule.rs b/crates/vm/src/builtins/capsule.rs new file mode 100644 index 00000000000..7e263c4cde2 --- /dev/null +++ b/crates/vm/src/builtins/capsule.rs @@ -0,0 +1,33 @@ +use super::PyType; +use crate::{Context, Py, PyPayload, PyResult, class::PyClassImpl, types::Representable}; + +/// PyCapsule - a container for C pointers. +/// In RustPython, this is a minimal implementation for compatibility. +#[pyclass(module = false, name = "PyCapsule")] +#[derive(Debug, Clone, Copy)] +pub struct PyCapsule { + // Capsules store opaque pointers; we don't expose the actual pointer functionality + // since RustPython doesn't have the same C extension model as CPython. + _private: (), +} + +impl PyPayload for PyCapsule { + #[inline] + fn class(ctx: &Context) -> &'static Py { + ctx.types.capsule_type + } +} + +#[pyclass(with(Representable), flags(DISALLOW_INSTANTIATION))] +impl PyCapsule {} + +impl Representable for PyCapsule { + #[inline] + fn repr_str(_zelf: &Py, _vm: &crate::VirtualMachine) -> PyResult { + Ok("".to_string()) + } +} + +pub fn init(context: &Context) { + PyCapsule::extend_class(context, context.types.capsule_type); +} diff --git a/crates/vm/src/builtins/mod.rs b/crates/vm/src/builtins/mod.rs index 994a4f9c758..fa7ab1b854e 100644 --- a/crates/vm/src/builtins/mod.rs +++ b/crates/vm/src/builtins/mod.rs @@ -9,6 +9,8 @@ pub(crate) mod bytearray; pub use bytearray::PyByteArray; pub(crate) mod bytes; pub use bytes::{PyBytes, PyBytesRef}; +pub(crate) mod capsule; +pub use capsule::PyCapsule; pub(crate) mod classmethod; pub use classmethod::PyClassMethod; pub(crate) mod code; diff --git a/crates/vm/src/function/method.rs b/crates/vm/src/function/method.rs index 6440fd801fc..29b5f4c230a 100644 --- a/crates/vm/src/function/method.rs +++ b/crates/vm/src/function/method.rs @@ -188,7 +188,7 @@ impl PyMethodDef { ) -> PyRef { PyRef::new_ref( self.to_bound_method(obj, class), - ctx.types.builtin_method_type.to_owned(), + ctx.types.builtin_function_or_method_type.to_owned(), None, ) } diff --git a/crates/vm/src/stdlib/_types.rs b/crates/vm/src/stdlib/_types.rs new file mode 100644 index 00000000000..385ecf8cf5a --- /dev/null +++ b/crates/vm/src/stdlib/_types.rs @@ -0,0 +1,157 @@ +//! Implementation of the `_types` module. +//! +//! This module exposes built-in types that are used by the `types` module. + +pub(crate) use _types::module_def; + +#[pymodule] +#[allow(non_snake_case)] +mod _types { + use crate::{PyObjectRef, VirtualMachine}; + + #[pyattr] + fn AsyncGeneratorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.async_generator.to_owned().into() + } + + #[pyattr] + fn BuiltinFunctionType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx + .types + .builtin_function_or_method_type + .to_owned() + .into() + } + + #[pyattr] + fn BuiltinMethodType(vm: &VirtualMachine) -> PyObjectRef { + // Same as BuiltinFunctionType in CPython + vm.ctx + .types + .builtin_function_or_method_type + .to_owned() + .into() + } + + #[pyattr] + fn CapsuleType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.capsule_type.to_owned().into() + } + + #[pyattr] + fn CellType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.cell_type.to_owned().into() + } + + #[pyattr] + fn CodeType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.code_type.to_owned().into() + } + + #[pyattr] + fn CoroutineType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.coroutine_type.to_owned().into() + } + + #[pyattr] + fn EllipsisType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.ellipsis_type.to_owned().into() + } + + #[pyattr] + fn FrameType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.frame_type.to_owned().into() + } + + #[pyattr] + fn FunctionType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.function_type.to_owned().into() + } + + #[pyattr] + fn GeneratorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.generator_type.to_owned().into() + } + + #[pyattr] + fn GenericAlias(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.generic_alias_type.to_owned().into() + } + + #[pyattr] + fn GetSetDescriptorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.getset_type.to_owned().into() + } + + #[pyattr] + fn LambdaType(vm: &VirtualMachine) -> PyObjectRef { + // Same as FunctionType in CPython + vm.ctx.types.function_type.to_owned().into() + } + + #[pyattr] + fn MappingProxyType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.mappingproxy_type.to_owned().into() + } + + #[pyattr] + fn MemberDescriptorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.member_descriptor_type.to_owned().into() + } + + #[pyattr] + fn MethodDescriptorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.method_descriptor_type.to_owned().into() + } + + #[pyattr] + fn ClassMethodDescriptorType(vm: &VirtualMachine) -> PyObjectRef { + // TODO: implement as separate type + vm.ctx.types.method_descriptor_type.to_owned().into() + } + + #[pyattr] + fn MethodType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.bound_method_type.to_owned().into() + } + + #[pyattr] + fn MethodWrapperType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.method_wrapper_type.to_owned().into() + } + + #[pyattr] + fn ModuleType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.module_type.to_owned().into() + } + + #[pyattr] + fn NoneType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.none_type.to_owned().into() + } + + #[pyattr] + fn NotImplementedType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.not_implemented_type.to_owned().into() + } + + #[pyattr] + fn SimpleNamespace(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.namespace_type.to_owned().into() + } + + #[pyattr] + fn TracebackType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.traceback_type.to_owned().into() + } + + #[pyattr] + fn UnionType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.union_type.to_owned().into() + } + + #[pyattr] + fn WrapperDescriptorType(vm: &VirtualMachine) -> PyObjectRef { + vm.ctx.types.wrapper_descriptor_type.to_owned().into() + } +} diff --git a/crates/vm/src/stdlib/mod.rs b/crates/vm/src/stdlib/mod.rs index 088abc5b4a6..eacc0f39978 100644 --- a/crates/vm/src/stdlib/mod.rs +++ b/crates/vm/src/stdlib/mod.rs @@ -1,4 +1,5 @@ mod _abc; +mod _types; #[cfg(feature = "ast")] pub(crate) mod ast; pub mod atexit; @@ -72,6 +73,7 @@ use crate::{Context, builtins::PyModuleDef}; pub fn builtin_module_defs(ctx: &Context) -> Vec<&'static PyModuleDef> { vec![ _abc::module_def(ctx), + _types::module_def(ctx), #[cfg(feature = "ast")] ast::module_def(ctx), atexit::module_def(ctx), diff --git a/crates/vm/src/types/zoo.rs b/crates/vm/src/types/zoo.rs index 9fa4e777a1a..2b60e37f316 100644 --- a/crates/vm/src/types/zoo.rs +++ b/crates/vm/src/types/zoo.rs @@ -1,7 +1,7 @@ use crate::{ Py, builtins::{ - asyncgenerator, bool_, builtin_func, bytearray, bytes, classmethod, code, complex, + asyncgenerator, bool_, builtin_func, bytearray, bytes, capsule, classmethod, code, complex, coroutine, descriptor, dict, enumerate, filter, float, frame, function, generator, genericalias, getset, int, interpolation, iter, list, map, mappingproxy, memory, module, namespace, object, property, pystr, range, set, singletons, slice, staticmethod, super_, @@ -28,6 +28,7 @@ pub struct TypeZoo { pub bytearray_iterator_type: &'static Py, pub bool_type: &'static Py, pub callable_iterator: &'static Py, + pub capsule_type: &'static Py, pub cell_type: &'static Py, pub classmethod_type: &'static Py, pub code_type: &'static Py, @@ -108,12 +109,20 @@ impl TypeZoo { #[cold] pub(crate) fn init() -> Self { let (type_type, object_type, weakref_type) = crate::object::init_type_hierarchy(); + // the order matters for type, object, weakref, and int - must be initialized first + let type_type = type_::PyType::init_manually(type_type); + let object_type = object::PyBaseObject::init_manually(object_type); + let weakref_type = weakref::PyWeak::init_manually(weakref_type); + let int_type = int::PyInt::init_builtin_type(); + + // builtin_function_or_method and builtin_method share the same type (CPython behavior) + let builtin_function_or_method_type = builtin_func::PyNativeFunction::init_builtin_type(); + Self { - // the order matters for type, object, weakref, and int - type_type: type_::PyType::init_manually(type_type), - object_type: object::PyBaseObject::init_manually(object_type), - weakref_type: weakref::PyWeak::init_manually(weakref_type), - int_type: int::PyInt::init_builtin_type(), + type_type, + object_type, + weakref_type, + int_type, // types exposed as builtins bool_type: bool_::PyBool::init_builtin_type(), @@ -147,11 +156,12 @@ impl TypeZoo { asyncgenerator::PyAsyncGenWrappedValue::init_builtin_type(), anext_awaitable: asyncgenerator::PyAnextAwaitable::init_builtin_type(), bound_method_type: function::PyBoundMethod::init_builtin_type(), - builtin_function_or_method_type: builtin_func::PyNativeFunction::init_builtin_type(), - builtin_method_type: builtin_func::PyNativeMethod::init_builtin_type(), + builtin_function_or_method_type, + builtin_method_type: builtin_function_or_method_type, bytearray_iterator_type: bytearray::PyByteArrayIterator::init_builtin_type(), bytes_iterator_type: bytes::PyBytesIterator::init_builtin_type(), callable_iterator: iter::PyCallableIterator::init_builtin_type(), + capsule_type: capsule::PyCapsule::init_builtin_type(), cell_type: function::PyCell::init_builtin_type(), code_type: code::PyCode::init_builtin_type(), coroutine_type: coroutine::PyCoroutine::init_builtin_type(), @@ -225,6 +235,7 @@ impl TypeZoo { complex::init(context); bytes::init(context); bytearray::init(context); + capsule::init(context); property::init(context); getset::init(context); memory::init(context);