⚡ Sangsakala Proxy 🏠 Home
Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
9 changes: 3 additions & 6 deletions crates/capi/src/methodobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::util::{CStrExt, FfiPtrExt};
use core::ffi::{c_char, c_int};
use core::fmt::Debug;
use rustpython_vm::function::{FuncArgs, HeapMethodDef, ItemDoc, PosArgs, PyMethodFlags};
use rustpython_vm::types::c_slots::{kwargs_ptr, split_args};
use rustpython_vm::{AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine};

define_py_check!(fn PyCFunction_Check, types.builtin_function_or_method_type);
Expand Down Expand Up @@ -197,16 +198,12 @@ unsafe fn call_function_with_keywords(
.as_ref()
.map(|obj| obj.as_object().as_raw().cast_mut())
.unwrap_or_default();
let arg_tuple = vm.ctx.new_tuple(args.args);
let kwargs = vm.ctx.new_dict();
for (k, v) in args.kwargs {
kwargs.set_item(&*k, v, vm)?;
}
let (arg_tuple, kwargs) = split_args(vm, args)?;
unsafe {
f(
slf_ptr,
arg_tuple.as_object().as_raw().cast_mut(),
kwargs.as_object().as_raw().cast_mut(),
kwargs_ptr(kwargs.as_ref()),
)
.assume_owned_or_err(vm)
}
Expand Down
24 changes: 18 additions & 6 deletions crates/capi/src/object/pytype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use core::ptr;
use rustpython_vm::builtins::{PyStr, PyType};
use rustpython_vm::function::{ItemDoc, PyMethodFlags};
use rustpython_vm::types::{
AtomicPyTypeFlags, PyTypeFlags, PyTypeSlots,
AtomicPyTypeFlags, CNewFunc, CSlotId, CSlots, PyTypeFlags, PyTypeSlots,
SlotAccessor::{TpBase, TpDealloc, TpDoc, TpGetset, TpMembers, TpMethods, TpNew, TpTraverse},
};
use rustpython_vm::{AsObject, Py, PyObject};
Expand Down Expand Up @@ -97,8 +97,15 @@ pub unsafe extern "C" fn PyType_GetFullyQualifiedName(ptr: *mut PyTypeObject) ->
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyType_GetSlot(_ty: *const PyTypeObject, _slot: c_int) -> *mut c_void {
ptr::null_mut()
pub unsafe extern "C" fn PyType_GetSlot(ty: *const PyTypeObject, slot: c_int) -> *mut c_void {
let ty = unsafe { &*ty };
match CSlotId::from_raw(slot) {
Some(CSlotId::TpNew) => ty.c_tp_new().map_or(ptr::null_mut(), |f| f as *mut c_void),
None => {
eprintln!("PyType_GetSlot: unknown slot {slot}");
ptr::null_mut()
}
}
}

#[unsafe(no_mangle)]
Expand All @@ -111,6 +118,7 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
let attrs = Default::default();
let mut getsets = Vec::new();
let mut members = Vec::new();
let c_slots = CSlots::new();
let mut metaclass = vm.ctx.types.type_type.to_owned();

for slot in PySlot::iter(slots) {
Expand Down Expand Up @@ -166,7 +174,12 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
methods.push((name, method));
}
}
PySlotType::TypeSlot(TpNew | TpDealloc | TpTraverse | _, _) => {
PySlotType::TypeSlot(TpNew, pfunc) => unsafe {
c_slots
.new
.store(Some(core::mem::transmute::<*mut c_void, CNewFunc>(pfunc)));
Comment thread
bschoenmaeckers marked this conversation as resolved.
Resolved
},
Comment thread
bschoenmaeckers marked this conversation as resolved.
Resolved
PySlotType::TypeSlot(TpDealloc | TpTraverse | _, _) => {
// TODO
}
}
Expand Down Expand Up @@ -194,6 +207,7 @@ pub unsafe extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject
|msg| vm.new_system_error(format!("Failed to create type from slots: {msg}")),
)?;

class.set_c_slots(c_slots, vm)?;
Comment thread
bschoenmaeckers marked this conversation as resolved.
Resolved
let attrs = &class.attributes;
let class_static = unsafe { &*((&*class) as *const _) };
for (name, method) in methods {
Expand Down Expand Up @@ -425,7 +439,6 @@ mod tests {
}

#[test]
#[ignore]
fn zero_sized_class() {
#[pyclass(frozen)]
struct MyEmptyClass {}
Expand Down Expand Up @@ -484,7 +497,6 @@ mod tests {
}

#[test]
#[ignore]
fn subclass_with_property() {
#[pyclass(frozen, extends=PyList)]
struct MyList {}
Expand Down
23 changes: 23 additions & 0 deletions crates/derive-impl/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,28 @@ pub(crate) fn impl_pyclass_impl(attr: PunctuatedNestedMeta, item: Item) -> Resul
sig_constructor,
sig_structseq,
);
// The table names `Self`, so it has to be an associated const on the
// payload, not a `static` item inside `extend_slots`.
let static_c_slots_impl = if sig_constructor {
Comment thread
bschoenmaeckers marked this conversation as resolved.
Resolved
quote! {
impl ::rustpython_vm::types::HasStaticCSlots for #payload_ty {
const TABLE: ::rustpython_vm::types::StaticCSlots =
::rustpython_vm::types::StaticCSlots {
new: Some(::rustpython_vm::types::CSlotPair {
rust: <#payload_ty as ::rustpython_vm::types::Constructor>::slot_new as _,
c: ::rustpython_vm::types::c_new_for::<
::rustpython_vm::types::ViaConstructor<#payload_ty>,
> as _,
}),
};
}
}
} else {
quote! {}
};
quote! {
#imp
#static_c_slots_impl
impl ::rustpython_vm::class::PyClassImpl for #payload_ty {
const TP_FLAGS: ::rustpython_vm::types::PyTypeFlags = #flags;

Expand Down Expand Up @@ -2567,6 +2587,9 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
} else if path.is_ident("Constructor") {
quote_spanned! { item_span =>
slots.new.store(Some(<Self as ::rustpython_vm::types::Constructor>::slot_new as _));
slots.static_c_slots.store(Some(
&<Self as ::rustpython_vm::types::HasStaticCSlots>::TABLE,
));
}
} else {
quote_spanned! { item_span =>
Expand Down
80 changes: 64 additions & 16 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ use crate::{
object::{Traverse, TraverseFn},
protocol::{PyIterReturn, PyNumberMethods},
types::{
AsNumber, Callable, Constructor, GetAttr, Initializer, NewFunc, PyTypeFlags, PyTypeSlots,
Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
VectorCallFunc, fn_addr, new_wrapper,
AsNumber, CSlots, Callable, Constructor, GetAttr, Initializer, OwnedCSlots, PyTypeFlags,
PyTypeSlots, Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
VectorCallFunc,
},
};
use core::{
Expand Down Expand Up @@ -352,6 +352,9 @@ pub struct HeapTypeExt {
pub qualname: PyRwLock<PyStrRef>,
pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
pub type_data: PyRwLock<Option<TypeDataSlot>>,
/// The C slot table this type owns, once an extension has filled one in.
/// Types that inherit from it point at this same table.
pub c_slots: PyRwLock<Option<OwnedCSlots>>,
pub specialization_cache: TypeSpecializationCache,
/// The interpreter this type was created in, or `None` for the types the
/// shared context builds before any interpreter exists.
Expand Down Expand Up @@ -852,6 +855,7 @@ impl PyType {
qualname: PyRwLock::new(name),
slots: None,
type_data: PyRwLock::new(None),
c_slots: PyRwLock::new(None),
specialization_cache: TypeSpecializationCache::new(),
interpreter_id: HeapTypeExt::creating_interpreter_id(),
};
Expand Down Expand Up @@ -1313,6 +1317,7 @@ impl PyType {

if slots.flags.has_feature(PyTypeFlags::DISALLOW_INSTANTIATION) {
slots.new.store(None);
slots.c_slots.store(None);
} else if slots.new.load().is_none() {
let inherited = base.and_then(|base| base.slots.new.load());
if !is_heap
Expand All @@ -1327,6 +1332,9 @@ impl PyType {
);
}
slots.new.store(inherited);
slots
.c_slots
.store(base.and_then(|base| base.slots.c_slots.load()));
}
}

Expand Down Expand Up @@ -1646,8 +1654,39 @@ impl PyType {
attributes
}

/// Give this type a C tp_new, taking ownership of the table it is read
/// from and installing the trampoline that reaches it.
///
/// Errors if `self` is not a heap type, which is the only kind that can own
/// a table, or if it already owns one.
pub fn set_c_slots(&self, c_slots: CSlots, vm: &VirtualMachine) -> PyResult<()> {
let ext = self.heaptype_ext.as_ref().ok_or_else(|| {
vm.new_type_error(format!(
"cannot set C slots on static type '{}'",
self.name()
))
})?;
let previous_new = self.slots.new.load();
let mut owned = ext.c_slots.write();
if owned.is_some() {
return Err(vm.new_type_error(format!("type '{}' already has C slots", self.name())));
}
let has_new = c_slots.new.load().is_some();
let table = OwnedCSlots::new(c_slots);
self.slots.c_slots.store(Some(table.as_ptr()));
if has_new {
self.slots.new.store(Some(crate::types::c_new_trampoline));
} else if previous_new.is_some_and(crate::types::c_slots::is_c_trampoline) {
// Installing a table that omits tp_new must not clear an existing
// C tp_new trampoline this type already uses.
self.slots.new.store(previous_new);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
*owned = Some(table);
Ok(())
}

// bound method for every type
pub(crate) fn __new__(zelf: PyRef<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
pub fn __new__(zelf: PyRef<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let (subtype, args): (PyRef<Self>, FuncArgs) = args.bind(vm)?;
if !subtype.fast_issubclass(&zelf) {
return Err(vm.new_type_error(format!(
Expand Down Expand Up @@ -1967,11 +2006,11 @@ impl Constructor for PyType {
// Search the bases for the proper metatype to deal with this:
let winner = calculate_meta_class(metatype.clone(), bases.as_slice(), vm)?;
let metatype = if !winner.is(&metatype) {
let slot_new = winner.slots.new.load().ok_or_else(|| {
vm.new_type_error(format!("cannot create '{}' instances", winner.slot_name()))
})?;
// Pass it to the winner.
return slot_new(winner, args, vm);
if let Some(ref slot_new) = winner.slots.new.load() {
// Pass it to the winner
return slot_new(winner, args, vm);
}
winner
} else {
metatype
};
Expand Down Expand Up @@ -2243,6 +2282,7 @@ impl Constructor for PyType {
qualname: PyRwLock::new(qualname),
slots: heaptype_slots.clone(),
type_data: PyRwLock::new(None),
c_slots: PyRwLock::new(None),
specialization_cache: TypeSpecializationCache::new(),
interpreter_id: HeapTypeExt::creating_interpreter_id(),
};
Expand Down Expand Up @@ -3600,25 +3640,33 @@ pub(crate) fn call_slot_new(
return Err(vm.new_type_error(format!("cannot create '{}' instances", subtype.slot_name())));
}

// "is not safe" check (tp_new_wrapper). Walk past bases whose tp_new is
// new_wrapper so a Python subclass of a native heap type still reaches
// that type's tp_new; reject object.__new__(dict) and similar.
// "is not safe" check (tp_new_wrapper logic)
// Check that the user doesn't do something silly and unsafe like
// object.__new__(dict). To do this, we check that the most derived base
// that owns a real tp_new is this type. Walking past the types whose slot
// is only the dynamic wrapper, rather than past every heap type, keeps a
// heap type that carries its own tp_new, as one built from a C spec does,
// from being skipped.
let mut staticbase = subtype.clone();
while staticbase
.slots
.new
.load()
.is_some_and(|f| fn_addr(f) == fn_addr(new_wrapper as NewFunc))
.map(|f| crate::types::fn_addr(f))
== Some(crate::types::fn_addr(
crate::types::new_wrapper as crate::types::NewFunc,
))
{
match staticbase.base.load_owned() {
Some(base) => staticbase = base,
None => break,
}
}

let typ_new = typ.slots.new.load();
let staticbase_new = staticbase.slots.new.load();
if typ_new.map(fn_addr) != staticbase_new.map(fn_addr) {
// Check if staticbase's tp_new differs from typ's tp_new. Compare what the
// slot dispatches to, so that two types reached through the same C
// trampoline are still told apart by their C functions.
if typ.slots.new_identity() != staticbase.slots.new_identity() {
return Err(vm.new_type_error(format!(
"{}.__new__({}) is not safe, use {}.__new__()",
typ.slot_name(),
Expand Down
1 change: 0 additions & 1 deletion crates/vm/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
//! - Rust `Debug` formatting now consistently uses the allocation's vtable rather
//! than the statically selected payload's formatter. This is a Rust diagnostic
//! difference, not a Python semantic assertion.

mod core;
mod ext;
mod payload;
Expand Down
Loading
Loading