From 022f22eb45e0101f85cea504b0c8f8edc04cf76d Mon Sep 17 00:00:00 2001 From: Bobronium Date: Fri, 20 Mar 2026 04:52:11 +0400 Subject: [PATCH 01/10] Implement some CPython API on types.rs, make memo accept generic --- src/copy.rs | 28 +++---- src/critical_section.rs | 6 +- src/deepcopy.rs | 58 +++++++-------- src/dict_iter.rs | 12 +-- src/memo/any.rs | 27 ++++--- src/memo/dict.rs | 23 +++--- src/memo/mod.rs | 17 +++-- src/memo/native.rs | 37 ++++++---- src/memo/table.rs | 6 +- src/reduce.rs | 47 ++++++------ src/types.rs | 157 ++++++++++++++++++++++++++++++++++++---- 11 files changed, 279 insertions(+), 139 deletions(-) diff --git a/src/copy.rs b/src/copy.rs index 4b43c10..6ebf366 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -57,20 +57,20 @@ impl PyCopy for *mut PyListObject { copied.set_slot_steal_unchecked(index, item); } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } impl PyCopy for *mut PyDictObject { unsafe fn copy(self) -> PyResult { - unsafe { PyResult::ok(check!(PyDict_Copy(self as *mut PyObject))) } + unsafe { PyResult::ok(check!(self.dict_copy())) } } } impl PyCopy for *mut PySetObject { unsafe fn copy(self) -> PyResult { - unsafe { PyResult::ok(check!(PySet_New(self as *mut PyObject))) } + unsafe { PyResult::ok(check!(py_set_from(self))) } } } @@ -82,7 +82,7 @@ impl PyCopy for *mut PyByteArrayObject { if size > 0 { ptr::copy_nonoverlapping(self.as_ptr(), copied.as_ptr(), size as usize); } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -154,7 +154,7 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - return -1; } - let result = PyDict_Merge(instance_dict, dict_state, 1); + let result = (instance_dict as *mut PyDictObject).merge(dict_state, 1); instance_dict.decref(); if result < 0 { let message = ffi_ext::PyUnicode_FromFormat( @@ -181,8 +181,9 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while PyDict_Next(dict_state, &mut position, &mut key, &mut value) != 0 { - if PyObject_SetItem(instance_dict, key, value) < 0 { + while (dict_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) != 0 + { + if instance_dict.setitem(key, value) < 0 { result = -1; break; } @@ -200,7 +201,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - } if !slot_state.is_dict() { - let items_attribute = PyObject_GetAttrString(slot_state, crate::cstr!("items")); + let items_attribute = slot_state.getattr_cstr(crate::cstr!("items")); if items_attribute.is_null() { let message = ffi_ext::PyUnicode_FromFormat( crate::cstr!( @@ -229,7 +230,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - let mut result: c_int = 0; loop { - let pair = PyIter_Next(iterator); + let pair = iterator.iter_next(); if pair.is_null() { break; } @@ -276,7 +277,8 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while PyDict_Next(slot_state, &mut position, &mut key, &mut value) != 0 { + while (slot_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) != 0 + { if instance.set_attr(key, value) < 0 { result = -1; break; @@ -324,7 +326,7 @@ unsafe fn apply_listitems(instance: *mut PyObject, listitems: *mut PyObject) -> let mut result: c_int = 0; loop { - let item = PyIter_Next(iterator); + let item = iterator.iter_next(); if item.is_null() { break; } @@ -360,7 +362,7 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> let mut result: c_int = 0; loop { - let pair = PyIter_Next(iterator); + let pair = iterator.iter_next(); if pair.is_null() { break; } @@ -408,7 +410,7 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> }; pair.decref(); - let set_result = PyObject_SetItem(instance, key, value); + let set_result = instance.setitem(key, value); key.decref(); value.decref(); if set_result < 0 { diff --git a/src/critical_section.rs b/src/critical_section.rs index 41ff376..aabe43c 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -37,7 +37,7 @@ //! but races are possible and the state of an object may change "underneath" a suspended thread in //! possibly surprising ways. -use pyo3_ffi::*; +use crate::types::PyTypeInfo; #[cfg(Py_GIL_DISABLED)] struct CSGuard(pyo3_ffi::PyCriticalSection); @@ -64,14 +64,14 @@ impl Drop for CS2Guard { } #[inline(always)] -pub fn with_critical_section_raw(object: *mut PyObject, f: F) -> R +pub fn with_critical_section_raw(object: *mut T, f: F) -> R where F: FnOnce() -> R, { #[cfg(Py_GIL_DISABLED)] { let mut guard = CSGuard(unsafe { std::mem::zeroed() }); - unsafe { pyo3_ffi::PyCriticalSection_Begin(&mut guard.0, object) }; + unsafe { pyo3_ffi::PyCriticalSection_Begin(&mut guard.0, object as _) }; f() } #[cfg(not(Py_GIL_DISABLED))] diff --git a/src/deepcopy.rs b/src/deepcopy.rs index e673e99..b79f4e4 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -15,8 +15,8 @@ pub struct PyResult(pub *mut PyObject); impl PyResult { #[inline(always)] - pub fn ok(p: *mut PyObject) -> Self { - Self(p) + pub fn ok(p: *mut T) -> Self { + unsafe { Self(p.as_object()) } } #[inline(always)] pub fn error() -> Self { @@ -159,7 +159,7 @@ impl PyDeepCopy for *mut PyListObject { copied.set_slot_steal_unchecked(i, ellipsis); } - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } @@ -171,7 +171,7 @@ impl PyDeepCopy for *mut PyListObject { PyExc_RuntimeError, crate::cstr!("list changed size during iteration"), ); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } @@ -180,14 +180,14 @@ impl PyDeepCopy for *mut PyListObject { item.decref(); if unlikely(item_copy.is_error()) { - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } let raw = item_copy.into_raw(); let mut size_changed = false; - with_critical_section_raw(copied as _, || { + with_critical_section_raw(copied, || { if unlikely(copied.length() != sz) { size_changed = true; } else { @@ -204,13 +204,13 @@ impl PyDeepCopy for *mut PyListObject { PyExc_RuntimeError, crate::cstr!("list changed size during iteration"), ); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -241,18 +241,18 @@ impl PyDeepCopy for *mut PyTupleObject { return PyResult::ok(self.newref()); } - let existing = memo.recall_probed(self as _, &probe); + let existing = memo.recall_probed(self, &probe); if unlikely(!existing.is_null()) { copied.decref(); return PyResult::ok(existing); } - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -262,12 +262,12 @@ impl PyDeepCopy for *mut PyDictObject { unsafe { let copied = check!(py_dict_new(self.len())); - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } - let mut guard = DictIterGuard::new(self as _); + let mut guard = DictIterGuard::new(self); guard.activate(); let mut key: *mut PyObject = ptr::null_mut(); let mut value: *mut PyObject = ptr::null_mut(); @@ -278,7 +278,7 @@ impl PyDeepCopy for *mut PyDictObject { break; } if flag < 0 { - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } @@ -287,7 +287,7 @@ impl PyDeepCopy for *mut PyDictObject { key.decref(); if unlikely(key_copy.is_error()) { value.decref(); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } @@ -296,7 +296,7 @@ impl PyDeepCopy for *mut PyDictObject { value.decref(); if unlikely(val_copy.is_error()) { key_copy.into_raw().decref(); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } @@ -304,13 +304,13 @@ impl PyDeepCopy for *mut PyDictObject { let rc = copied.set_item_steal_two(key_copy.into_raw(), val_copy.into_raw()); if unlikely(rc < 0) { - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -325,7 +325,7 @@ impl PyDeepCopy for *mut PySetObject { let snapshot = check!(py_tuple_new(sz)); let mut i: Py_ssize_t = 0; - with_critical_section_raw(self as _, || { + with_critical_section_raw(self, || { let mut pos: Py_ssize_t = 0; let mut item: *mut PyObject = ptr::null_mut(); let mut hash: Py_hash_t = 0; @@ -342,7 +342,7 @@ impl PyDeepCopy for *mut PySetObject { return PyResult::error(); } - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { snapshot.decref(); copied.decref(); return PyResult::error(); @@ -353,7 +353,7 @@ impl PyDeepCopy for *mut PySetObject { let item_copy = deepcopy(item, memo); if item_copy.is_error() { snapshot.decref(); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } @@ -362,14 +362,14 @@ impl PyDeepCopy for *mut PySetObject { raw.decref(); if rc < 0 { snapshot.decref(); - memo.forget(self as _, &probe); + memo.forget(self, &probe); copied.decref(); return PyResult::error(); } } snapshot.decref(); - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -419,13 +419,13 @@ impl PyDeepCopy for *mut PyFrozensetObject { } snapshot.decref(); - let copied = frozenset_from(items as _); + let copied = frozenset_from(items); items.decref(); if copied.is_null() { return PyResult::error(); } - if memo.memoize(self as _, copied, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } @@ -445,12 +445,12 @@ impl PyDeepCopy for *mut PyByteArrayObject { ptr::copy_nonoverlapping(self.as_ptr(), copied.as_ptr(), sz as usize); } - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } @@ -475,12 +475,12 @@ impl PyDeepCopy for *mut PyMethodObject { return PyResult::error(); } - if memo.memoize(self as _, copied as _, &probe) < 0 { + if memo.memoize(self, copied, &probe) < 0 { copied.decref(); return PyResult::error(); } - PyResult::ok(copied as _) + PyResult::ok(copied) } } } diff --git a/src/dict_iter.rs b/src/dict_iter.rs index e8c67b6..89c6cdd 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -3,7 +3,7 @@ use std::hint::{likely, unlikely}; use std::ptr; #[cfg(Py_GIL_DISABLED)] use crate::{py_cache_typed, py_obj}; -use crate::types::PyObjectPtr; +use crate::types::{PyObjectPtr}; #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] extern "C" { @@ -165,23 +165,23 @@ impl DictIterGuard { } #[inline(always)] - pub unsafe fn new(dict: *mut PyObject) -> Self { + pub unsafe fn new(dict: *mut PyDictObject) -> Self { #[cfg(not(Py_3_14))] unsafe { Self { dict, pos: 0, - ver0: dict_version_tag(dict), - used0: dict_used(dict), + ver0: dict_version_tag(dict as _), + used0: dict_used(dict as _), } } #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] unsafe { Self { - dict, + dict: dict as _, pos: 0, - size0: PyDict_Size(dict), + size0: PyDict_Size(dict as _), mutated: false, size_changed: false, prev: ptr::null_mut(), diff --git a/src/memo/any.rs b/src/memo/any.rs index c6b97f5..38876de 100644 --- a/src/memo/any.rs +++ b/src/memo/any.rs @@ -3,12 +3,12 @@ use std::ffi::c_void; use std::ptr; use super::Memo; -use crate::types::PyObjectPtr; +use crate::types::{py_list_new, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; use crate::{py_cache, py_eval, py_str}; pub struct AnyMemo { pub object: *mut PyObject, - keepalive: *mut PyObject, + keepalive: *mut PyListObject, } impl AnyMemo { @@ -44,20 +44,20 @@ impl AnyMemo { } if existing != sentinel { - self.keepalive = existing; + self.keepalive = existing as *mut PyListObject; pykey.decref(); return 0; } existing.decref(); - let list = PyList_New(0); + let list = py_list_new(0); if list.is_null() { pykey.decref(); return -1; } - if PyObject_SetItem(self.object, pykey, list) < 0 { + if self.object.setitem(pykey, list.as_object()) < 0 { list.decref(); pykey.decref(); return -1; @@ -74,7 +74,7 @@ impl Memo for AnyMemo { type Probe = (); const RECALL_CAN_ERROR: bool = true; - unsafe fn recall(&mut self, object: *mut PyObject) -> ((), *mut PyObject) { + unsafe fn recall(&mut self, object: *mut T) -> ((), *mut T) { unsafe { let sentinel = py_cache!(py_eval!("object()")); let pykey = PyLong_FromVoidPtr(object as *mut c_void); @@ -99,18 +99,23 @@ impl Memo for AnyMemo { return ((), ptr::null_mut()); } - ((), found) + ((), T::cast_unchecked(found)) } } - unsafe fn memoize(&mut self, original: *mut PyObject, copy: *mut PyObject, _probe: &()) -> i32 { + unsafe fn memoize( + &mut self, + original: *mut T, + copy: *mut T, + _probe: &Self::Probe, + ) -> i32 { unsafe { let pykey = PyLong_FromVoidPtr(original as *mut c_void); if pykey.is_null() { return -1; } - let rc = PyObject_SetItem(self.object, pykey, copy); + let rc = self.object.setitem(pykey, copy.as_object()); pykey.decref(); if rc < 0 { return -1; @@ -120,11 +125,11 @@ impl Memo for AnyMemo { return -1; } - PyList_Append(self.keepalive, original) + self.keepalive.append(original) } } - unsafe fn forget(&mut self, _original: *mut PyObject, _probe: &()) {} + unsafe fn forget(&mut self, _original: *mut T, _probe: &Self::Probe) {} #[inline(always)] unsafe fn as_call_arg(&mut self) -> *mut PyObject { diff --git a/src/memo/dict.rs b/src/memo/dict.rs index 763134d..2b52848 100644 --- a/src/memo/dict.rs +++ b/src/memo/dict.rs @@ -3,7 +3,7 @@ use std::ffi::c_void; use std::ptr; use super::Memo; -use crate::types::{py_list_new, PyMapPtr, PyObjectPtr}; +use crate::types::{py_list_new, PyMapPtr, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; pub struct DictMemo { pub dict: *mut PyDictObject, @@ -48,13 +48,13 @@ impl DictMemo { return -1; } - if self.dict.set_item(pykey, list as *mut PyObject) < 0 { + if self.dict.set_item(pykey, list) < 0 { list.decref(); pykey.decref(); return -1; } - self.keepalive = list as *mut PyListObject; + self.keepalive = list; pykey.decref(); 0 } @@ -66,7 +66,7 @@ impl Memo for DictMemo { const RECALL_CAN_ERROR: bool = true; #[inline(always)] - unsafe fn recall(&mut self, object: *mut PyObject) -> ((), *mut PyObject) { + unsafe fn recall(&mut self, object: *mut T) -> ((), *mut T) { unsafe { let pykey = PyLong_FromVoidPtr(object as *mut c_void); if pykey.is_null() { @@ -78,14 +78,19 @@ impl Memo for DictMemo { found.incref(); } pykey.decref(); - ((), found) + ((), T::cast_unchecked(found)) } } #[inline(always)] - unsafe fn memoize(&mut self, original: *mut PyObject, copy: *mut PyObject, _probe: &()) -> i32 { + unsafe fn memoize( + &mut self, + original: *mut T, + copy: *mut T, + _probe: &(), + ) -> i32 { unsafe { - let pykey = PyLong_FromVoidPtr(original as *mut c_void); + let pykey = original.id(); if pykey.is_null() { return -1; } @@ -100,12 +105,12 @@ impl Memo for DictMemo { return -1; } - PyList_Append(self.keepalive as *mut PyObject, original) + self.keepalive.append(original) } } #[inline(always)] - unsafe fn forget(&mut self, _original: *mut PyObject, _probe: &()) {} + unsafe fn forget(&mut self, _original: *mut T, _probe: &()) {} #[inline(always)] unsafe fn as_call_arg(&mut self) -> *mut PyObject { diff --git a/src/memo/mod.rs b/src/memo/mod.rs index 9768535..47c224e 100644 --- a/src/memo/mod.rs +++ b/src/memo/mod.rs @@ -14,6 +14,7 @@ pub use native::PyMemoObject; pub use pytype::{memo_ready_type, Memo_Type}; pub use table::{KeepaliveVec, MemoTable, UndoLog}; pub use tss::{cleanup_memo, get_memo, pymemo_alloc}; +use crate::types::PyTypeInfo; pub type MemoCheckpoint = usize; @@ -22,25 +23,25 @@ pub trait Memo: Sized { const RECALL_CAN_ERROR: bool; - unsafe fn recall(&mut self, object: *mut PyObject) -> (Self::Probe, *mut PyObject); + unsafe fn recall(&mut self, object: *mut T) -> (Self::Probe, *mut T); - unsafe fn recall_probed( + unsafe fn recall_probed( &mut self, - object: *mut PyObject, + object: *mut T, probe: &Self::Probe, - ) -> *mut PyObject { + ) -> *mut T { let _ = probe; self.recall(object).1 } - unsafe fn memoize( + unsafe fn memoize( &mut self, - original: *mut PyObject, - copy: *mut PyObject, + original: *mut T, + copy: *mut T, probe: &Self::Probe, ) -> i32; - unsafe fn forget(&mut self, original: *mut PyObject, probe: &Self::Probe); + unsafe fn forget(&mut self, original: *mut T, probe: &Self::Probe); unsafe fn as_call_arg(&mut self) -> *mut PyObject; diff --git a/src/memo/native.rs b/src/memo/native.rs index 88d615f..d959dca 100644 --- a/src/memo/native.rs +++ b/src/memo/native.rs @@ -1,6 +1,6 @@ use super::{KeepaliveVec, Memo, MemoCheckpoint, MemoTable, UndoLog}; use crate::memo::table::{hash_pointer, TOMBSTONE}; -use crate::types::PyObjectPtr; +use crate::types::{PyMapPtr, PyObjectPtr, PyTypeInfo}; use pyo3_ffi::*; use std::ffi::c_void; use std::hint::unlikely; @@ -63,13 +63,13 @@ impl PyMemoObject { #[cold] pub unsafe fn to_dict(&self) -> *mut PyObject { unsafe { - let dict = PyDict_New(); + let dict = crate::types::py_dict_new(0); if dict.is_null() { return ptr::null_mut(); } if self.table.slots.is_null() { - return dict; + return dict.as_object(); } for i in 0..self.table.size { @@ -80,7 +80,7 @@ impl PyMemoObject { dict.decref(); return ptr::null_mut(); } - if PyDict_SetItem(dict, pykey, entry.value) < 0 { + if dict.set_item(pykey, entry.value) < 0 { pykey.decref(); dict.decref(); return ptr::null_mut(); @@ -89,14 +89,15 @@ impl PyMemoObject { } } - dict + dict.as_object() } } #[cold] pub unsafe fn sync_from_dict(&mut self, dict: *mut PyObject, orig_size: Py_ssize_t) -> i32 { unsafe { - let cur_size = PyDict_Size(dict); + let dict_typed = dict as *mut PyDictObject; + let cur_size = dict_typed.len(); if cur_size <= orig_size { return 0; } @@ -106,7 +107,7 @@ impl PyMemoObject { let mut value: *mut PyObject = ptr::null_mut(); let mut idx: Py_ssize_t = 0; - while PyDict_Next(dict, &mut pos, &mut py_key, &mut value) != 0 { + while dict_typed.dict_next(&mut pos, &mut py_key, &mut value) != 0 { idx += 1; if idx <= orig_size { continue; @@ -134,7 +135,7 @@ impl Memo for PyMemoObject { const RECALL_CAN_ERROR: bool = false; #[inline(always)] - unsafe fn recall(&mut self, object: *mut PyObject) -> (usize, *mut PyObject) { + unsafe fn recall(&mut self, object: *mut T) -> (usize, *mut T) { let key = object as usize; let hash = hash_pointer(key); let found = self.recall_probed(object, &hash); @@ -142,24 +143,28 @@ impl Memo for PyMemoObject { } #[inline(always)] - unsafe fn recall_probed(&mut self, object: *mut PyObject, probe: &usize) -> *mut PyObject { + unsafe fn recall_probed( + &mut self, + object: *mut T, + probe: &Self::Probe, + ) -> *mut T { let key = object as usize; let found = self.table.lookup_h(key, *probe); if !found.is_null() { unsafe { found.incref() }; } - found + T::cast_unchecked(found) } #[inline(always)] - unsafe fn memoize( + unsafe fn memoize( &mut self, - original: *mut PyObject, - copy: *mut PyObject, - probe: &usize, + original: *mut T, + copy: *mut T, + probe: &Self::Probe, ) -> i32 { let key = original as usize; - if unlikely(self.table.insert_h(key, copy, *probe) < 0) { + if unlikely(self.table.insert_h(key, copy.as_object(), *probe) < 0) { return -1; } self.keepalive.append(original); @@ -167,7 +172,7 @@ impl Memo for PyMemoObject { } #[cold] - unsafe fn forget(&mut self, original: *mut PyObject, probe: &usize) { + unsafe fn forget(&mut self, original: *mut T, probe: &Self::Probe) { let _ = self.table.remove_h(original as usize, *probe); } diff --git a/src/memo/table.rs b/src/memo/table.rs index 3aef319..68707d1 100644 --- a/src/memo/table.rs +++ b/src/memo/table.rs @@ -2,7 +2,7 @@ use pyo3_ffi::*; use std::hint::likely; use std::ptr; -use crate::types::PyObjectPtr; +use crate::types::{PyObjectPtr, PyTypeInfo}; pub(crate) const TOMBSTONE: usize = usize::MAX; @@ -254,9 +254,9 @@ impl KeepaliveVec { Self { items: Vec::new() } } - pub fn append(&mut self, obj: *mut PyObject) { + pub fn append(&mut self, obj: *mut T) { unsafe { obj.incref() }; - self.items.push(obj); + self.items.push(obj as _); } pub fn clear(&mut self) { diff --git a/src/reduce.rs b/src/reduce.rs index 7b2a979..df70cc3 100644 --- a/src/reduce.rs +++ b/src/reduce.rs @@ -36,7 +36,7 @@ pub(crate) unsafe fn chain_type_error(msg: *mut PyObject) { PyErr_NormalizeException(&mut cause_type, &mut cause_val, &mut cause_tb); } - let new_exc = PyObject_CallOneArg(PyExc_TypeError, msg); + let new_exc = PyExc_TypeError.call_one(msg); msg.decref(); if new_exc.is_null() { @@ -71,14 +71,14 @@ pub(crate) unsafe fn try_reduce_via_registry( if reducer.is_null() { return ptr::null_mut(); } - if PyCallable_Check(reducer) == 0 { + if !reducer.is_callable() { PyErr_SetString( PyExc_TypeError, crate::cstr!("copyreg.dispatch_table value is not callable"), ); return ptr::null_mut(); } - PyObject_CallOneArg(reducer, obj) + reducer.call_one(obj) } } @@ -273,8 +273,7 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m return ptr::null_mut(); } - let args = bail!(PyTuple_New(nargs - 1)); - let args_tup = args as *mut PyTupleObject; + let args = bail!(py_tuple_new(nargs - 1)); for i in 1..nargs { let arg = tup.get_borrowed_unchecked(i); @@ -283,10 +282,10 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m args.decref(); return ptr::null_mut(); } - args_tup.set_slot_steal_unchecked(i - 1, copied.into_raw()); + args.set_slot_steal_unchecked(i - 1, copied.into_raw()); } - let instance = call_tp_new(cls as *mut PyTypeObject, args, ptr::null_mut()); + let instance = call_tp_new(cls as *mut PyTypeObject, args.as_object(), ptr::null_mut()); args.decref(); instance } @@ -322,7 +321,7 @@ unsafe fn reconstruct_newobj_ex( } let mut coerced_args: *mut PyObject = ptr::null_mut(); - let mut coerced_kwargs: *mut PyObject = ptr::null_mut(); + let mut coerced_kwargs: *mut PyDictObject = ptr::null_mut(); if !args.is_tuple() { coerced_args = PySequence_Tuple(args); @@ -343,12 +342,12 @@ unsafe fn reconstruct_newobj_ex( } if !kwargs.is_dict() { - coerced_kwargs = PyDict_New(); + coerced_kwargs = py_dict_new(0); if coerced_kwargs.is_null() { coerced_args.decref_nullable(); return ptr::null_mut(); } - if PyDict_Merge(coerced_kwargs, kwargs, 1) < 0 { + if coerced_kwargs.merge(kwargs, 1) < 0 { let msg = ffi_ext::PyUnicode_FromFormat( crate::cstr!( "__newobj_ex__ kwargs in %s.__reduce__ result must be a dict, not %.200s" @@ -363,7 +362,7 @@ unsafe fn reconstruct_newobj_ex( coerced_kwargs.decref(); return ptr::null_mut(); } - kwargs = coerced_kwargs; + kwargs = coerced_kwargs.as_object(); } let copied_args = deepcopy::deepcopy(args, memo); @@ -402,8 +401,7 @@ unsafe fn reconstruct_callable( return callable.call(); } - let copied_args = bail!(PyTuple_New(nargs)); - let copied_tup = copied_args as *mut PyTupleObject; + let copied_args = bail!(py_tuple_new(nargs)); for i in 0..nargs { let arg = tup.get_borrowed_unchecked(i); @@ -412,10 +410,10 @@ unsafe fn reconstruct_callable( copied_args.decref(); return ptr::null_mut(); } - copied_tup.set_slot_steal_unchecked(i, copied.into_raw()); + copied_args.set_slot_steal_unchecked(i, copied.into_raw()); } - let instance = callable.call_with(copied_args); + let instance = callable.call_with(copied_args.as_object()); copied_args.decref(); instance } @@ -423,7 +421,6 @@ unsafe fn reconstruct_callable( // ── State application ────────────────────────────────────── -/// Returns 1 if __setstate__ found and applied, 0 if not found, -1 on error. unsafe fn apply_setstate( instance: *mut PyObject, state: *mut PyObject, @@ -479,7 +476,7 @@ unsafe fn apply_dict_state( copied.decref(); return -1; } - let ret = PyDict_Merge(instance_dict, copied, 1); + let ret = (instance_dict as *mut PyDictObject).merge(copied, 1); instance_dict.decref(); if ret < 0 { let msg = ffi_ext::PyUnicode_FromFormat( @@ -508,8 +505,8 @@ unsafe fn apply_dict_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while PyDict_Next(copied, &mut pos, &mut key, &mut value) != 0 { - if PyObject_SetItem(instance_dict, key, value) < 0 { + while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) != 0 { + if instance_dict.setitem(key, value) < 0 { ret = -1; break; } @@ -538,7 +535,7 @@ unsafe fn apply_slot_state( let copied = copied.into_raw(); if !copied.is_dict() { - let items_attr = PyObject_GetAttrString(copied, crate::cstr!("items")); + let items_attr = copied.getattr_cstr(crate::cstr!("items")); if items_attr.is_null() { let msg = ffi_ext::PyUnicode_FromFormat( crate::cstr!( @@ -568,7 +565,7 @@ unsafe fn apply_slot_state( let mut ret: c_int = 0; loop { - let pair = PyIter_Next(iterator); + let pair = iterator.iter_next(); if pair.is_null() { break; } @@ -610,7 +607,7 @@ unsafe fn apply_slot_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while PyDict_Next(copied, &mut pos, &mut key, &mut value) != 0 { + while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) != 0 { if instance.set_attr(key, value) < 0 { ret = -1; break; @@ -670,7 +667,7 @@ unsafe fn apply_listitems( let mut ret: c_int = 0; loop { - let item = PyIter_Next(iterator); + let item = iterator.iter_next(); if item.is_null() { break; } @@ -716,7 +713,7 @@ unsafe fn apply_dictitems( let mut ret: c_int = 0; loop { - let pair = PyIter_Next(iterator); + let pair = iterator.iter_next(); if pair.is_null() { break; } @@ -778,7 +775,7 @@ unsafe fn apply_dictitems( } value = val_copy.into_raw(); - let status = PyObject_SetItem(instance, key, value); + let status = instance.setitem(key, value); key.decref(); value.decref(); if status < 0 { diff --git a/src/types.rs b/src/types.rs index 1a02ca7..f350073 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use libc::c_ulong; use pyo3_ffi::*; +use std::ffi::c_void; use std::hint::unlikely; use std::os::raw::{c_char, c_int}; use std::ptr; @@ -25,6 +26,11 @@ pub unsafe trait PyTypeInfo: Sized { None } } + + #[inline(always)] + unsafe fn cast_unchecked(object: *mut PyObject) -> *mut Self { + object as *mut Self + } } macro_rules! pytype { @@ -50,25 +56,42 @@ pytype! { PyMethodObject => PyMethod_Type, PyMemoObject => Memo_Type, PySliceObject => PySlice_Type, - + PyLongObject => PyLong_Type, } -// ── PyAnyPtr — pointer methods on every *mut T ───────────── +// ── PyObjectPtr — pointer methods on every *mut T ───────────── pub unsafe trait PyObjectPtr { + unsafe fn id(self) -> *mut PyLongObject; + unsafe fn as_object(self) -> *mut PyObject; unsafe fn refcount(self) -> Py_ssize_t; unsafe fn incref(self); unsafe fn decref(self); unsafe fn decref_nullable(self); unsafe fn newref(self) -> *mut PyObject; unsafe fn class(self) -> *mut PyTypeObject; + unsafe fn getattr(self, name: *mut PyObject) -> *mut PyObject; + unsafe fn getattr_cstr(self, name: *const c_char) -> *mut PyObject; unsafe fn get_optional_attr(self, name: *mut PyObject, out: &mut *mut PyObject) -> c_int; + unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int; + unsafe fn set_attr_cstr(self, name: *const c_char, value: *mut PyObject) -> c_int; + unsafe fn del_attr_cstr(self, name: *const c_char) -> c_int; + unsafe fn has_attr_cstr(self, name: *const c_char) -> c_int; + unsafe fn call(self) -> *mut PyObject; unsafe fn call_one(self, arg: *mut PyObject) -> *mut PyObject; unsafe fn call_with(self, args: *mut PyObject) -> *mut PyObject; - unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int; + unsafe fn call_with_kwargs(self, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject; + + unsafe fn setitem(self, key: *mut PyObject, value: *mut PyObject) -> c_int; + unsafe fn getitem(self, key: *mut PyObject) -> *mut PyObject; unsafe fn get_iter(self) -> *mut PyObject; + unsafe fn iter_next(self) -> *mut PyObject; + + unsafe fn repr(self) -> *mut PyObject; + unsafe fn str_(self) -> *mut PyObject; + unsafe fn is_callable(self) -> bool; unsafe fn is_type(self) -> bool; unsafe fn is_tuple(self) -> bool; @@ -79,6 +102,14 @@ pub unsafe trait PyObjectPtr { } unsafe impl PyObjectPtr for *mut T { + #[inline(always)] + unsafe fn id(self) -> *mut PyLongObject { + PyLong_FromVoidPtr(self as *mut c_void) as _ + } + #[inline(always)] + unsafe fn as_object(self) -> *mut PyObject { + self as *mut PyObject + } #[inline(always)] unsafe fn refcount(self) -> Py_ssize_t { Py_REFCNT(self as *mut PyObject) @@ -105,14 +136,36 @@ unsafe impl PyObjectPtr for *mut T { unsafe fn class(self) -> *mut PyTypeObject { (*(self as *mut PyObject)).ob_type } + #[inline(always)] unsafe fn getattr(self, name: *mut PyObject) -> *mut PyObject { PyObject_GetAttr(self as *mut PyObject, name) } #[inline(always)] + unsafe fn getattr_cstr(self, name: *const c_char) -> *mut PyObject { + PyObject_GetAttrString(self as *mut PyObject, name) + } + #[inline(always)] unsafe fn get_optional_attr(self, name: *mut PyObject, out: &mut *mut PyObject) -> c_int { compat::PyObject_GetOptionalAttr(self as *mut PyObject, name, out) } + #[inline(always)] + unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int { + PyObject_SetAttr(self as *mut PyObject, name, value) + } + #[inline(always)] + unsafe fn set_attr_cstr(self, name: *const c_char, value: *mut PyObject) -> c_int { + PyObject_SetAttrString(self as *mut PyObject, name, value) + } + #[inline(always)] + unsafe fn del_attr_cstr(self, name: *const c_char) -> c_int { + PyObject_SetAttrString(self as *mut PyObject, name, ptr::null_mut()) + } + #[inline(always)] + unsafe fn has_attr_cstr(self, name: *const c_char) -> c_int { + PyObject_HasAttrString(self as *mut PyObject, name) + } + #[inline(always)] unsafe fn call(self) -> *mut PyObject { PyObject_CallNoArgs(self as *mut PyObject) @@ -126,13 +179,40 @@ unsafe impl PyObjectPtr for *mut T { PyObject_CallObject(self as *mut PyObject, args) } #[inline(always)] - unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int { - PyObject_SetAttr(self as *mut PyObject, name, value) + unsafe fn call_with_kwargs(self, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject { + PyObject_Call(self as *mut PyObject, args, kwargs) + } + + #[inline(always)] + unsafe fn setitem(self, key: *mut PyObject, value: *mut PyObject) -> c_int { + PyObject_SetItem(self as *mut PyObject, key, value) + } + #[inline(always)] + unsafe fn getitem(self, key: *mut PyObject) -> *mut PyObject { + PyObject_GetItem(self as *mut PyObject, key) } #[inline(always)] unsafe fn get_iter(self) -> *mut PyObject { PyObject_GetIter(self as *mut PyObject) } + #[inline(always)] + unsafe fn iter_next(self) -> *mut PyObject { + PyIter_Next(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn repr(self) -> *mut PyObject { + PyObject_Repr(self as *mut PyObject) + } + #[inline(always)] + unsafe fn str_(self) -> *mut PyObject { + PyObject_Str(self as *mut PyObject) + } + #[inline(always)] + unsafe fn is_callable(self) -> bool { + PyCallable_Check(self as *mut PyObject) != 0 + } + #[inline(always)] unsafe fn is_type(self) -> bool { (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_TYPE_SUBCLASS as c_ulong)) != 0 @@ -199,8 +279,13 @@ pub unsafe fn py_set_new() -> *mut PySetObject { } #[inline(always)] -pub unsafe fn frozenset_from(iterable: *mut PyObject) -> *mut PyObject { - PyFrozenSet_New(iterable) +pub unsafe fn py_set_from(iterable: *mut T) -> *mut PySetObject { + PySet_New(iterable as _) as _ +} + +#[inline(always)] +pub unsafe fn frozenset_from(iterable: *mut T) -> *mut PyFrozensetObject { + PyFrozenSet_New(iterable as _) as _ } #[inline(always)] @@ -228,7 +313,6 @@ pub unsafe trait PySeqPtr: Sized { #[inline(always)] unsafe fn get_owned_check_bounds(self, i: Py_ssize_t) -> *mut PyObject { - // analogous to _PyList_GetItemRef() if unlikely(!valid_index(i, self.length())) { return ptr::null_mut(); } @@ -266,16 +350,40 @@ unsafe impl PySeqPtr for *mut PyTupleObject { } } +// ── Mutable sequence ops (list only) ─────────────────────── + +pub unsafe trait PyMutSeqPtr { + unsafe fn append(self, item: *mut T) -> c_int; + unsafe fn as_tuple(self) -> *mut PyTupleObject; +} + +unsafe impl PyMutSeqPtr for *mut PyListObject { + #[inline(always)] + unsafe fn append(self, item: *mut T) -> c_int { + PyList_Append(self as _, item as _) + } + #[inline(always)] + unsafe fn as_tuple(self) -> *mut PyTupleObject { + PyList_AsTuple(self as _) as _ + } +} + // ── Mapping ops (dict) ───────────────────────────────────── pub unsafe trait PyMapPtr { unsafe fn len(self) -> Py_ssize_t; - /// Does NOT steal references. - unsafe fn set_item(self, k: *mut PyObject, v: *mut PyObject) -> c_int; - /// Steals both key and value references. - unsafe fn set_item_steal_two(self, k: *mut PyObject, v: *mut PyObject) -> c_int; + unsafe fn set_item(self, k: *mut KT, v: *mut VT) -> c_int; + unsafe fn set_item_steal_two(self, k: *mut KT, v: *mut VT) -> c_int; /// Returns borrowed ref or null. unsafe fn get_item(self, k: *mut PyObject) -> *mut PyObject; + unsafe fn dict_copy(self) -> *mut PyDictObject; + unsafe fn merge(self, other: *mut T, override_: c_int) -> c_int; + unsafe fn dict_next( + self, + pos: &mut Py_ssize_t, + key: &mut *mut PyObject, + value: &mut *mut PyObject, + ) -> c_int; } unsafe impl PyMapPtr for *mut PyDictObject { @@ -284,17 +392,34 @@ unsafe impl PyMapPtr for *mut PyDictObject { PyDict_Size(self as _) } #[inline(always)] - unsafe fn set_item(self, k: *mut PyObject, v: *mut PyObject) -> c_int { - PyDict_SetItem(self as _, k, v) + unsafe fn set_item(self, k: *mut KT, v: *mut VT) -> c_int { + PyDict_SetItem(self as _, k as _, v as _) } #[inline(always)] - unsafe fn set_item_steal_two(self, k: *mut PyObject, v: *mut PyObject) -> c_int { - compat::_PyDict_SetItem_Take2(self as _, k, v) + unsafe fn set_item_steal_two(self, k: *mut KT, v: *mut VT) -> c_int { + compat::_PyDict_SetItem_Take2(self as _, k as _, v as _) } #[inline(always)] unsafe fn get_item(self, k: *mut PyObject) -> *mut PyObject { PyDict_GetItemWithError(self as _, k) } + #[inline(always)] + unsafe fn dict_copy(self) -> *mut PyDictObject { + PyDict_Copy(self as _) as _ + } + #[inline(always)] + unsafe fn merge(self, other: *mut T, override_: c_int) -> c_int { + PyDict_Merge(self as _, other as _, override_) + } + #[inline(always)] + unsafe fn dict_next( + self, + pos: &mut Py_ssize_t, + key: &mut *mut PyObject, + value: &mut *mut PyObject, + ) -> c_int { + PyDict_Next(self as _, pos, key, value) + } } pub unsafe trait PySetPtr { From 7928c60fa0410afe0adde25c2fc40445eeb954fe Mon Sep 17 00:00:00 2001 From: Bobronium Date: Fri, 20 Mar 2026 12:14:01 +0400 Subject: [PATCH 02/10] Move CPython API to py crate --- src/about.rs | 167 +++++++---- src/cache.rs | 78 +++-- src/compat.rs | 71 +---- src/config.rs | 13 +- src/copy.rs | 51 ++-- src/critical_section.rs | 6 +- src/deepcopy.rs | 11 +- src/dict_iter.rs | 77 ++--- src/extra.rs | 65 +++-- src/fallback.rs | 237 ++++++++------- src/ffi_ext.rs | 116 +------- src/lib.rs | 107 ++++--- src/memo/any.rs | 58 ++-- src/memo/dict.rs | 8 +- src/memo/native.rs | 11 +- src/memo/pytype.rs | 197 ++++++------- src/memo/tss.rs | 6 +- src/patch.rs | 112 ++++---- src/py/boolean.rs | 6 + src/py/bytearray.rs | 33 +++ src/py/call.rs | 45 +++ src/py/capsule.rs | 24 ++ src/py/critical_section.rs | 40 +++ src/py/dict.rs | 208 ++++++++++++++ src/py/err.rs | 104 +++++++ src/py/eval.rs | 30 ++ src/py/ffi.rs | 207 +++++++++++++ src/py/frame.rs | 24 ++ src/py/gc.rs | 30 ++ src/py/list.rs | 72 +++++ src/py/long.rs | 40 +++ src/py/method.rs | 29 ++ src/py/mod.rs | 114 ++++++++ src/py/module.rs | 48 ++++ src/py/object.rs | 362 +++++++++++++++++++++++ src/py/seq.rs | 120 ++++++++ src/py/set.rs | 85 ++++++ src/py/tuple.rs | 29 ++ src/py/type_object.rs | 71 +++++ src/py/unicode.rs | 135 +++++++++ src/py/vectorcall.rs | 36 +++ src/recursion.rs | 4 +- src/reduce.rs | 109 ++++--- src/state.rs | 30 +- src/types.rs | 576 ++----------------------------------- 45 files changed, 2636 insertions(+), 1366 deletions(-) create mode 100644 src/py/boolean.rs create mode 100644 src/py/bytearray.rs create mode 100644 src/py/call.rs create mode 100644 src/py/capsule.rs create mode 100644 src/py/critical_section.rs create mode 100644 src/py/dict.rs create mode 100644 src/py/err.rs create mode 100644 src/py/eval.rs create mode 100644 src/py/ffi.rs create mode 100644 src/py/frame.rs create mode 100644 src/py/gc.rs create mode 100644 src/py/list.rs create mode 100644 src/py/long.rs create mode 100644 src/py/method.rs create mode 100644 src/py/mod.rs create mode 100644 src/py/module.rs create mode 100644 src/py/object.rs create mode 100644 src/py/seq.rs create mode 100644 src/py/set.rs create mode 100644 src/py/tuple.rs create mode 100644 src/py/type_object.rs create mode 100644 src/py/unicode.rs create mode 100644 src/py/vectorcall.rs diff --git a/src/about.rs b/src/about.rs index ee12184..021678d 100644 --- a/src/about.rs +++ b/src/about.rs @@ -1,7 +1,9 @@ use pyo3_ffi::*; +use std::ffi::CString; use std::ptr; -use crate::types::PyObjectPtr; +use crate::py; +use crate::types::{PyObjectPtr, PySeqPtr}; static mut ABOUT_METHODS: [PyMethodDef; 1] = [PyMethodDef::zeroed()]; @@ -21,43 +23,60 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { unsafe { ABOUT_MODULE_DEF.m_methods = ptr::addr_of_mut!(ABOUT_METHODS).cast::(); - let module = PyModule_Create(std::ptr::addr_of_mut!(ABOUT_MODULE_DEF)); + let module = py::module::create(std::ptr::addr_of_mut!(ABOUT_MODULE_DEF)); if module.is_null() { return -1; } let version = env!("CARGO_PKG_VERSION"); - let version_cstr = format!("{version}\0"); - PyModule_AddStringConstant( + let version_cstring = CString::new(version).unwrap(); + py::module::add_string_constant( module, crate::cstr!("__version__"), - version_cstr.as_ptr() as _, + version_cstring.as_c_str(), ); - // VersionInfo namedtuple - let collections = PyImport_ImportModule(crate::cstr!("collections")); + let collections = py::module::import(crate::cstr!("collections")); if collections.is_null() { module.decref(); return -1; } - let namedtuple = PyObject_GetAttrString(collections, crate::cstr!("namedtuple")); + let namedtuple = collections.getattr_cstr(crate::cstr!("namedtuple")); collections.decref(); if namedtuple.is_null() { module.decref(); return -1; } - let vi_cls = PyObject_CallFunction( - namedtuple, - crate::cstr!("s[ssssss]"), - crate::cstr!("VersionInfo"), - crate::cstr!("major"), - crate::cstr!("minor"), - crate::cstr!("patch"), - crate::cstr!("pre"), - crate::cstr!("dev"), - crate::cstr!("local"), - ); + let version_info_name = py::unicode::from_cstr(crate::cstr!("VersionInfo")); + let version_info_fields = py::list::new(6); + if version_info_name.is_null() || version_info_fields.is_null() { + version_info_name.decref_if_nonnull(); + version_info_fields.decref_if_nonnull(); + namedtuple.decref(); + module.decref(); + return -1; + } + version_info_fields.steal_item_unchecked(0, py::unicode::from_cstr(crate::cstr!("major")).as_object()); + version_info_fields.steal_item_unchecked(1, py::unicode::from_cstr(crate::cstr!("minor")).as_object()); + version_info_fields.steal_item_unchecked(2, py::unicode::from_cstr(crate::cstr!("patch")).as_object()); + version_info_fields.steal_item_unchecked(3, py::unicode::from_cstr(crate::cstr!("pre")).as_object()); + version_info_fields.steal_item_unchecked(4, py::unicode::from_cstr(crate::cstr!("dev")).as_object()); + version_info_fields.steal_item_unchecked(5, py::unicode::from_cstr(crate::cstr!("local")).as_object()); + + let version_info_args = py::tuple::new(2); + if version_info_args.is_null() { + version_info_name.decref(); + version_info_fields.decref(); + namedtuple.decref(); + module.decref(); + return -1; + } + version_info_args.steal_item_unchecked(0, version_info_name.as_object()); + version_info_args.steal_item_unchecked(1, version_info_fields.as_object()); + + let vi_cls = namedtuple.call_with(version_info_args); + version_info_args.decref(); if vi_cls.is_null() { namedtuple.decref(); module.decref(); @@ -82,60 +101,100 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { .unwrap_or(0); let build_hash = env!("COPIUM_BUILD_HASH"); - let local_str = format!("{build_hash}\0"); - - let vi = PyObject_CallFunction( - vi_cls, - crate::cstr!("lllOOs"), - major, - minor, - patch, - Py_None(), - Py_None(), - local_str.as_ptr() as *const core::ffi::c_char, + let local_cstring = CString::new(build_hash).unwrap(); + + let version_info_value_args = py::tuple::new(6); + if version_info_value_args.is_null() { + namedtuple.decref(); + vi_cls.decref(); + module.decref(); + return -1; + } + version_info_value_args.steal_item_unchecked(0, py::long::from_i64(major).as_object()); + version_info_value_args.steal_item_unchecked(1, py::long::from_i64(minor).as_object()); + version_info_value_args.steal_item_unchecked(2, py::long::from_i64(patch).as_object()); + version_info_value_args.steal_item_unchecked(3, py::none().newref()); + version_info_value_args.steal_item_unchecked(4, py::none().newref()); + version_info_value_args.steal_item_unchecked( + 5, + py::unicode::from_cstr(local_cstring.as_c_str()).as_object(), ); - PyModule_AddObject(module, crate::cstr!("VersionInfo"), vi_cls); + let vi = vi_cls.call_with(version_info_value_args); + version_info_value_args.decref(); + + py::module::add_object(module, crate::cstr!("VersionInfo"), vi_cls); if !vi.is_null() { - PyModule_AddObject(module, crate::cstr!("__version_tuple__"), vi); + py::module::add_object(module, crate::cstr!("__version_tuple__"), vi); } - // __commit_id__ - PyModule_AddObject(module, crate::cstr!("__commit_id__"), Py_None().newref()); + py::module::add_object(module, crate::cstr!("__commit_id__"), py::none().newref()); - // __build_hash__ - PyModule_AddStringConstant( + py::module::add_string_constant( module, crate::cstr!("__build_hash__"), - local_str.as_ptr() as _, + local_cstring.as_c_str(), ); - // Author namedtuple - let author_cls = PyObject_CallFunction( - namedtuple, - crate::cstr!("s[ss]"), - crate::cstr!("Author"), - crate::cstr!("name"), - crate::cstr!("email"), - ); + let author_name = py::unicode::from_cstr(crate::cstr!("Author")); + let author_fields = py::list::new(2); + if author_name.is_null() || author_fields.is_null() { + author_name.decref_if_nonnull(); + author_fields.decref_if_nonnull(); + namedtuple.decref(); + vi_cls.decref(); + module.decref(); + return -1; + } + author_fields.steal_item_unchecked(0, py::unicode::from_cstr(crate::cstr!("name")).as_object()); + author_fields.steal_item_unchecked(1, py::unicode::from_cstr(crate::cstr!("email")).as_object()); + + let author_type_args = py::tuple::new(2); + if author_type_args.is_null() { + author_name.decref(); + author_fields.decref(); + namedtuple.decref(); + vi_cls.decref(); + module.decref(); + return -1; + } + author_type_args.steal_item_unchecked(0, author_name.as_object()); + author_type_args.steal_item_unchecked(1, author_fields.as_object()); + + let author_cls = namedtuple.call_with(author_type_args); + author_type_args.decref(); namedtuple.decref(); if author_cls.is_null() { module.decref(); return -1; } - let author = PyObject_CallFunction( - author_cls, - crate::cstr!("ss"), - crate::cstr!("Arseny Boykov (Bobronium)"), - crate::cstr!("hi@bobronium.me"), + let author_value_args = py::tuple::new(2); + if author_value_args.is_null() { + author_cls.decref(); + module.decref(); + return -1; + } + author_value_args.steal_item_unchecked( + 0, + py::unicode::from_cstr(crate::cstr!("Arseny Boykov (Bobronium)")).as_object(), + ); + author_value_args.steal_item_unchecked( + 1, + py::unicode::from_cstr(crate::cstr!("hi@bobronium.me")).as_object(), ); - PyModule_AddObject(module, crate::cstr!("Author"), author_cls); + let author = author_cls.call_with(author_value_args); + author_value_args.decref(); + py::module::add_object(module, crate::cstr!("Author"), author_cls); if !author.is_null() { - let authors = PyTuple_New(1); - PyTuple_SetItem(authors, 0, author); - PyModule_AddObject(module, crate::cstr!("__authors__"), authors); + let authors = py::tuple::new(1); + if authors.is_null() { + module.decref(); + return -1; + } + authors.steal_item_unchecked(0, author); + py::module::add_object(module, crate::cstr!("__authors__"), authors); } crate::add_submodule(parent, crate::cstr!("__about__"), module) diff --git a/src/cache.rs b/src/cache.rs index ba9a741..ef06c16 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,12 +1,13 @@ use std::cell::UnsafeCell; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::mem::MaybeUninit; -use std::os::raw::{c_char, c_int}; +use std::os::raw::c_int; use std::ptr; use pyo3_ffi::*; -use crate::types::PyObjectPtr; +use crate::py; +use crate::types::{PyMapPtr, PyObjectPtr}; // ── Slot types ───────────────────────────────────────────── @@ -69,8 +70,8 @@ init_phase!(StrEntry, ObjEntry, CacheEntry); // ── Primitives ───────────────────────────────────────────── -pub unsafe fn intern_str(s: *const c_char) -> *mut PyObject { - unsafe { PyUnicode_InternFromString(s) } +pub unsafe fn intern_str(string: &CStr) -> *mut PyObject { + unsafe { py::unicode::intern(string).as_object() } } /// Resolve a dotted path like "decimal.Decimal" or "xml.etree.ElementTree.Element". @@ -80,12 +81,7 @@ pub unsafe fn intern_str(s: *const c_char) -> *mut PyObject { pub unsafe fn resolve_path(path: &str) -> *mut PyObject { let segments: Vec<&str> = path.split('.').collect(); if segments.is_empty() { - unsafe { - PyErr_SetString( - PyExc_ValueError, - b"cache::resolve_path: empty path\0".as_ptr().cast(), - ); - } + unsafe { py::err::set_string(PyExc_ValueError, crate::cstr!("cache::resolve_path: empty path")) }; return ptr::null_mut(); } @@ -96,9 +92,9 @@ pub unsafe fn resolve_path(path: &str) -> *mut PyObject { let mut cur: *mut PyObject; unsafe { - let builtins = PyEval_GetBuiltins(); + let builtins = py::eval::builtins(); let builtin_hit = if !builtins.is_null() { - PyDict_GetItemString(builtins, first.as_ptr()) + builtins.borrow_item_cstr(first.as_c_str()) } else { ptr::null_mut() }; @@ -106,8 +102,8 @@ pub unsafe fn resolve_path(path: &str) -> *mut PyObject { if !builtin_hit.is_null() { cur = builtin_hit.newref(); } else { - PyErr_Clear(); - cur = PyImport_ImportModule(first.as_ptr()); + py::err::clear(); + cur = py::module::import(first.as_c_str()); if cur.is_null() { return ptr::null_mut(); } @@ -126,17 +122,17 @@ pub unsafe fn resolve_path(path: &str) -> *mut PyObject { }; unsafe { - let next = PyObject_GetAttrString(cur, seg.as_ptr()); + let next = cur.getattr_cstr(seg.as_c_str()); if !next.is_null() { cur.decref(); cur = next; continue; } - PyErr_Clear(); + py::err::clear(); let dotted: String = segments[..=i].join("."); if let Ok(module_path) = CString::new(dotted) { - let module = PyImport_ImportModule(module_path.as_ptr()); + let module = py::module::import(module_path.as_c_str()); if !module.is_null() { cur.decref(); cur = module; @@ -144,11 +140,11 @@ pub unsafe fn resolve_path(path: &str) -> *mut PyObject { } } - PyErr_Clear(); + py::err::clear(); let path_cstr = CString::new(path).unwrap_or_default(); - PyErr_Format( + py::err::format!( PyExc_AttributeError, - b"cache: cannot resolve '%s' in '%s'\0".as_ptr().cast(), + crate::cstr!("cache: cannot resolve '%s' in '%s'"), seg.as_ptr(), path_cstr.as_ptr(), ); @@ -163,49 +159,47 @@ pub unsafe fn resolve_path(path: &str) -> *mut PyObject { pub unsafe fn resolve_path_optional(path: &str) -> *mut PyObject { let result = unsafe { resolve_path(path) }; if result.is_null() { - unsafe { - PyErr_Clear(); - } + unsafe { py::err::clear() }; } result } unsafe fn make_globals() -> *mut PyObject { unsafe { - let globals = PyDict_New(); + let globals = py::dict::new(); if globals.is_null() { return ptr::null_mut(); } - let builtins = PyEval_GetBuiltins(); + let builtins = py::eval::builtins(); if !builtins.is_null() - && PyDict_SetItemString(globals, b"__builtins__\0".as_ptr().cast(), builtins) < 0 + && globals.set_item_cstr(crate::cstr!("__builtins__"), builtins) < 0 { globals.decref(); return ptr::null_mut(); } - globals + globals.as_object() } } -pub unsafe fn eval_cstr(code: *const c_char) -> *mut PyObject { +pub unsafe fn eval_cstr(code: &CStr) -> *mut PyObject { unsafe { let globals = make_globals(); if globals.is_null() { return ptr::null_mut(); } - let result = PyRun_StringFlags(code, Py_eval_input, globals, globals, ptr::null_mut()); + let result = py::eval::run_string(code, Py_eval_input, globals, globals); globals.decref(); result } } -pub unsafe fn exec_cstr(code: *const c_char) -> *mut PyDictObject { +pub unsafe fn exec_cstr(code: &CStr) -> *mut PyDictObject { unsafe { let globals = make_globals(); if globals.is_null() { return ptr::null_mut(); } - let result = PyRun_StringFlags(code, Py_file_input, globals, globals, ptr::null_mut()); + let result = py::eval::run_string(code, Py_file_input, globals, globals); if result.is_null() { globals.decref(); return ptr::null_mut(); @@ -238,12 +232,12 @@ fn dedent(s: &str) -> CString { pub unsafe fn eval_str(s: &str) -> *mut PyObject { let code = dedent(s); - unsafe { eval_cstr(code.as_ptr()) } + unsafe { eval_cstr(code.as_c_str()) } } pub unsafe fn exec_str(s: &str) -> *mut PyDictObject { let code = dedent(s); - unsafe { exec_cstr(code.as_ptr()) } + unsafe { exec_cstr(code.as_c_str()) } } // ── Init ─────────────────────────────────────────────────── @@ -273,9 +267,7 @@ macro_rules! py_str { static SLOT: $crate::cache::PtrSlot = $crate::cache::PtrSlot::new(); unsafe fn __init() -> ::std::os::raw::c_int { - let val = $crate::cache::intern_str( - concat!($s, "\0").as_ptr() as *const ::std::os::raw::c_char - ); + let val = $crate::cache::intern_str($crate::cstr!($s)); if val.is_null() { return -1; } @@ -354,11 +346,15 @@ macro_rules! py_type { if val.is_null() { return -1; } - if ::pyo3_ffi::PyType_Check(val) == 0 { - ::pyo3_ffi::PyErr_SetString( + if !$crate::types::PyObjectPtr::is_type(val) { + $crate::py::err::set_string( ::pyo3_ffi::PyExc_TypeError, - concat!("py_type!(\"", $path, "\"): resolved to non-type\0").as_ptr() - as *const ::std::os::raw::c_char, + unsafe { + ::std::ffi::CStr::from_bytes_with_nul_unchecked( + concat!("py_type!(\"", $path, "\"): resolved to non-type\0") + .as_bytes(), + ) + }, ); $crate::types::PyObjectPtr::decref(val); return -1; diff --git a/src/compat.rs b/src/compat.rs index 0b1439b..2b3e842 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -1,71 +1,4 @@ #![allow(non_snake_case)] -// These shims intentionally mirror CPython C API names. +#![allow(unused_imports)] -use std::os::raw::c_int; - -use pyo3_ffi::*; - -#[cfg(not(Py_3_13))] -use crate::types::PyObjectPtr; - -extern "C" { - pub fn _PyDict_NewPresized(minused: Py_ssize_t) -> *mut PyObject; - pub fn _PySet_NextEntry( - set: *mut PyObject, - pos: *mut Py_ssize_t, - key: *mut *mut PyObject, - hash: *mut Py_hash_t, - ) -> c_int; -} - -#[cfg(Py_3_13)] -extern "C" { - pub fn _PyDict_SetItem_Take2( - op: *mut PyObject, - key: *mut PyObject, - value: *mut PyObject, - ) -> c_int; -} - -#[cfg(not(Py_3_13))] -#[inline] -pub unsafe fn _PyDict_SetItem_Take2( - op: *mut PyObject, - key: *mut PyObject, - value: *mut PyObject, -) -> c_int { - unsafe { - let status = PyDict_SetItem(op, key, value); - key.decref(); - value.decref(); - status - } -} - -#[cfg(any(Py_3_13, Py_3_14))] -extern "C" { - pub fn PyObject_GetOptionalAttr( - obj: *mut PyObject, - name: *mut PyObject, - result: *mut *mut PyObject, - ) -> c_int; -} - -#[cfg(not(any(Py_3_13, Py_3_14)))] -extern "C" { - fn _PyObject_LookupAttr( - obj: *mut PyObject, - name: *mut PyObject, - result: *mut *mut PyObject, - ) -> c_int; -} - -#[cfg(not(any(Py_3_13, Py_3_14)))] -#[inline] -pub unsafe fn PyObject_GetOptionalAttr( - obj: *mut PyObject, - name: *mut PyObject, - result: *mut *mut PyObject, -) -> c_int { - unsafe { _PyObject_LookupAttr(obj, name, result) } -} +pub use crate::py::ffi::{_PyDict_NewPresized, _PyDict_SetItem_Take2, PyObject_GetOptionalAttr}; diff --git a/src/config.rs b/src/config.rs index ef6904e..17e8981 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; use pyo3_ffi::PyObject; +use crate::py; use crate::state::{MemoMode, OnIncompatible, STATE}; use crate::types::PyObjectPtr; @@ -112,19 +113,19 @@ fn apply( if let Some(suppress_warnings_object) = suppress_warnings { unsafe { let new_tuple = if suppress_warnings_object.is_none() { - pyo3_ffi::PyTuple_New(0) + py::tuple::new(0).as_object() } else { - pyo3_ffi::PySequence_Tuple(suppress_warnings_object.as_ptr()) + py::seq::to_tuple(suppress_warnings_object.as_ptr()).as_object() }; if new_tuple.is_null() { return Err(PyErr::take(py) .unwrap_or_else(|| PyRuntimeError::new_err("PySequence_Tuple failed"))); } - let suppress_warning_count = pyo3_ffi::PyTuple_Size(new_tuple); + let suppress_warning_count = py::tuple::size(new_tuple); for index in 0..suppress_warning_count { - let item = pyo3_ffi::PyTuple_GetItem(new_tuple, index); - if pyo3_ffi::PyUnicode_Check(item) == 0 { + let item = py::tuple::get_item(new_tuple, index); + if !item.is_unicode() { let item_type_name = Bound::::from_borrowed_ptr(py, item) .get_type() .name() @@ -178,7 +179,7 @@ fn get(py: Python<'_>) -> PyResult> { if !ignored_errors.is_null() { ignored_errors.newref() } else { - pyo3_ffi::PyTuple_New(0) + py::tuple::new(0).as_object() } }; let sw_obj = unsafe { Bound::from_owned_ptr(py, sw) }.cast_into::()?; diff --git a/src/copy.rs b/src/copy.rs index 6ebf366..823d1e0 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -1,7 +1,8 @@ use crate::deepcopy::PyResult; +use crate::py; use crate::reduce::{self, ReduceKind}; use crate::types::*; -use crate::{ffi_ext, py_str}; +use crate::{py_str}; use pyo3_ffi::*; use std::os::raw::c_int; use std::ptr; @@ -154,10 +155,10 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - return -1; } - let result = (instance_dict as *mut PyDictObject).merge(dict_state, 1); + let result = (instance_dict as *mut PyDictObject).merge(dict_state, true); instance_dict.decref(); if result < 0 { - let message = ffi_ext::PyUnicode_FromFormat( + let message = py::unicode::from_format!( crate::cstr!( "dict state from %s.__reduce__ must be a dict or mapping, got %.200s" ), @@ -165,7 +166,7 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - (*dict_state.class()).tp_name, ); if !message.is_null() { - reduce::chain_type_error(message); + reduce::chain_type_error(message.as_object()); } } return result; @@ -181,7 +182,7 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while (dict_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) != 0 + while (dict_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) { if instance_dict.setitem(key, value) < 0 { result = -1; @@ -203,7 +204,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - if !slot_state.is_dict() { let items_attribute = slot_state.getattr_cstr(crate::cstr!("items")); if items_attribute.is_null() { - let message = ffi_ext::PyUnicode_FromFormat( + let message = py::unicode::from_format!( crate::cstr!( "slot state from %s.__reduce__ must be a dict or have an items() method, got %.200s" ), @@ -211,7 +212,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - (*slot_state.class()).tp_name, ); if !message.is_null() { - reduce::chain_type_error(message); + reduce::chain_type_error(message.as_object()); } return -1; } @@ -235,18 +236,17 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - break; } - let sequence = - ffi_ext::PySequence_Fast(pair, crate::cstr!("items() must return pairs")); + let sequence = py::seq::fast(pair, crate::cstr!("items() must return pairs")); pair.decref(); if sequence.is_null() { result = -1; break; } - if ffi_ext::PySequence_Fast_GET_SIZE(sequence) != 2 { + if py::seq::fast_length(sequence) != 2 { sequence.decref(); - if PyErr_Occurred().is_null() { - PyErr_SetString( + if py::err::occurred().is_null() { + py::err::set_string( PyExc_ValueError, crate::cstr!("not enough values to unpack"), ); @@ -255,8 +255,8 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - break; } - let key = ffi_ext::PySequence_Fast_GET_ITEM(sequence, 0); - let value = ffi_ext::PySequence_Fast_GET_ITEM(sequence, 1); + let key = py::seq::fast_borrow_item_unchecked(sequence, 0); + let value = py::seq::fast_borrow_item_unchecked(sequence, 1); let set_result = instance.set_attr(key, value); sequence.decref(); if set_result < 0 { @@ -265,7 +265,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - } } - if result == 0 && !PyErr_Occurred().is_null() { + if result == 0 && !py::err::occurred().is_null() { result = -1; } iterator.decref(); @@ -277,7 +277,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while (slot_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) != 0 + while (slot_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) { if instance.set_attr(key, value) < 0 { result = -1; @@ -340,7 +340,7 @@ unsafe fn apply_listitems(instance: *mut PyObject, listitems: *mut PyObject) -> append_result.decref(); } - if result == 0 && !PyErr_Occurred().is_null() { + if result == 0 && !py::err::occurred().is_null() { result = -1; } iterator.decref(); @@ -374,26 +374,25 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> pair_tuple.get_borrowed_unchecked(1).newref(), ) } else { - let sequence = - ffi_ext::PySequence_Fast(pair, crate::cstr!("cannot unpack non-sequence")); + let sequence = py::seq::fast(pair, crate::cstr!("cannot unpack non-sequence")); if sequence.is_null() { pair.decref(); result = -1; break; } - let item_count = ffi_ext::PySequence_Fast_GET_SIZE(sequence); + let item_count = py::seq::fast_length(sequence); if item_count != 2 { sequence.decref(); pair.decref(); if item_count < 2 { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_ValueError, crate::cstr!("not enough values to unpack (expected 2, got %zd)"), item_count, ); } else { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_ValueError, crate::cstr!("too many values to unpack (expected 2, got %zd)"), item_count, @@ -403,8 +402,8 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> break; } - let key = ffi_ext::PySequence_Fast_GET_ITEM(sequence, 0).newref(); - let value = ffi_ext::PySequence_Fast_GET_ITEM(sequence, 1).newref(); + let key = py::seq::fast_borrow_item_unchecked(sequence, 0).newref(); + let value = py::seq::fast_borrow_item_unchecked(sequence, 1).newref(); sequence.decref(); (key, value) }; @@ -419,7 +418,7 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> } } - if result == 0 && !PyErr_Occurred().is_null() { + if result == 0 && !py::err::occurred().is_null() { result = -1; } iterator.decref(); @@ -458,7 +457,7 @@ unsafe fn copy_via_reduce(object: *mut PyObject) -> PyResult { let mut reduce_result = reduce::try_reduce_via_registry(object, class); if reduce_result.is_null() { - if !PyErr_Occurred().is_null() { + if !crate::py::err::occurred().is_null() { return PyResult::error(); } diff --git a/src/critical_section.rs b/src/critical_section.rs index aabe43c..4a89229 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -46,7 +46,7 @@ struct CSGuard(pyo3_ffi::PyCriticalSection); impl Drop for CSGuard { fn drop(&mut self) { unsafe { - pyo3_ffi::PyCriticalSection_End(&mut self.0); + py::critical_section::end(&mut self.0); } } } @@ -58,7 +58,7 @@ struct CS2Guard(pyo3_ffi::PyCriticalSection2); impl Drop for CS2Guard { fn drop(&mut self) { unsafe { - pyo3_ffi::PyCriticalSection2_End(&mut self.0); + py::critical_section::end2(&mut self.0); } } } @@ -71,7 +71,7 @@ where #[cfg(Py_GIL_DISABLED)] { let mut guard = CSGuard(unsafe { std::mem::zeroed() }); - unsafe { pyo3_ffi::PyCriticalSection_Begin(&mut guard.0, object as _) }; + unsafe { py::critical_section::begin(&mut guard.0, object) }; f() } #[cfg(not(Py_GIL_DISABLED))] diff --git a/src/deepcopy.rs b/src/deepcopy.rs index b79f4e4..028ecb5 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -5,6 +5,7 @@ use std::ptr; use crate::critical_section::with_critical_section_raw; use crate::dict_iter::DictIterGuard; use crate::memo::Memo; +use crate::py; use crate::{ffi_ext::*, py_str}; use crate::types::*; @@ -111,7 +112,7 @@ pub unsafe fn deepcopy(object: *mut PyObject, memo: &mut M) -> PyResult if !found.is_null() { return PyResult::ok(found); } - if M::RECALL_CAN_ERROR && unlikely(!PyErr_Occurred().is_null()) { + if M::RECALL_CAN_ERROR && unlikely(!crate::py::err::occurred().is_null()) { return PyResult::error(); } @@ -167,7 +168,7 @@ impl PyDeepCopy for *mut PyListObject { for i in 0..sz { let item = self.get_owned_check_bounds(i); if unlikely(item.is_null()) { - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("list changed size during iteration"), ); @@ -200,7 +201,7 @@ impl PyDeepCopy for *mut PyListObject { }); if unlikely(size_changed) { raw.decref(); - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("list changed size during iteration"), ); @@ -329,7 +330,7 @@ impl PyDeepCopy for *mut PySetObject { let mut pos: Py_ssize_t = 0; let mut item: *mut PyObject = ptr::null_mut(); let mut hash: Py_hash_t = 0; - while self.next_entry(&mut pos, &mut item, &mut hash) != 0 { + while self.next_entry(&mut pos, &mut item, &mut hash) { item.incref(); snapshot.set_slot_steal_unchecked(i, item); i += 1; @@ -395,7 +396,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { let mut item: *mut PyObject = ptr::null_mut(); let mut hash: Py_hash_t = 0; let mut i: Py_ssize_t = 0; - while self.next_entry(&mut pos, &mut item, &mut hash) != 0 { + while self.next_entry(&mut pos, &mut item, &mut hash) { item.incref(); snapshot.set_slot_steal_unchecked(i, item); i += 1; diff --git a/src/dict_iter.rs b/src/dict_iter.rs index 89c6cdd..f50de93 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -3,30 +3,11 @@ use std::hint::{likely, unlikely}; use std::ptr; #[cfg(Py_GIL_DISABLED)] use crate::{py_cache_typed, py_obj}; -use crate::types::{PyObjectPtr}; - -#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -extern "C" { - fn PyDict_AddWatcher( - callback: Option< - unsafe extern "C" fn( - event: i32, - dict: *mut PyObject, - key: *mut PyObject, - new_value: *mut PyObject, - ) -> i32, - >, - ) -> i32; - fn PyDict_ClearWatcher(watcher_id: i32) -> i32; - fn PyDict_Watch(watcher_id: i32, dict: *mut PyObject) -> i32; - fn PyDict_Unwatch(watcher_id: i32, dict: *mut PyObject) -> i32; -} - -#[cfg(all(Py_3_14, Py_GIL_DISABLED))] -extern "C" { - fn PyIter_NextItem(iter: *mut PyObject, item: *mut *mut PyObject) -> i32; -} - +use crate::py; +#[cfg(Py_GIL_DISABLED)] +use crate::py::vectorcall::PyVectorcallPtr; +use crate::types::PyObjectPtr; +use crate::py::dict::PyMapPtr; #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] static mut G_DICT_WATCHER_ID: i32 = -1; @@ -60,7 +41,7 @@ unsafe extern "C" fn copium_dict_watcher_cb( while !cur_guard.is_null() { let guard = unsafe { &mut *cur_guard }; if likely(guard.dict == dict) { - let cur = unsafe { PyDict_Size(dict) }; + let cur = unsafe { py::dict::size(dict) }; if unlikely(cur >= 0 && cur != guard.size0) { guard.size_changed = true; } @@ -131,11 +112,11 @@ impl DictIterGuard { #[inline(always)] fn cached_dict_items_vc() -> pyo3_ffi::vectorcallfunc { py_cache_typed!(pyo3_ffi::vectorcallfunc, { - match crate::ffi_ext::PyVectorcall_Function(py_obj!("dict.items")) { + match py_obj!("dict.items").vectorcall_function() { Some(f) => Some(f), None => { - pyo3_ffi::PyErr_SetString( - pyo3_ffi::PyExc_TypeError, + py::err::set_string( + PyExc_TypeError, crate::cstr!("copium: dict.items has no vectorcall"), ); None @@ -158,7 +139,7 @@ impl DictIterGuard { return ptr::null_mut(); } - let iterator = PyObject_GetIter(dict_items_view); + let iterator = dict_items_view.get_iter(); dict_items_view.decref(); iterator } @@ -169,7 +150,7 @@ impl DictIterGuard { #[cfg(not(Py_3_14))] unsafe { Self { - dict, + dict: dict as *mut PyObject, pos: 0, ver0: dict_version_tag(dict as _), used0: dict_used(dict as _), @@ -181,7 +162,7 @@ impl DictIterGuard { Self { dict: dict as _, pos: 0, - size0: PyDict_Size(dict as _), + size0: py::dict::size(dict), mutated: false, size_changed: false, prev: ptr::null_mut(), @@ -238,7 +219,7 @@ impl DictIterGuard { } if unlikely(need_watch && unsafe { G_DICT_WATCHER_REGISTERED }) { - let _ = unsafe { PyDict_Watch(G_DICT_WATCHER_ID, self.dict) }; + let _ = unsafe { (self.dict as *mut PyDictObject).watch(G_DICT_WATCHER_ID) }; } } @@ -269,7 +250,7 @@ impl DictIterGuard { let need_unwatch = dict_watch_count(dict) == 0; if unlikely(need_unwatch && G_DICT_WATCHER_REGISTERED) { - let _ = PyDict_Unwatch(G_DICT_WATCHER_ID, dict); + let _ = (dict as *mut PyDictObject).unwatch(G_DICT_WATCHER_ID); } self.prev = ptr::null_mut(); @@ -300,17 +281,17 @@ impl DictIterGuard { let mut k: *mut PyObject = ptr::null_mut(); let mut v: *mut PyObject = ptr::null_mut(); - if likely(PyDict_Next(self.dict, &mut self.pos, &mut k, &mut v) != 0) { + if likely(py::dict::next(self.dict, &mut self.pos, &mut k, &mut v)) { let ver_now = dict_version_tag(self.dict); if unlikely(ver_now != self.ver0) { let used_now = dict_used(self.dict); if unlikely(used_now != self.used0) { - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("dictionary changed size during iteration"), ); } else { - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("dictionary keys changed during iteration"), ); @@ -333,16 +314,16 @@ impl DictIterGuard { let mut k: *mut PyObject = ptr::null_mut(); let mut v: *mut PyObject = ptr::null_mut(); - if likely(PyDict_Next(self.dict, &mut self.pos, &mut k, &mut v) != 0) { + if likely(py::dict::next(self.dict, &mut self.pos, &mut k, &mut v)) { if unlikely(self.mutated) { - let cur = PyDict_Size(self.dict); + let cur = py::dict::size(self.dict); let size_changed_now = if likely(cur >= 0) { cur != self.size0 } else { self.size_changed }; - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, if unlikely(size_changed_now) { crate::cstr!("dictionary changed size during iteration") @@ -362,14 +343,14 @@ impl DictIterGuard { } if unlikely(self.mutated) { - let cur = PyDict_Size(self.dict); + let cur = py::dict::size(self.dict); let size_changed_now = if likely(cur >= 0) { cur != self.size0 } else { self.size_changed }; - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, if unlikely(size_changed_now) { crate::cstr!("dictionary changed size during iteration") @@ -392,16 +373,16 @@ impl DictIterGuard { } let mut item: *mut PyObject = ptr::null_mut(); - let result = PyIter_NextItem(self.it, &mut item); + let result = py::object::iter_next_item(self.it, &mut item); if unlikely(result != 1) { self.cleanup(); return result; } - if unlikely(item.is_null() || PyTuple_Check(item) == 0 || PyTuple_GET_SIZE(item) != 2) { + if unlikely(item.is_null() || !py::tuple::check(item) || py::tuple::size(item) != 2) { item.decref_nullable(); - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("dict.items() iterator returned non-pair item"), ); @@ -409,8 +390,8 @@ impl DictIterGuard { return -1; } - *key = PyTuple_GET_ITEM(item, 0).newref(); - *value = PyTuple_GET_ITEM(item, 1).newref(); + *key = py::tuple::get_item(item, 0).newref(); + *value = py::tuple::get_item(item, 1).newref(); item.decref(); 1 } @@ -448,7 +429,7 @@ pub unsafe fn dict_iter_module_cleanup() {} #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] pub unsafe fn dict_iter_module_init() -> i32 { if likely(unsafe { !G_DICT_WATCHER_REGISTERED }) { - let watcher_id = unsafe { PyDict_AddWatcher(Some(copium_dict_watcher_cb)) }; + let watcher_id = unsafe { py::dict::add_watcher(Some(copium_dict_watcher_cb)) }; if unlikely(watcher_id < 0) { return -1; } @@ -465,7 +446,7 @@ pub unsafe fn dict_iter_module_init() -> i32 { #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] pub unsafe fn dict_iter_module_cleanup() { if likely(unsafe { G_DICT_WATCHER_REGISTERED }) { - let _ = unsafe { PyDict_ClearWatcher(G_DICT_WATCHER_ID) }; + unsafe { py::dict::clear_watcher(G_DICT_WATCHER_ID) }; unsafe { G_DICT_WATCHER_REGISTERED = false; G_DICT_WATCHER_ID = -1; diff --git a/src/extra.rs b/src/extra.rs index fe04804..df7a409 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -2,7 +2,8 @@ use pyo3_ffi::*; use std::ptr; use crate::deepcopy; -use crate::types::{PyObjectPtr, PyTypeObjectPtr}; +use crate::py; +use crate::types::{PyObjectPtr, PySeqPtr, PyTypeObjectPtr}; unsafe extern "C" fn py_replicate( _self: *mut PyObject, @@ -12,11 +13,11 @@ unsafe extern "C" fn py_replicate( ) -> *mut PyObject { unsafe { if nargs != 2 { - PyErr_SetString(PyExc_TypeError, crate::cstr!("replicate(obj, n, /)")); + py::err::set_string(PyExc_TypeError, crate::cstr!("replicate(obj, n, /)")); return ptr::null_mut(); } - if !kwnames.is_null() && PyTuple_Size(kwnames) > 0 { - PyErr_SetString( + if !kwnames.is_null() && (kwnames as *mut PyTupleObject).length() > 0 { + py::err::set_string( PyExc_TypeError, crate::cstr!("replicate() does not accept keyword arguments"), ); @@ -24,32 +25,32 @@ unsafe extern "C" fn py_replicate( } let obj = *args; - let n = PyLong_AsLong(*args.add(1)); - if n == -1 && !PyErr_Occurred().is_null() { + let n = py::long::as_i64(*args.add(1)); + if n == -1 && !py::err::occurred().is_null() { return ptr::null_mut(); } if n < 0 { - PyErr_SetString(PyExc_ValueError, crate::cstr!("n must be >= 0")); + py::err::set_string(PyExc_ValueError, crate::cstr!("n must be >= 0")); return ptr::null_mut(); } if n == 0 { - return PyList_New(0); + return py::list::new(0).as_object(); } let type_pointer = obj.class(); if type_pointer.is_atomic_immutable() { - let out = PyList_New(n as Py_ssize_t); + let out = py::list::new(n as Py_ssize_t); if out.is_null() { return ptr::null_mut(); } for i in 0..n as Py_ssize_t { - PyList_SET_ITEM(out, i, obj.newref()); + out.steal_item_unchecked(i, obj.newref()); } - return out; + return out.as_object(); } - let out = PyList_New(n as Py_ssize_t); + let out = py::list::new(n as Py_ssize_t); if out.is_null() { return ptr::null_mut(); } @@ -62,9 +63,9 @@ unsafe extern "C" fn py_replicate( out.decref(); return ptr::null_mut(); } - PyList_SetItem(out, i, copy.into_raw()); + out.steal_item_unchecked(i, copy.into_raw()); } - out + out.as_object() } } @@ -76,14 +77,14 @@ unsafe extern "C" fn py_repeatcall( ) -> *mut PyObject { unsafe { if nargs != 2 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, crate::cstr!("repeatcall(function, size, /)"), ); return ptr::null_mut(); } - if !kwnames.is_null() && PyTuple_Size(kwnames) > 0 { - PyErr_SetString( + if !kwnames.is_null() && (kwnames as *mut PyTupleObject).length() > 0 { + py::err::set_string( PyExc_TypeError, crate::cstr!("repeatcall() takes no keyword arguments"), ); @@ -91,34 +92,34 @@ unsafe extern "C" fn py_repeatcall( } let func = *args; - if PyCallable_Check(func) == 0 { - PyErr_SetString(PyExc_TypeError, crate::cstr!("function must be callable")); + if !func.is_callable() { + py::err::set_string(PyExc_TypeError, crate::cstr!("function must be callable")); return ptr::null_mut(); } - let n = PyLong_AsLong(*args.add(1)); - if n == -1 && !PyErr_Occurred().is_null() { + let n = py::long::as_i64(*args.add(1)); + if n == -1 && !py::err::occurred().is_null() { return ptr::null_mut(); } if n < 0 { - PyErr_SetString(PyExc_ValueError, crate::cstr!("size must be >= 0")); + py::err::set_string(PyExc_ValueError, crate::cstr!("size must be >= 0")); return ptr::null_mut(); } - let out = PyList_New(n as Py_ssize_t); + let out = py::list::new(n as Py_ssize_t); if out.is_null() { return ptr::null_mut(); } for i in 0..n as Py_ssize_t { - let item = PyObject_CallNoArgs(func); + let item = func.call(); if item.is_null() { out.decref(); return ptr::null_mut(); } - PyList_SetItem(out, i, item); + out.steal_item_unchecked(i, item); } - out + out.as_object() } } @@ -139,30 +140,32 @@ static mut EXTRA_MODULE_DEF: PyModuleDef = PyModuleDef { pub unsafe fn create_module(parent: *mut PyObject) -> i32 { unsafe { EXTRA_METHODS[0] = PyMethodDef { - ml_name: crate::cstr!("replicate"), + ml_name: crate::cstr!("replicate").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFastWithKeywords: py_replicate, }, ml_flags: METH_FASTCALL | METH_KEYWORDS, ml_doc: crate::cstr!( "replicate(obj, n, /)\n--\n\nReturns n deep copies of the object in a list." - ), + ) + .as_ptr(), }; EXTRA_METHODS[1] = PyMethodDef { - ml_name: crate::cstr!("repeatcall"), + ml_name: crate::cstr!("repeatcall").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFastWithKeywords: py_repeatcall, }, ml_flags: METH_FASTCALL | METH_KEYWORDS, ml_doc: crate::cstr!( "repeatcall(function, size, /)\n--\n\nCall function repeatedly size times." - ), + ) + .as_ptr(), }; EXTRA_METHODS[2] = PyMethodDef::zeroed(); EXTRA_MODULE_DEF.m_methods = ptr::addr_of_mut!(EXTRA_METHODS).cast::(); - let module = PyModule_Create(std::ptr::addr_of_mut!(EXTRA_MODULE_DEF)); + let module = py::module::create(std::ptr::addr_of_mut!(EXTRA_MODULE_DEF)); if module.is_null() { return -1; } diff --git a/src/fallback.rs b/src/fallback.rs index cc09608..fd0bb6a 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -1,11 +1,11 @@ use pyo3_ffi::*; -use std::ffi::{c_char, CStr}; use std::ptr; -use crate::ffi_ext::PyUnicode_FromFormat; use crate::memo::{MemoCheckpoint, PyMemoObject}; +use crate::py; +use crate::py::frame::PyFramePtr; use crate::state::{OnIncompatible, STATE}; -use crate::types::PyObjectPtr; +use crate::types::{PyObjectPtr, PySeqPtr}; macro_rules! cleanup_traceback_build { ($parts:expr, $traceback_module:expr, $format_exception:expr, $traceback_lines:expr, $empty_string:expr, $caller_string:expr) => {{ @@ -46,16 +46,15 @@ macro_rules! finish_fallback_retry { unsafe fn unicode_to_string(object: *mut PyObject) -> Option { unsafe { - let utf8 = PyUnicode_AsUTF8(object); - if utf8.is_null() { + if object.is_null() || !object.is_unicode() { return None; } - Some(CStr::from_ptr(utf8).to_string_lossy().into_owned()) + Some(py::unicode::as_utf8(object).to_string_lossy().into_owned()) } } unsafe fn new_unicode_from_string(value: &str) -> *mut PyObject { - unsafe { PyUnicode_FromStringAndSize(value.as_ptr() as *const c_char, value.len() as isize) } + unsafe { py::unicode::from_str_and_size(value).as_object() } } unsafe fn build_error_identifier( @@ -66,29 +65,29 @@ unsafe fn build_error_identifier( let mut type_name: *mut PyObject = ptr::null_mut(); let mut message: *mut PyObject = ptr::null_mut(); - if !exception_type.is_null() && PyType_Check(exception_type) != 0 { - type_name = PyObject_GetAttrString(exception_type, crate::cstr!("__name__")); + if !exception_type.is_null() && exception_type.is_type() { + type_name = exception_type.getattr_cstr(crate::cstr!("__name__")); } if type_name.is_null() { - PyErr_Clear(); - type_name = PyUnicode_FromString(crate::cstr!("Exception")); + py::err::clear(); + type_name = py::unicode::from_cstr(crate::cstr!("Exception")).as_object(); if type_name.is_null() { return ptr::null_mut(); } } if !exception_value.is_null() { - message = PyObject_Str(exception_value); + message = exception_value.str_().as_object(); if message.is_null() { - PyErr_Clear(); + py::err::clear(); } } - let result = if !message.is_null() && PyUnicode_GET_LENGTH(message) > 0 { - PyUnicode_FromFormat(crate::cstr!("%U: %U"), type_name, message) + let result = if !message.is_null() && py::unicode::byte_length(message) > 0 { + py::unicode::from_format!(crate::cstr!("%U: %U"), type_name, message).as_object() } else { - PyUnicode_FromFormat(crate::cstr!("%U: "), type_name) + py::unicode::from_format!(crate::cstr!("%U: "), type_name).as_object() }; type_name.decref_nullable(); @@ -103,15 +102,14 @@ unsafe fn error_is_ignored(error_identifier: *mut PyObject) -> bool { return false; } - let ignored_error_count = PyTuple_GET_SIZE(STATE.ignored_errors); + let ignored_error_count = py::tuple::size(STATE.ignored_errors); for index in 0..ignored_error_count { - let suffix = PyTuple_GET_ITEM(STATE.ignored_errors, index); - let matched = PyUnicode_Tailmatch(error_identifier, suffix, 0, PY_SSIZE_T_MAX, 1); - if matched == 1 { + let suffix = py::tuple::get_item(STATE.ignored_errors, index); + if py::unicode::tailmatch(error_identifier, suffix, 0, PY_SSIZE_T_MAX, 1) { return true; } - if matched == -1 { - PyErr_Clear(); + if !py::err::occurred().is_null() { + py::err::clear(); } } @@ -121,7 +119,7 @@ unsafe fn error_is_ignored(error_identifier: *mut PyObject) -> bool { unsafe fn extract_deepcopy_expression(line: *mut PyObject) -> *mut PyObject { unsafe { - if line.is_null() || PyUnicode_Check(line) == 0 { + if line.is_null() || !line.is_unicode() { return ptr::null_mut(); } @@ -196,7 +194,7 @@ unsafe fn extract_deepcopy_expression(line: *mut PyObject) -> *mut PyObject { unsafe fn make_expression_with_memo(expression: *mut PyObject) -> *mut PyObject { unsafe { - if expression.is_null() || PyUnicode_Check(expression) == 0 { + if expression.is_null() || !expression.is_unicode() { return ptr::null_mut(); } @@ -225,7 +223,7 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { let mut line_number_object: *mut PyObject = ptr::null_mut(); let mut line: *mut PyObject = ptr::null_mut(); let mut stripped: *mut PyObject = ptr::null_mut(); - let mut frame = PyEval_GetFrame(); + let mut frame = py::eval::current_frame(); let mut code: *mut PyCodeObject = ptr::null_mut(); let mut filename: *mut PyObject = ptr::null_mut(); let mut name: *mut PyObject = ptr::null_mut(); @@ -237,88 +235,90 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { (frame as *mut PyObject).incref(); while !frame.is_null() { - code = PyFrame_GetCode(frame); + code = frame.code(); if code.is_null() { - let back = PyFrame_GetBack(frame); + let back = frame.back(); (frame as *mut PyObject).decref(); frame = back; continue; } - filename = PyObject_GetAttrString(code as *mut PyObject, crate::cstr!("co_filename")); + filename = (code as *mut PyObject).getattr_cstr(crate::cstr!("co_filename")); if filename.is_null() { - PyErr_Clear(); + py::err::clear(); } - name = PyObject_GetAttrString(code as *mut PyObject, crate::cstr!("co_name")); + name = (code as *mut PyObject).getattr_cstr(crate::cstr!("co_name")); if name.is_null() { - PyErr_Clear(); + py::err::clear(); } if !filename.is_null() && !name.is_null() { - let line_number = PyFrame_GetLineNumber(frame); + let line_number = frame.line_number(); - linecache_module = PyImport_ImportModule(crate::cstr!("linecache")); + linecache_module = py::module::import(crate::cstr!("linecache")); if linecache_module.is_null() { - PyErr_Clear(); + py::err::clear(); break; } - getline = PyObject_GetAttrString(linecache_module, crate::cstr!("getline")); + getline = linecache_module.getattr_cstr(crate::cstr!("getline")); if getline.is_null() { - PyErr_Clear(); + py::err::clear(); break; } - line_number_object = PyLong_FromLong(line_number as _); + line_number_object = py::long::from_i64(line_number as i64).as_object(); if line_number_object.is_null() { break; } - line = PyObject_CallFunctionObjArgs( - getline, - filename, - line_number_object, - ptr::null_mut::(), - ); + let getline_args = py::tuple::new(2); + if getline_args.is_null() { + break; + } + getline_args.steal_item_unchecked(0, filename.newref()); + getline_args.steal_item_unchecked(1, line_number_object.newref()); + line = getline.call_with(getline_args); + getline_args.decref(); if line.is_null() { - PyErr_Clear(); - line = PyUnicode_FromString(crate::cstr!("")); + py::err::clear(); + line = py::unicode::from_cstr(crate::cstr!("")).as_object(); if line.is_null() { break; } } - let strip_method = PyObject_GetAttrString(line, crate::cstr!("strip")); + let strip_method = line.getattr_cstr(crate::cstr!("strip")); if strip_method.is_null() { - PyErr_Clear(); - stripped = PyUnicode_FromString(crate::cstr!("")); + py::err::clear(); + stripped = py::unicode::from_cstr(crate::cstr!("")).as_object(); } else { - stripped = PyObject_CallNoArgs(strip_method); + stripped = strip_method.call(); strip_method.decref(); if stripped.is_null() { - PyErr_Clear(); - stripped = PyUnicode_FromString(crate::cstr!("")); + py::err::clear(); + stripped = py::unicode::from_cstr(crate::cstr!("")).as_object(); } } if stripped.is_null() { break; } - result = PyTuple_New(4); + result = py::tuple::new(4).as_object(); if result.is_null() { break; } filename.incref(); - if PyTuple_SetItem(result, 0, filename) < 0 { + if py::tuple::set_item(result, 0, filename) < 0 { result.decref(); result = ptr::null_mut(); break; } filename = ptr::null_mut(); - if PyTuple_SetItem(result, 1, line_number_object) < 0 { + if py::tuple::set_item(result, 1, line_number_object) < 0 { result.decref(); result = ptr::null_mut(); break; @@ -326,14 +326,14 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { line_number_object = ptr::null_mut(); name.incref(); - if PyTuple_SetItem(result, 2, name) < 0 { + if py::tuple::set_item(result, 2, name) < 0 { result.decref(); result = ptr::null_mut(); break; } name = ptr::null_mut(); - if PyTuple_SetItem(result, 3, stripped) < 0 { + if py::tuple::set_item(result, 3, stripped) < 0 { result.decref(); result = ptr::null_mut(); break; @@ -350,7 +350,7 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { (code as *mut PyObject).decref(); code = ptr::null_mut(); - let back = PyFrame_GetBack(frame); + let back = frame.back(); (frame as *mut PyObject).decref(); frame = back; } @@ -374,7 +374,7 @@ unsafe fn format_combined_traceback( ) -> *mut PyObject { unsafe { let mut parts: *mut PyObject = ptr::null_mut(); - let traceback_module = PyImport_ImportModule(crate::cstr!("traceback")); + let traceback_module = py::module::import(crate::cstr!("traceback")); let mut format_exception: *mut PyObject = ptr::null_mut(); let mut traceback_lines: *mut PyObject = ptr::null_mut(); let mut empty_string: *mut PyObject = ptr::null_mut(); @@ -391,8 +391,7 @@ unsafe fn format_combined_traceback( ); } - format_exception = - PyObject_GetAttrString(traceback_module, crate::cstr!("format_exception")); + format_exception = traceback_module.getattr_cstr(crate::cstr!("format_exception")); if format_exception.is_null() { cleanup_traceback_build!( parts, @@ -404,8 +403,8 @@ unsafe fn format_combined_traceback( ); } - traceback_lines = PyObject_CallOneArg(format_exception, exception_value); - if traceback_lines.is_null() || PyList_Check(traceback_lines) == 0 { + traceback_lines = format_exception.call_one(exception_value); + if traceback_lines.is_null() || !py::list::check(traceback_lines) { cleanup_traceback_build!( parts, traceback_module, @@ -416,7 +415,7 @@ unsafe fn format_combined_traceback( ); } - empty_string = PyUnicode_FromString(crate::cstr!("")); + empty_string = py::unicode::from_cstr(crate::cstr!("")).as_object(); if empty_string.is_null() { cleanup_traceback_build!( parts, @@ -428,7 +427,7 @@ unsafe fn format_combined_traceback( ); } - parts = PyList_New(0); + parts = py::list::new(0).as_object(); if parts.is_null() { cleanup_traceback_build!( parts, @@ -441,38 +440,39 @@ unsafe fn format_combined_traceback( } if !caller_info.is_null() - && PyTuple_Check(caller_info) != 0 - && PyTuple_GET_SIZE(caller_info) == 4 + && py::tuple::check(caller_info) + && py::tuple::size(caller_info) == 4 { - let filename = PyTuple_GET_ITEM(caller_info, 0); - let line_number = PyTuple_GET_ITEM(caller_info, 1); - let function_name = PyTuple_GET_ITEM(caller_info, 2); - let line = PyTuple_GET_ITEM(caller_info, 3); + let filename = py::tuple::get_item(caller_info, 0); + let line_number = py::tuple::get_item(caller_info, 1); + let function_name = py::tuple::get_item(caller_info, 2); + let line = py::tuple::get_item(caller_info, 3); - caller_string = PyUnicode_FromFormat( + caller_string = py::unicode::from_format!( crate::cstr!(" File \"%U\", line %S, in %U\n %U\n"), filename, line_number, function_name, line, - ); + ) + .as_object(); if caller_string.is_null() { - PyErr_Clear(); + py::err::clear(); } } - let traceback_line_count = PyList_GET_SIZE(traceback_lines); + let traceback_line_count = py::list::size(traceback_lines); let mut found_traceback_header = false; let mut caller_inserted = false; for index in 0..traceback_line_count { - let line = PyList_GET_ITEM(traceback_lines, index); + let line = py::list::borrow_item(traceback_lines, index); - if !found_traceback_header && PyUnicode_Check(line) != 0 { + if !found_traceback_header && line.is_unicode() { if let Some(line_text) = unicode_to_string(line) { if line_text.starts_with("Traceback") { found_traceback_header = true; - if PyList_Append(parts, line) < 0 { + if py::list::append(parts, line) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -483,7 +483,7 @@ unsafe fn format_combined_traceback( ); } if !caller_string.is_null() { - if PyList_Append(parts, caller_string) < 0 { + if py::list::append(parts, caller_string) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -500,7 +500,7 @@ unsafe fn format_combined_traceback( } } - if PyList_Append(parts, line) < 0 { + if py::list::append(parts, line) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -513,16 +513,18 @@ unsafe fn format_combined_traceback( } if !found_traceback_header && !caller_string.is_null() && !caller_inserted { - let header = PyUnicode_FromString(crate::cstr!("Traceback (most recent call last):\n")); + let header = + py::unicode::from_cstr(crate::cstr!("Traceback (most recent call last):\n")) + .as_object(); if !header.is_null() { - if PyList_Insert(parts, 0, header) == 0 { - let _ = PyList_Insert(parts, 1, caller_string); + if py::list::insert(parts, 0, header) == 0 { + let _ = py::list::insert(parts, 1, caller_string); } header.decref(); } } - let result = PyUnicode_Join(empty_string, parts); + let result = py::unicode::join(empty_string, parts).as_object(); parts.decref_nullable(); traceback_module.decref_nullable(); @@ -545,15 +547,16 @@ unsafe fn emit_fallback_warning( let mut traceback_string = format_combined_traceback(caller_info, exception_value); let mut full_message: *mut PyObject = ptr::null_mut(); let type_object = object.class() as *mut PyObject; - let mut module_name = PyObject_GetAttrString(type_object, crate::cstr!("__module__")); - let mut type_name = PyObject_GetAttrString(type_object, crate::cstr!("__name__")); + let mut module_name = type_object.getattr_cstr(crate::cstr!("__module__")); + let mut type_name = type_object.getattr_cstr(crate::cstr!("__name__")); let mut deepcopy_qualified_name: *mut PyObject = ptr::null_mut(); let mut deepcopy_expression: *mut PyObject = ptr::null_mut(); let mut deepcopy_expression_with_memo: *mut PyObject = ptr::null_mut(); if traceback_string.is_null() { - PyErr_Clear(); - traceback_string = PyUnicode_FromString(crate::cstr!("[traceback unavailable]\n")); + py::err::clear(); + traceback_string = + py::unicode::from_cstr(crate::cstr!("[traceback unavailable]\n")).as_object(); if traceback_string.is_null() { status = -1; finish_warning_emit!( @@ -571,8 +574,8 @@ unsafe fn emit_fallback_warning( } if module_name.is_null() { - PyErr_Clear(); - module_name = PyUnicode_FromString(crate::cstr!("__main__")); + py::err::clear(); + module_name = py::unicode::from_cstr(crate::cstr!("__main__")).as_object(); if module_name.is_null() { status = -1; finish_warning_emit!( @@ -590,8 +593,8 @@ unsafe fn emit_fallback_warning( } if type_name.is_null() { - PyErr_Clear(); - type_name = PyUnicode_FromString(crate::cstr!("?")); + py::err::clear(); + type_name = py::unicode::from_cstr(crate::cstr!("?")).as_object(); if type_name.is_null() { status = -1; finish_warning_emit!( @@ -609,7 +612,8 @@ unsafe fn emit_fallback_warning( } deepcopy_qualified_name = - PyUnicode_FromFormat(crate::cstr!("%U.%U.__deepcopy__"), module_name, type_name); + py::unicode::from_format!(crate::cstr!("%U.%U.__deepcopy__"), module_name, type_name) + .as_object(); if deepcopy_qualified_name.is_null() { status = -1; finish_warning_emit!( @@ -626,22 +630,23 @@ unsafe fn emit_fallback_warning( } if !caller_info.is_null() - && PyTuple_Check(caller_info) != 0 - && PyTuple_GET_SIZE(caller_info) == 4 + && py::tuple::check(caller_info) + && py::tuple::size(caller_info) == 4 { - let line = PyTuple_GET_ITEM(caller_info, 3); + let line = py::tuple::get_item(caller_info, 3); deepcopy_expression = extract_deepcopy_expression(line); } if !deepcopy_expression.is_null() { deepcopy_expression_with_memo = make_expression_with_memo(deepcopy_expression); if deepcopy_expression_with_memo.is_null() { - PyErr_Clear(); + py::err::clear(); } } if deepcopy_expression.is_null() { - deepcopy_expression = PyUnicode_FromFormat(crate::cstr!("deepcopy(%U())"), type_name); + deepcopy_expression = + py::unicode::from_format!(crate::cstr!("deepcopy(%U())"), type_name).as_object(); if deepcopy_expression.is_null() { status = -1; finish_warning_emit!( @@ -661,9 +666,10 @@ unsafe fn emit_fallback_warning( if deepcopy_expression_with_memo.is_null() { deepcopy_expression_with_memo = make_expression_with_memo(deepcopy_expression); if deepcopy_expression_with_memo.is_null() { - PyErr_Clear(); + py::err::clear(); deepcopy_expression_with_memo = - PyUnicode_FromFormat(crate::cstr!("deepcopy(%U(), memo={})"), type_name); + py::unicode::from_format!(crate::cstr!("deepcopy(%U(), memo={})"), type_name) + .as_object(); if deepcopy_expression_with_memo.is_null() { status = -1; finish_warning_emit!( @@ -681,7 +687,7 @@ unsafe fn emit_fallback_warning( } } - full_message = PyUnicode_FromFormat( + full_message = py::unicode::from_format!( crate::cstr!( "\n\nSeems like 'copium.memo' was rejected inside '%U':\n\n%U\ncopium was able to recover from this error, but this is slow.\n\nFix:\n\n Per Python docs, '%U' should treat memo as an opaque object.\n See: https://docs.python.org/3/library/copy.html#object.__deepcopy__\n\nWorkarounds:\n\n local change %U to %U\n -> copium uses dict memo in this call (recommended)\n\n global `copium.config.apply(memo=\"dict\")` or export COPIUM_USE_DICT_MEMO=1\n -> copium uses dict memo everywhere (~1.3-2x slowdown, still faster than stdlib)\n\n explosive `copium.config.apply(on_incompatible=\"raise\")` or export COPIUM_NO_MEMO_FALLBACK=1\n -> '%U' raises the error above. Useful if you want to handle it yourself.\n\n silent `copium.config.apply(suppress_warnings=[%R])` or export COPIUM_NO_MEMO_FALLBACK_WARNING='%U'\n -> disables this warning for '%U', it stays slow to deepcopy\n" ), @@ -694,7 +700,8 @@ unsafe fn emit_fallback_warning( error_identifier, error_identifier, deepcopy_expression, - ); + ) + .as_object(); if full_message.is_null() { status = -1; finish_warning_emit!( @@ -710,10 +717,10 @@ unsafe fn emit_fallback_warning( ); } - if PyErr_WarnEx(PyExc_UserWarning, PyUnicode_AsUTF8(full_message), 1) < 0 { + if py::err::warn(PyExc_UserWarning, py::unicode::as_utf8(full_message), 1) < 0 { status = -1; } else { - PyErr_Clear(); + py::err::clear(); } finish_warning_emit!( @@ -738,13 +745,12 @@ pub unsafe fn maybe_retry_with_dict_memo( ) -> *mut PyObject { unsafe { let mut result: *mut PyObject = ptr::null_mut(); - let mut exception_type: *mut PyObject = ptr::null_mut(); - let mut exception_value: *mut PyObject = ptr::null_mut(); - let mut exception_traceback: *mut PyObject = ptr::null_mut(); + let (mut exception_type, mut exception_value, mut exception_traceback) = + py::err::fetch(); let mut error_identifier: *mut PyObject = ptr::null_mut(); - if PyErr_ExceptionMatches(PyExc_TypeError) == 0 - && PyErr_ExceptionMatches(PyExc_AssertionError) == 0 + if !py::err::matches_current(PyExc_TypeError) + && !py::err::matches_current(PyExc_AssertionError) { return ptr::null_mut(); } @@ -753,14 +759,7 @@ pub unsafe fn maybe_retry_with_dict_memo( return ptr::null_mut(); } - #[allow(deprecated)] - PyErr_Fetch( - &mut exception_type, - &mut exception_value, - &mut exception_traceback, - ); - #[allow(deprecated)] - PyErr_NormalizeException( + py::err::normalize( &mut exception_type, &mut exception_value, &mut exception_traceback, @@ -780,8 +779,8 @@ pub unsafe fn maybe_retry_with_dict_memo( ); } - let dict_size_before = PyDict_Size(dict_memo); - result = PyObject_CallOneArg(dunder_deepcopy, dict_memo); + let dict_size_before = py::dict::size(dict_memo); + result = dunder_deepcopy.call_one(dict_memo); if result.is_null() { finish_fallback_retry!( result, @@ -812,7 +811,7 @@ pub unsafe fn maybe_retry_with_dict_memo( && !error_is_ignored(error_identifier) { if !exception_traceback.is_null() && !exception_value.is_null() { - let _ = PyException_SetTraceback(exception_value, exception_traceback); + let _ = py::err::set_traceback(exception_value, exception_traceback); } if emit_fallback_warning(exception_value, object, error_identifier) < 0 { diff --git a/src/ffi_ext.rs b/src/ffi_ext.rs index 9b45668..f895e0c 100644 --- a/src/ffi_ext.rs +++ b/src/ffi_ext.rs @@ -1,104 +1,14 @@ #![allow(non_snake_case)] -use core::ffi::c_char; -use libc::c_ulong; -use pyo3_ffi::*; - -use crate::types::PyObjectPtr; - -#[cfg(Py_GIL_DISABLED)] -use core::sync::atomic::Ordering; - -// ── Symbols not (reliably) in pyo3-ffi ────────────────────── - -pub use pyo3_ffi::{_PyWeakref_RefType, PyEllipsis_Type, PyProperty_Type, Py_None}; - -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - pub static mut PyMethod_Type: PyTypeObject; - pub static mut _PyNone_Type: PyTypeObject; - pub static mut _PyNotImplemented_Type: PyTypeObject; -} - -/// PyMethod_Function is a macro in CPython; access via struct layout. -#[repr(C)] -pub struct PyMethodObject { - pub ob_base: PyObject, - pub im_func: *mut PyObject, - pub im_self: *mut PyObject, - pub im_weakreflist: *mut PyObject, - pub vectorcall: vectorcallfunc, -} - -#[inline(always)] -pub unsafe fn PyMethod_GET_FUNCTION(m: *mut PyObject) -> *mut PyObject { - unsafe { (*(m as *mut PyMethodObject)).im_func } -} - -#[inline(always)] -pub unsafe fn PyMethod_GET_SELF(m: *mut PyObject) -> *mut PyObject { - unsafe { (*(m as *mut PyMethodObject)).im_self } -} - -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - pub fn PyMethod_New(func: *mut PyObject, self_: *mut PyObject) -> *mut PyObject; -} - -// ── Variadic FFI not reliably in pyo3-ffi ─────────────────── - -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - pub fn PyErr_Format(exception: *mut PyObject, format: *const c_char, ...) -> *mut PyObject; - pub fn PyUnicode_FromFormat(format: *const c_char, ...) -> *mut PyObject; - pub fn PySequence_Fast(o: *mut PyObject, m: *const c_char) -> *mut PyObject; - #[cfg(Py_3_12)] - pub fn PyFunction_SetVectorcall(callable: *mut PyObject, vc: vectorcallfunc); - pub fn PyVectorcall_Function(callable: *mut PyObject) -> Option; -} - -#[inline(always)] -pub unsafe fn PySequence_Fast_GET_SIZE(o: *mut PyObject) -> Py_ssize_t { - unsafe { Py_SIZE(o) } -} - -#[cfg(Py_GIL_DISABLED)] -#[inline(always)] -pub unsafe fn tp_flags_of(tp: *mut PyTypeObject) -> c_ulong { - unsafe { (*tp).tp_flags.load(Ordering::Relaxed) } -} - -#[cfg(not(Py_GIL_DISABLED))] -#[inline(always)] -pub unsafe fn tp_flags_of(tp: *mut PyTypeObject) -> c_ulong { - unsafe { (*tp).tp_flags } -} - -#[inline(always)] -pub unsafe fn PySequence_Fast_GET_ITEM(o: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { - unsafe { - if (tp_flags_of(o.class()) & (Py_TPFLAGS_LIST_SUBCLASS as c_ulong)) != 0 { - *(*(o as *mut PyListObject)).ob_item.add(i as usize) - } else { - *(*(o as *mut PyTupleObject)) - .ob_item - .as_ptr() - .add(i as usize) - } - } -} - -/// Strips PY_VECTORCALL_ARGUMENTS_OFFSET from nargsf. -#[inline(always)] -pub fn PyVectorcall_NARGS(nargsf: usize) -> Py_ssize_t { - const OFFSET_BIT: usize = 1usize << (usize::BITS - 1); - (nargsf & !OFFSET_BIT) as Py_ssize_t -} - -// ── C string literal helper ───────────────────────────────── - -#[macro_export] -macro_rules! cstr { - ($s:literal) => { - concat!($s, "\0").as_ptr() as *const core::ffi::c_char - }; -} +#![allow(unused_imports)] + +pub use crate::py::ffi::{ + _PyNone_Type, _PyNotImplemented_Type, _PySet_NextEntry, _PyWeakref_RefType, PyEllipsis_Type, + PyErr_Format, PyMethod_GET_FUNCTION, PyMethod_GET_SELF, PyMethod_New, PyMethod_Type, + PyMethodObject, PyObject_CallFunction, PyObject_CallFunctionObjArgs, + PyObject_CallMethodObjArgs, PySequence_Fast, PySequence_Fast_GET_ITEM, + PySequence_Fast_GET_SIZE, PyUnicode_FromFormat, PyVectorcall_Function, PyVectorcall_NARGS, + Py_None, tp_flags_of, +}; + +#[cfg(Py_3_12)] +pub use crate::py::ffi::PyFunction_SetVectorcall; diff --git a/src/lib.rs b/src/lib.rs index d2e292c..e656dba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ #![feature(thread_local)] #![feature(likely_unlikely)] -use core::ffi::{c_char, c_void}; +use core::ffi::{c_void, CStr}; use pyo3_ffi::*; use std::hint::{likely, unlikely}; use std::ptr; -#[macro_use] +mod py; mod ffi_ext; mod about; #[allow(dead_code)] @@ -25,9 +25,9 @@ mod recursion; mod reduce; mod state; mod types; - +use crate::py::seq::PySeqPtr; use crate::memo::PyMemoObject; -use crate::types::{py_dict_new, PyObjectPtr, PyTypeInfo, PyTypeObjectPtr}; +use crate::types::{py_dict_new, PyMapPtr, PyObjectPtr, PyTypeInfo, PyTypeObjectPtr}; use memo::{AnyMemo, DictMemo}; use state::{MemoMode, STATE}; // ══════════════════════════════════════════════════════════════ @@ -50,25 +50,25 @@ pub(crate) unsafe extern "C" fn py_deepcopy( ) -> *mut PyObject { unsafe { let mut obj: *mut PyObject = ptr::null_mut(); - let mut memo_arg: *mut PyObject = Py_None(); + let mut memo_arg: *mut PyObject = py::none(); // ── Fast path: no keyword arguments ───────────────── let kwcount = if kwnames.is_null() { 0 } else { - PyTuple_Size(kwnames) + py::tuple::size(kwnames) }; if likely(kwcount == 0) { if unlikely(nargs < 1) { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("deepcopy() missing 1 required positional argument: 'x'"), ); return ptr::null_mut(); } if unlikely(nargs > 2) { - PyErr_Format( + py::err::format!( PyExc_TypeError, cstr!("deepcopy() takes from 1 to 2 positional arguments but %zd were given"), nargs, @@ -82,7 +82,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( } else { // ── Keyword argument handling ──────────────────── if unlikely(nargs > 2) { - PyErr_Format( + py::err::format!( PyExc_TypeError, cstr!("deepcopy() takes from 1 to 2 positional arguments but %zd were given"), nargs, @@ -99,21 +99,21 @@ pub(crate) unsafe extern "C" fn py_deepcopy( let mut seen_memo_kw = false; for i in 0..kwcount { - let name = PyTuple_GetItem(kwnames, i); + let name = py::tuple::get_item(kwnames, i); let val = *args.offset(nargs + i); - if PyUnicode_CompareWithASCIIString(name, cstr!("x")) == 0 { + if py::unicode::compare_ascii(name, cstr!("x")) == 0 { if !obj.is_null() { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("deepcopy() got multiple values for argument 'x'"), ); return ptr::null_mut(); } obj = val; - } else if PyUnicode_CompareWithASCIIString(name, cstr!("memo")) == 0 { + } else if py::unicode::compare_ascii(name, cstr!("memo")) == 0 { if seen_memo_kw || nargs == 2 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("deepcopy() got multiple values for argument 'memo'"), ); @@ -122,7 +122,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( memo_arg = val; seen_memo_kw = true; } else { - PyErr_Format( + py::err::format!( PyExc_TypeError, cstr!("deepcopy() got an unexpected keyword argument '%U'"), name, @@ -132,7 +132,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( } if unlikely(obj.is_null()) { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("deepcopy() missing 1 required positional argument: 'x'"), ); @@ -141,7 +141,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( } // ── Dispatch based on memo type ───────────────────── - if likely(memo_arg == Py_None()) { + if likely(memo_arg == py::none()) { let tp = obj.class(); if tp.is_atomic_immutable() { return obj.newref(); @@ -202,14 +202,14 @@ unsafe extern "C" fn py_replace( ) -> *mut PyObject { unsafe { if nargs == 0 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("replace() missing 1 required positional argument: 'obj'"), ); return ptr::null_mut(); } if nargs > 1 { - PyErr_Format( + py::err::format!( PyExc_TypeError, cstr!("replace() takes 1 positional argument but %zd were given"), nargs, @@ -220,10 +220,10 @@ unsafe extern "C" fn py_replace( let obj = *args; let type_pointer = obj.class(); let class_object = type_pointer as *mut PyObject; - let func = PyObject_GetAttrString(class_object, cstr!("__replace__")); + let func = class_object.getattr_cstr(cstr!("__replace__")); if func.is_null() { - PyErr_Clear(); - PyErr_Format( + py::err::clear(); + py::err::format!( PyExc_TypeError, cstr!("replace() does not support %.200s objects"), (*type_pointer).tp_name, @@ -231,34 +231,34 @@ unsafe extern "C" fn py_replace( return ptr::null_mut(); } - let posargs = PyTuple_New(1); + let posargs = py::tuple::new(1); if posargs.is_null() { func.decref(); return ptr::null_mut(); } - PyTuple_SetItem(posargs, 0, obj.newref()); + posargs.steal_item_unchecked(0, obj.newref()); let mut kwargs: *mut PyObject = ptr::null_mut(); let kwcount = if kwnames.is_null() { 0 } else { - PyTuple_Size(kwnames) + py::tuple::size(kwnames) }; if kwcount > 0 { - kwargs = PyDict_New(); + kwargs = py::dict::new().as_object(); if kwargs.is_null() { func.decref(); posargs.decref(); return ptr::null_mut(); } for i in 0..kwcount { - let key = PyTuple_GetItem(kwnames, i); + let key = py::tuple::get_item(kwnames, i); let val = *args.offset(nargs + i); - PyDict_SetItem(kwargs, key, val); + (kwargs as *mut PyDictObject).set_item(key, val); } } - let out = PyObject_Call(func, posargs, kwargs); + let out = func.call_with_kwargs(posargs, kwargs); func.decref(); posargs.decref(); kwargs.decref_nullable(); @@ -277,34 +277,35 @@ unsafe fn init_methods() { let mut i = 0usize; MAIN_METHODS[i] = PyMethodDef { - ml_name: cstr!("copy"), + ml_name: cstr!("copy").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: py_copy, }, ml_flags: METH_O, - ml_doc: cstr!("copy(obj, /)\n--\n\nReturn a shallow copy of obj."), + ml_doc: cstr!("copy(obj, /)\n--\n\nReturn a shallow copy of obj.").as_ptr(), }; i += 1; MAIN_METHODS[i] = PyMethodDef { - ml_name: cstr!("deepcopy"), + ml_name: cstr!("deepcopy").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFastWithKeywords: py_deepcopy, }, ml_flags: METH_FASTCALL | METH_KEYWORDS, - ml_doc: cstr!("deepcopy(x, memo=None, /)\n--\n\nReturn a deep copy of obj."), + ml_doc: cstr!("deepcopy(x, memo=None, /)\n--\n\nReturn a deep copy of obj.").as_ptr(), }; i += 1; #[cfg(Py_3_13)] { MAIN_METHODS[i] = PyMethodDef { - ml_name: cstr!("replace"), + ml_name: cstr!("replace").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFastWithKeywords: py_replace, }, ml_flags: METH_FASTCALL | METH_KEYWORDS, - ml_doc: cstr!("replace(obj, /, **changes)\n--\n\nReplace fields on a copy."), + ml_doc: cstr!("replace(obj, /, **changes)\n--\n\nReplace fields on a copy.") + .as_ptr(), }; i += 1; } @@ -331,12 +332,12 @@ unsafe extern "C" fn orcopium_exec(module: *mut PyObject) -> i32 { return -1; } - if PyModule_AddObject(module, cstr!("Error"), py_obj!("copy.Error").newref()) < 0 { + if py::module::add_object(module, cstr!("Error"), py_obj!("copy.Error").newref()) < 0 { return -1; } let memo_type = ptr::addr_of_mut!(memo::Memo_Type) as *mut PyObject; - if PyModule_AddObject(module, cstr!("memo"), memo_type.newref()) < 0 { + if py::module::add_object(module, cstr!("memo"), memo_type.newref()) < 0 { return -1; } @@ -350,28 +351,28 @@ unsafe extern "C" fn orcopium_exec(module: *mut PyObject) -> i32 { return -1; } - let config_module = PyObject_GetAttrString(module, cstr!("config")); + let config_module = module.getattr_cstr(cstr!("config")); if config_module.is_null() { return -1; } - let configure = PyObject_GetAttrString(config_module, cstr!("apply")); + let configure = config_module.getattr_cstr(cstr!("apply")); if configure.is_null() { config_module.decref(); return -1; } - if PyModule_AddObject(module, cstr!("configure"), configure) < 0 { + if py::module::add_object(module, cstr!("configure"), configure) < 0 { config_module.decref(); configure.decref(); return -1; } - let get_config = PyObject_GetAttrString(config_module, cstr!("get")); + let get_config = config_module.getattr_cstr(cstr!("get")); config_module.decref(); if get_config.is_null() { return -1; } - if PyModule_AddObject(module, cstr!("get_config"), get_config) < 0 { + if py::module::add_object(module, cstr!("get_config"), get_config) < 0 { get_config.decref(); return -1; } @@ -436,31 +437,29 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef { /// Register a submodule on the parent and in sys.modules. pub unsafe fn add_submodule( parent: *mut PyObject, - name: *const c_char, + name: &CStr, submodule: *mut PyObject, ) -> i32 { unsafe { - // Always register as "copium." regardless of internal module nesting - let canonical = PyUnicode_FromFormat(cstr!("copium.%s"), name); + let canonical = py::unicode::from_format!(cstr!("copium.%s"), name.as_ptr()); if canonical.is_null() { submodule.decref(); return -1; } - PyObject_SetAttrString(submodule, cstr!("__name__"), canonical); + submodule.set_attr_cstr(cstr!("__name__"), canonical); - let sys_modules = PyImport_GetModuleDict(); + let sys_modules = py::module::get_module_dict(); if !sys_modules.is_null() { - PyDict_SetItem(sys_modules, canonical, submodule); + sys_modules.set_item(canonical, submodule); } - // Also register under the actual parent name (e.g. orcopium.orcopium.extra) - let parent_name = PyModule_GetNameObject(parent); + let parent_name = py::module::get_name(parent); if !parent_name.is_null() { - let full_name = PyUnicode_FromFormat(cstr!("%U.%s"), parent_name, name); + let full_name = py::unicode::from_format!(cstr!("%U.%s"), parent_name, name.as_ptr()); if !full_name.is_null() { if !sys_modules.is_null() { - PyDict_SetItem(sys_modules, full_name, submodule); + sys_modules.set_item(full_name, submodule); } full_name.decref(); } @@ -468,7 +467,7 @@ pub unsafe fn add_submodule( } canonical.decref(); - if PyModule_AddObject(parent, name, submodule) < 0 { + if py::module::add_object(parent, name, submodule) < 0 { submodule.decref(); return -1; } @@ -483,6 +482,6 @@ pub unsafe extern "C" fn PyInit_copium() -> *mut PyObject { init_methods(); MODULE_DEF.m_methods = ptr::addr_of_mut!(MAIN_METHODS).cast::(); MODULE_DEF.m_slots = ptr::addr_of_mut!(MODULE_SLOTS).cast::(); - PyModuleDef_Init(std::ptr::addr_of_mut!(MODULE_DEF)) + py::module::def_init(std::ptr::addr_of_mut!(MODULE_DEF)) } } diff --git a/src/memo/any.rs b/src/memo/any.rs index 38876de..2500663 100644 --- a/src/memo/any.rs +++ b/src/memo/any.rs @@ -1,9 +1,8 @@ use pyo3_ffi::*; -use std::ffi::c_void; use std::ptr; use super::Memo; -use crate::types::{py_list_new, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; +use crate::types::{py_list_new, py_tuple_new, PyMutSeqPtr, PyObjectPtr, PySeqPtr, PyTypeInfo}; use crate::{py_cache, py_eval, py_str}; pub struct AnyMemo { @@ -26,26 +25,33 @@ impl AnyMemo { } let sentinel = py_cache!(py_eval!("object()")); - let pykey = PyLong_FromVoidPtr(self.object as *mut c_void); + let pykey = self.object.id(); if pykey.is_null() { return -1; } - let existing = PyObject_CallMethodObjArgs( - self.object, - py_str!("get"), - pykey, - sentinel, - ptr::null_mut::(), - ); - if existing.is_null() { + let getter = self.object.getattr(py_str!("get")); + if getter.is_null() { + pykey.decref(); + return -1; + } + let args = py_tuple_new(2); + if args.is_null() { + getter.decref(); pykey.decref(); return -1; } + args.steal_item_unchecked(0, pykey.as_object()); + args.steal_item_unchecked(1, sentinel.newref()); + let existing = getter.call_with(args); + getter.decref(); + args.decref(); + if existing.is_null() { + return -1; + } if existing != sentinel { self.keepalive = existing as *mut PyListObject; - pykey.decref(); return 0; } @@ -77,19 +83,27 @@ impl Memo for AnyMemo { unsafe fn recall(&mut self, object: *mut T) -> ((), *mut T) { unsafe { let sentinel = py_cache!(py_eval!("object()")); - let pykey = PyLong_FromVoidPtr(object as *mut c_void); + let pykey = object.id(); if pykey.is_null() { return ((), ptr::null_mut()); } - let found = PyObject_CallMethodObjArgs( - self.object, - py_str!("get"), - pykey, - sentinel, - ptr::null_mut::(), - ); - pykey.decref(); + let getter = self.object.getattr(py_str!("get")); + if getter.is_null() { + pykey.decref(); + return ((), ptr::null_mut()); + } + let args = py_tuple_new(2); + if args.is_null() { + getter.decref(); + pykey.decref(); + return ((), ptr::null_mut()); + } + args.steal_item_unchecked(0, pykey.as_object()); + args.steal_item_unchecked(1, sentinel.newref()); + let found = getter.call_with(args); + getter.decref(); + args.decref(); if found.is_null() { return ((), ptr::null_mut()); @@ -110,7 +124,7 @@ impl Memo for AnyMemo { _probe: &Self::Probe, ) -> i32 { unsafe { - let pykey = PyLong_FromVoidPtr(original as *mut c_void); + let pykey = original.id(); if pykey.is_null() { return -1; } diff --git a/src/memo/dict.rs b/src/memo/dict.rs index 2b52848..2b3a6be 100644 --- a/src/memo/dict.rs +++ b/src/memo/dict.rs @@ -1,8 +1,8 @@ use pyo3_ffi::*; -use std::ffi::c_void; use std::ptr; use super::Memo; +use crate::py; use crate::types::{py_list_new, PyMapPtr, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; pub struct DictMemo { @@ -25,7 +25,7 @@ impl DictMemo { return 0; } - let pykey = PyLong_FromVoidPtr(self.dict as *mut c_void); + let pykey = self.dict.id(); if pykey.is_null() { return -1; } @@ -37,7 +37,7 @@ impl DictMemo { pykey.decref(); return 0; } - if !PyErr_Occurred().is_null() { + if !py::err::occurred().is_null() { pykey.decref(); return -1; } @@ -68,7 +68,7 @@ impl Memo for DictMemo { #[inline(always)] unsafe fn recall(&mut self, object: *mut T) -> ((), *mut T) { unsafe { - let pykey = PyLong_FromVoidPtr(object as *mut c_void); + let pykey = object.id(); if pykey.is_null() { return ((), ptr::null_mut()); } diff --git a/src/memo/native.rs b/src/memo/native.rs index d959dca..0bf327c 100644 --- a/src/memo/native.rs +++ b/src/memo/native.rs @@ -1,5 +1,6 @@ use super::{KeepaliveVec, Memo, MemoCheckpoint, MemoTable, UndoLog}; use crate::memo::table::{hash_pointer, TOMBSTONE}; +use crate::py; use crate::types::{PyMapPtr, PyObjectPtr, PyTypeInfo}; use pyo3_ffi::*; use std::ffi::c_void; @@ -75,7 +76,7 @@ impl PyMemoObject { for i in 0..self.table.size { let entry = &*self.table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let pykey = PyLong_FromVoidPtr(entry.key as *mut c_void); + let pykey = py::long::from_ptr(entry.key as *mut c_void).as_object(); if pykey.is_null() { dict.decref(); return ptr::null_mut(); @@ -107,16 +108,16 @@ impl PyMemoObject { let mut value: *mut PyObject = ptr::null_mut(); let mut idx: Py_ssize_t = 0; - while dict_typed.dict_next(&mut pos, &mut py_key, &mut value) != 0 { + while dict_typed.dict_next(&mut pos, &mut py_key, &mut value) { idx += 1; if idx <= orig_size { continue; } - if PyLong_Check(py_key) == 0 { + if !py::long::check(py_key) { continue; } - let key = PyLong_AsVoidPtr(py_key) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(py_key) as usize; + if key == 0 && !py::err::occurred().is_null() { return -1; } let hash = hash_pointer(key); diff --git a/src/memo/pytype.rs b/src/memo/pytype.rs index 08eecc2..7f6c66d 100644 --- a/src/memo/pytype.rs +++ b/src/memo/pytype.rs @@ -3,9 +3,10 @@ use std::ffi::c_void; use std::ptr; use super::native::PyMemoObject; -use crate::ffi_ext::PyUnicode_FromFormat; +use crate::cstr; use crate::memo::table::{hash_pointer, TOMBSTONE}; -use crate::types::{PyObjectPtr, PyObjectSlotPtr}; +use crate::py; +use crate::types::{PyMapPtr, PyObjectPtr, PyObjectSlotPtr}; #[allow(non_upper_case_globals)] pub static mut Memo_Type: PyTypeObject = unsafe { std::mem::zeroed() }; @@ -29,13 +30,13 @@ static mut KEEPALIVE_LIST_METHODS_TABLE: [PyMethodDef; 3] = unsafe { std::mem::z unsafe fn keepalive_list_new(owner: *mut PyMemoObject) -> *mut PyObject { unsafe { - let obj = PyObject_GC_New::(ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE)); + let obj = py::gc::new::(ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE)); if obj.is_null() { return ptr::null_mut(); } (*obj).owner = owner; (owner as *mut PyObject).incref(); - PyObject_GC_Track(obj as *mut c_void); + py::gc::track(obj as *mut c_void); obj as *mut PyObject } } @@ -59,9 +60,9 @@ pub(super) unsafe fn memo_keepalive_proxy(self_: *mut PyMemoObject) -> *mut PyOb unsafe extern "C" fn keepalive_list_dealloc(obj: *mut PyObject) { unsafe { let self_ = obj as *mut PyKeepaliveListObject; - PyObject_GC_UnTrack(self_ as *mut c_void); + py::gc::untrack(self_ as *mut c_void); ((*self_).owner as *mut PyObject).decref_nullable(); - PyObject_GC_Del(self_ as *mut c_void); + py::gc::delete(self_ as *mut c_void); } } @@ -104,7 +105,7 @@ unsafe extern "C" fn keepalive_list_getitem( unsafe { let self_ = obj as *mut PyKeepaliveListObject; if (*self_).owner.is_null() { - PyErr_SetString(PyExc_SystemError, cstr!("keepalive has no owner")); + py::err::set_string(PyExc_SystemError, cstr!("keepalive has no owner")); return ptr::null_mut(); } @@ -115,7 +116,7 @@ unsafe extern "C" fn keepalive_list_getitem( i += n; } if i < 0 || i >= n { - PyErr_SetString(PyExc_IndexError, cstr!("index out of range")); + py::err::set_string(PyExc_IndexError, cstr!("index out of range")); return ptr::null_mut(); } @@ -127,23 +128,23 @@ unsafe extern "C" fn keepalive_list_iter(obj: *mut PyObject) -> *mut PyObject { unsafe { let self_ = obj as *mut PyKeepaliveListObject; if (*self_).owner.is_null() { - PyErr_SetString(PyExc_SystemError, cstr!("keepalive has no owner")); + py::err::set_string(PyExc_SystemError, cstr!("keepalive has no owner")); return ptr::null_mut(); } let items = &(*(*self_).owner).keepalive.items; let n = items.len() as Py_ssize_t; - let list = PyList_New(n); + let list = py::list::new(n); if list.is_null() { return ptr::null_mut(); } for (i, &item) in items.iter().enumerate() { item.incref(); - PyList_SetItem(list, i as Py_ssize_t, item); + py::list::set_item(list, i as Py_ssize_t, item); } - let it = PyObject_GetIter(list); + let it = list.get_iter(); list.decref(); it } @@ -151,16 +152,16 @@ unsafe extern "C" fn keepalive_list_iter(obj: *mut PyObject) -> *mut PyObject { unsafe extern "C" fn keepalive_list_repr(obj: *mut PyObject) -> *mut PyObject { unsafe { - let list = PySequence_List(obj); + let list = py::seq::to_list(obj).as_object(); if list.is_null() { return ptr::null_mut(); } - let inner = PyObject_Repr(list); + let inner = list.repr().as_object(); list.decref(); if inner.is_null() { return ptr::null_mut(); } - let wrapped = PyUnicode_FromFormat(cstr!("keepalive(%U)"), inner); + let wrapped = py::unicode::from_format!(cstr!("keepalive(%U)"), inner).as_object(); inner.decref(); wrapped } @@ -173,11 +174,11 @@ unsafe extern "C" fn keepalive_list_append( unsafe { let self_ = obj as *mut PyKeepaliveListObject; if (*self_).owner.is_null() { - PyErr_SetString(PyExc_SystemError, cstr!("keepalive has no owner")); + py::err::set_string(PyExc_SystemError, cstr!("keepalive has no owner")); return ptr::null_mut(); } (*(*self_).owner).keepalive.append(arg); - Py_None().newref() + py::none().newref() } } @@ -188,18 +189,18 @@ unsafe extern "C" fn keepalive_list_clear_py( unsafe { let self_ = obj as *mut PyKeepaliveListObject; if (*self_).owner.is_null() { - PyErr_SetString(PyExc_SystemError, cstr!("keepalive has no owner")); + py::err::set_string(PyExc_SystemError, cstr!("keepalive has no owner")); return ptr::null_mut(); } (*(*self_).owner).keepalive.clear(); - Py_None().newref() + py::none().newref() } } unsafe fn init_keepalive_methods() { unsafe { KEEPALIVE_LIST_METHODS_TABLE[0] = PyMethodDef { - ml_name: cstr!("append"), + ml_name: cstr!("append").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: keepalive_list_append, }, @@ -207,7 +208,7 @@ unsafe fn init_keepalive_methods() { ml_doc: ptr::null(), }; KEEPALIVE_LIST_METHODS_TABLE[1] = PyMethodDef { - ml_name: cstr!("clear"), + ml_name: cstr!("clear").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: keepalive_list_clear_py, }, @@ -230,7 +231,7 @@ unsafe fn init_keepalive_methods() { }; let tp = ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE); - (*tp).tp_name = cstr!("copium.keepalive"); + (*tp).tp_name = cstr!("copium.keepalive").as_ptr(); (*tp).tp_basicsize = size_of::() as Py_ssize_t; (*tp).tp_dealloc = Some(keepalive_list_dealloc); (*tp).tp_repr = Some(keepalive_list_repr); @@ -260,12 +261,12 @@ unsafe fn init_keepalive_methods() { unsafe extern "C" fn memo_dealloc(obj: *mut PyObject) { unsafe { let self_ = obj as *mut PyMemoObject; - PyObject_GC_UnTrack(self_ as *mut c_void); - if PyObject_CallFinalizerFromDealloc(obj) != 0 { + py::gc::untrack(self_ as *mut c_void); + if py::gc::call_finalizer_from_dealloc(obj) != 0 { return; } ptr::drop_in_place(self_); - PyObject_GC_Del(self_ as *mut c_void); + py::gc::delete(self_ as *mut c_void); } } @@ -348,7 +349,7 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { } if !(*self_).keepalive.items.is_empty() { - let py_key = PyLong_FromVoidPtr(self_ as *mut c_void); + let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); if py_key.is_null() { dict.decref(); return ptr::null_mut(); @@ -361,7 +362,7 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { return ptr::null_mut(); } - if PyDict_SetItem(dict, py_key, proxy) < 0 { + if (dict as *mut PyDictObject).set_item(py_key, proxy) < 0 { proxy.decref(); py_key.decref(); dict.decref(); @@ -372,13 +373,13 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { py_key.decref(); } - let inner = PyObject_Repr(dict); + let inner = dict.repr().as_object(); dict.decref(); if inner.is_null() { return ptr::null_mut(); } - let wrapped = PyUnicode_FromFormat(cstr!("memo(%U)"), inner); + let wrapped = py::unicode::from_format!(cstr!("memo(%U)"), inner).as_object(); inner.decref(); wrapped } @@ -389,7 +390,7 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { let self_ = obj as *mut PyMemoObject; let table = &(*self_).table; - let list = PyList_New(0); + let list = py::list::new(0).as_object(); if list.is_null() { return ptr::null_mut(); } @@ -398,8 +399,8 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { for i in 0..table.size { let entry = &*table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = PyLong_FromVoidPtr(entry.key as *mut c_void); - if py_key.is_null() || PyList_Append(list, py_key) < 0 { + let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); + if py_key.is_null() || py::list::append(list, py_key) < 0 { py_key.decref_nullable(); list.decref(); return ptr::null_mut(); @@ -410,8 +411,8 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { } if !(*self_).keepalive.items.is_empty() { - let py_key = PyLong_FromVoidPtr(self_ as *mut c_void); - if py_key.is_null() || PyList_Append(list, py_key) < 0 { + let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); + if py_key.is_null() || py::list::append(list, py_key) < 0 { py_key.decref_nullable(); list.decref(); return ptr::null_mut(); @@ -419,7 +420,7 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { py_key.decref(); } - let it = PyObject_GetIter(list); + let it = list.get_iter(); list.decref(); it } @@ -444,19 +445,19 @@ unsafe extern "C" fn memo_mp_subscript(obj: *mut PyObject, pykey: *mut PyObject) unsafe { let self_ = obj as *mut PyMemoObject; - if PyLong_Check(pykey) == 0 { - PyErr_SetObject(PyExc_KeyError, pykey); + if !py::long::check(pykey) { + py::err::set(PyExc_KeyError, pykey); return ptr::null_mut(); } - let key = PyLong_AsVoidPtr(pykey) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(pykey) as usize; + if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } if key == self_ as usize { if (*self_).keepalive.items.is_empty() { - PyErr_SetObject(PyExc_KeyError, pykey); + py::err::set(PyExc_KeyError, pykey); return ptr::null_mut(); } return memo_keepalive_proxy(self_); @@ -464,7 +465,7 @@ unsafe extern "C" fn memo_mp_subscript(obj: *mut PyObject, pykey: *mut PyObject) let found = (*self_).table.lookup_h(key, hash_pointer(key)); if found.is_null() { - PyErr_SetObject(PyExc_KeyError, pykey); + py::err::set(PyExc_KeyError, pykey); return ptr::null_mut(); } @@ -480,27 +481,27 @@ unsafe extern "C" fn memo_mp_ass_subscript( unsafe { let self_ = obj as *mut PyMemoObject; - if PyLong_Check(pykey) == 0 { - PyErr_SetString(PyExc_KeyError, cstr!("keys must be integers")); + if !py::long::check(pykey) { + py::err::set_string(PyExc_KeyError, cstr!("keys must be integers")); return -1; } - let key = PyLong_AsVoidPtr(pykey) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(pykey) as usize; + if key == 0 && !py::err::occurred().is_null() { return -1; } if key == self_ as usize { if value.is_null() { if (*self_).keepalive.items.is_empty() { - PyErr_SetObject(PyExc_KeyError, pykey); + py::err::set(PyExc_KeyError, pykey); return -1; } (*self_).keepalive.clear(); return 0; } - let it = PyObject_GetIter(value); + let it = value.get_iter(); if it.is_null() { return -1; } @@ -508,7 +509,7 @@ unsafe extern "C" fn memo_mp_ass_subscript( (*self_).keepalive.clear(); loop { - let item = PyIter_Next(it); + let item = it.iter_next(); if item.is_null() { break; } @@ -518,7 +519,7 @@ unsafe extern "C" fn memo_mp_ass_subscript( it.decref(); - if !PyErr_Occurred().is_null() { + if !py::err::occurred().is_null() { return -1; } @@ -528,7 +529,7 @@ unsafe extern "C" fn memo_mp_ass_subscript( if value.is_null() { let hash = hash_pointer(key); if (*self_).table.remove_h(key, hash) < 0 { - PyErr_SetObject(PyExc_KeyError, pykey); + py::err::set(PyExc_KeyError, pykey); return -1; } return 0; @@ -546,13 +547,13 @@ unsafe extern "C" fn memo_sq_contains(obj: *mut PyObject, pykey: *mut PyObject) unsafe { let self_ = obj as *mut PyMemoObject; - if PyLong_Check(pykey) == 0 { - PyErr_SetString(PyExc_TypeError, cstr!("keys must be integers")); + if !py::long::check(pykey) { + py::err::set_string(PyExc_TypeError, cstr!("keys must be integers")); return -1; } - let key = PyLong_AsVoidPtr(pykey) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(pykey) as usize; + if key == 0 && !py::err::occurred().is_null() { return -1; } @@ -582,7 +583,7 @@ unsafe extern "C" fn memo_py_clear(obj: *mut PyObject, _: *mut PyObject) -> *mut (*self_).dict_proxy.decref(); (*self_).dict_proxy = ptr::null_mut(); } - Py_None().newref() + py::none().newref() } } @@ -593,20 +594,20 @@ unsafe extern "C" fn memo_py_get( ) -> *mut PyObject { unsafe { if nargs < 1 || nargs > 2 { - PyErr_SetString(PyExc_TypeError, cstr!("get expected 1 or 2 arguments")); + py::err::set_string(PyExc_TypeError, cstr!("get expected 1 or 2 arguments")); return ptr::null_mut(); } let self_ = obj as *mut PyMemoObject; let pykey = *args; - if PyLong_Check(pykey) == 0 { - PyErr_SetString(PyExc_TypeError, cstr!("keys must be integers")); + if !py::long::check(pykey) { + py::err::set_string(PyExc_TypeError, cstr!("keys must be integers")); return ptr::null_mut(); } - let key = PyLong_AsVoidPtr(pykey) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(pykey) as usize; + if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -619,7 +620,7 @@ unsafe extern "C" fn memo_py_get( return (*args.add(1)).newref(); } - Py_None().newref() + py::none().newref() } } @@ -629,7 +630,7 @@ unsafe extern "C" fn memo_py_contains(obj: *mut PyObject, pykey: *mut PyObject) if result < 0 { return ptr::null_mut(); } - PyBool_FromLong(result as _) + py::boolean::from_bool(result != 0) } } @@ -641,7 +642,7 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu n += 1; } - let list = PyList_New(n); + let list = py::list::new(n).as_object(); if list.is_null() { return ptr::null_mut(); } @@ -652,7 +653,7 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { entry.value.incref(); - PyList_SetItem(list, idx, entry.value); + py::list::set_item(list, idx, entry.value); idx += 1; } } @@ -664,7 +665,7 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu list.decref(); return ptr::null_mut(); } - PyList_SetItem(list, idx, proxy); + py::list::set_item(list, idx, proxy); } list @@ -679,7 +680,7 @@ unsafe extern "C" fn memo_py_keys(obj: *mut PyObject, _: *mut PyObject) -> *mut n += 1; } - let list = PyList_New(n); + let list = py::list::new(n).as_object(); if list.is_null() { return ptr::null_mut(); } @@ -689,24 +690,24 @@ unsafe extern "C" fn memo_py_keys(obj: *mut PyObject, _: *mut PyObject) -> *mut for i in 0..(*self_).table.size { let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = PyLong_FromVoidPtr(entry.key as *mut c_void); + let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - PyList_SetItem(list, idx, py_key); + py::list::set_item(list, idx, py_key); idx += 1; } } } if !(*self_).keepalive.items.is_empty() { - let py_key = PyLong_FromVoidPtr(self_ as *mut c_void); + let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - PyList_SetItem(list, idx, py_key); + py::list::set_item(list, idx, py_key); } list @@ -721,7 +722,7 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut n += 1; } - let list = PyList_New(n); + let list = py::list::new(n).as_object(); if list.is_null() { return ptr::null_mut(); } @@ -731,28 +732,28 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut for i in 0..(*self_).table.size { let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = PyLong_FromVoidPtr(entry.key as *mut c_void); + let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - let pair = PyTuple_New(2); + let pair = py::tuple::new(2).as_object(); if pair.is_null() { py_key.decref(); list.decref(); return ptr::null_mut(); } - PyTuple_SetItem(pair, 0, py_key); + py::tuple::set_item(pair, 0, py_key); entry.value.incref(); - PyTuple_SetItem(pair, 1, entry.value); - PyList_SetItem(list, idx, pair); + py::tuple::set_item(pair, 1, entry.value); + py::list::set_item(list, idx, pair); idx += 1; } } } if !(*self_).keepalive.items.is_empty() { - let py_key = PyLong_FromVoidPtr(self_ as *mut c_void); + let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); if py_key.is_null() { list.decref(); return ptr::null_mut(); @@ -763,16 +764,16 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut list.decref(); return ptr::null_mut(); } - let pair = PyTuple_New(2); + let pair = py::tuple::new(2).as_object(); if pair.is_null() { py_key.decref(); proxy.decref(); list.decref(); return ptr::null_mut(); } - PyTuple_SetItem(pair, 0, py_key); - PyTuple_SetItem(pair, 1, proxy); - PyList_SetItem(list, idx, pair); + py::tuple::set_item(pair, 0, py_key); + py::tuple::set_item(pair, 1, proxy); + py::list::set_item(list, idx, pair); } list @@ -786,7 +787,7 @@ unsafe extern "C" fn memo_py_setdefault( ) -> *mut PyObject { unsafe { if nargs < 1 || nargs > 2 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, cstr!("setdefault expected 1 or 2 arguments"), ); @@ -796,13 +797,13 @@ unsafe extern "C" fn memo_py_setdefault( let self_ = obj as *mut PyMemoObject; let pykey = *args; - if PyLong_Check(pykey) == 0 { - PyErr_SetString(PyExc_KeyError, cstr!("keys must be integers")); + if !py::long::check(pykey) { + py::err::set_string(PyExc_KeyError, cstr!("keys must be integers")); return ptr::null_mut(); } - let key = PyLong_AsVoidPtr(pykey) as usize; - if key == 0 && !PyErr_Occurred().is_null() { + let key = py::long::as_ptr(pykey) as usize; + if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -815,7 +816,7 @@ unsafe extern "C" fn memo_py_setdefault( return found.newref(); } - let default_value = if nargs == 2 { *args.add(1) } else { Py_None() }; + let default_value = if nargs == 2 { *args.add(1) } else { py::none() }; if (*self_).insert_logged(key, default_value, hash_pointer(key)) < 0 { return ptr::null_mut(); } @@ -831,7 +832,7 @@ unsafe extern "C" fn memo_py_setdefault( unsafe fn init_memo_methods() { unsafe { MEMO_METHODS_TABLE[0] = PyMethodDef { - ml_name: cstr!("clear"), + ml_name: cstr!("clear").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_clear, }, @@ -839,7 +840,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[1] = PyMethodDef { - ml_name: cstr!("get"), + ml_name: cstr!("get").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFast: memo_py_get, }, @@ -847,7 +848,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[2] = PyMethodDef { - ml_name: cstr!("values"), + ml_name: cstr!("values").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_values, }, @@ -855,7 +856,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[3] = PyMethodDef { - ml_name: cstr!("keys"), + ml_name: cstr!("keys").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_keys, }, @@ -863,7 +864,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[4] = PyMethodDef { - ml_name: cstr!("items"), + ml_name: cstr!("items").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_items, }, @@ -871,7 +872,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[5] = PyMethodDef { - ml_name: cstr!("setdefault"), + ml_name: cstr!("setdefault").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunctionFast: memo_py_setdefault, }, @@ -879,7 +880,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[6] = PyMethodDef { - ml_name: cstr!("__contains__"), + ml_name: cstr!("__contains__").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_contains, }, @@ -887,7 +888,7 @@ unsafe fn init_memo_methods() { ml_doc: ptr::null(), }; MEMO_METHODS_TABLE[7] = PyMethodDef { - ml_name: cstr!("__del__"), + ml_name: cstr!("__del__").as_ptr(), ml_meth: PyMethodDefPointer { PyCFunction: memo_py_clear, }, @@ -901,7 +902,7 @@ unsafe fn init_memo_methods() { pub unsafe fn memo_ready_type() -> i32 { unsafe { init_keepalive_methods(); - if PyType_Ready(ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE)) < 0 { + if py::type_object::ready(ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE)) < 0 { return -1; } @@ -916,7 +917,7 @@ pub unsafe fn memo_ready_type() -> i32 { MEMO_SEQUENCE.sq_contains = Some(memo_sq_contains); let tp = ptr::addr_of_mut!(Memo_Type); - (*tp).tp_name = cstr!("copium.memo"); + (*tp).tp_name = cstr!("copium.memo").as_ptr(); (*tp).tp_basicsize = std::mem::size_of::() as Py_ssize_t; (*tp).tp_dealloc = Some(memo_dealloc); (*tp).tp_repr = Some(memo_repr); @@ -939,6 +940,6 @@ pub unsafe fn memo_ready_type() -> i32 { (*tp).tp_methods = ptr::addr_of_mut!(MEMO_METHODS_TABLE).cast::(); (*tp).tp_finalize = Some(memo_finalize); - PyType_Ready(tp) + py::type_object::ready(tp) } } diff --git a/src/memo/tss.rs b/src/memo/tss.rs index 13e4d05..07d4632 100644 --- a/src/memo/tss.rs +++ b/src/memo/tss.rs @@ -1,10 +1,10 @@ -use pyo3_ffi::*; use std::ffi::c_void; use std::hint::{likely, unlikely}; use std::ptr; use super::native::PyMemoObject; use super::pytype::Memo_Type; +use crate::py; use crate::types::PyObjectPtr; #[thread_local] @@ -12,7 +12,7 @@ static mut TSS_MEMO: *mut PyMemoObject = ptr::null_mut(); pub unsafe fn pymemo_alloc() -> *mut PyMemoObject { unsafe { - let memo = PyObject_GC_New::(ptr::addr_of_mut!(Memo_Type)); + let memo = py::gc::new::(ptr::addr_of_mut!(Memo_Type)); if memo.is_null() { return ptr::null_mut(); } @@ -60,7 +60,7 @@ pub unsafe fn cleanup_memo(memo: *mut PyMemoObject, is_tss: bool) { } } - PyObject_GC_Track(memo as *mut c_void); + py::gc::track(memo as *mut c_void); memo.decref(); } } diff --git a/src/patch.rs b/src/patch.rs index a652b85..ab346f8 100644 --- a/src/patch.rs +++ b/src/patch.rs @@ -4,9 +4,14 @@ use pyo3::types::PyFunction; use pyo3_ffi::*; use std::ptr; +use crate::py; +use crate::py::vectorcall::PyVectorcallPtr; use crate::types::PyObjectPtr; -const CAPSULE_NAME: *const std::ffi::c_char = b"copium._original_vectorcall\0".as_ptr().cast(); +#[inline(always)] +fn capsule_name() -> &'static std::ffi::CStr { + crate::cstr!("copium._original_vectorcall") +} // ══════════════════════════════════════════════════════════════ // 3.12+: swap the vectorcall slot on the function object @@ -23,7 +28,7 @@ pub(crate) unsafe extern "C" fn copium_deepcopy_vectorcall( crate::py_deepcopy( ptr::null_mut(), args, - crate::ffi_ext::PyVectorcall_NARGS(nargsf), + py::vectorcall::nargs(nargsf), kwnames, ) } @@ -32,7 +37,8 @@ pub(crate) unsafe extern "C" fn copium_deepcopy_vectorcall( #[cfg(Py_3_12)] unsafe fn is_patched(fn_ptr: *mut PyObject) -> bool { unsafe { - crate::ffi_ext::PyVectorcall_Function(fn_ptr) + fn_ptr + .vectorcall_function() .map(|f| f as usize == copium_deepcopy_vectorcall as *const () as usize) .unwrap_or(false) } @@ -41,10 +47,10 @@ unsafe fn is_patched(fn_ptr: *mut PyObject) -> bool { #[cfg(Py_3_12)] unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObject) -> i32 { unsafe { - let original_vc = match crate::ffi_ext::PyVectorcall_Function(fn_ptr) { + let original_vc = match fn_ptr.vectorcall_function() { Some(f) => f, None => { - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("copium.patch: function has no vectorcall slot"), ); @@ -52,28 +58,28 @@ unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObj } }; - let capsule = PyCapsule_New( + let capsule = py::capsule::new( std::mem::transmute::(original_vc), - CAPSULE_NAME, + capsule_name(), None, ); if capsule.is_null() { return -1; } - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__copium_original__"), capsule) < 0 { + if fn_ptr.set_attr_cstr(crate::cstr!("__copium_original__"), capsule) < 0 { capsule.decref(); return -1; } capsule.decref(); - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__wrapped__"), target) < 0 { - PyObject_DelAttrString(fn_ptr, crate::cstr!("__copium_original__")); - PyErr_Clear(); + if fn_ptr.set_attr_cstr(crate::cstr!("__wrapped__"), target) < 0 { + fn_ptr.del_attr_cstr(crate::cstr!("__copium_original__")); + py::err::clear(); return -1; } - crate::ffi_ext::PyFunction_SetVectorcall(fn_ptr, copium_deepcopy_vectorcall); + py::vectorcall::set_function_vectorcall(fn_ptr, copium_deepcopy_vectorcall); 1 } } @@ -81,29 +87,32 @@ unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObj #[cfg(Py_3_12)] unsafe fn unapply_patch(_py: Python<'_>, fn_ptr: *mut PyObject) -> i32 { unsafe { - let capsule = PyObject_GetAttrString(fn_ptr, crate::cstr!("__copium_original__")); + let capsule = fn_ptr.getattr_cstr(crate::cstr!("__copium_original__")); if capsule.is_null() { - PyErr_Clear(); - PyErr_SetString( + py::err::clear(); + py::err::set_string( PyExc_RuntimeError, crate::cstr!("copium.patch: not applied"), ); return -1; } - let raw = PyCapsule_GetPointer(capsule, CAPSULE_NAME); + let raw = py::capsule::PyCapsulePtr::get_pointer( + capsule, + capsule_name(), + ); capsule.decref(); if raw.is_null() { return -1; } let original_vc = std::mem::transmute::<*mut std::ffi::c_void, vectorcallfunc>(raw); - crate::ffi_ext::PyFunction_SetVectorcall(fn_ptr, original_vc); + py::vectorcall::set_function_vectorcall(fn_ptr, original_vc); - PyObject_DelAttrString(fn_ptr, crate::cstr!("__copium_original__")); - PyErr_Clear(); - PyObject_DelAttrString(fn_ptr, crate::cstr!("__wrapped__")); - PyErr_Clear(); + fn_ptr.del_attr_cstr(crate::cstr!("__copium_original__")); + py::err::clear(); + fn_ptr.del_attr_cstr(crate::cstr!("__wrapped__")); + py::err::clear(); 0 } @@ -115,7 +124,7 @@ unsafe fn unapply_patch(_py: Python<'_>, fn_ptr: *mut PyObject) -> i32 { #[cfg(not(Py_3_12))] unsafe fn is_patched(fn_ptr: *mut PyObject) -> bool { - unsafe { PyObject_HasAttrString(fn_ptr, crate::cstr!("__copium_original__")) != 0 } + unsafe { fn_ptr.has_attr_cstr(crate::cstr!("__copium_original__")) } } #[cfg(not(Py_3_12))] @@ -124,13 +133,13 @@ unsafe fn template_code() -> *mut PyObject { use crate::{py_cache, py_exec, py_obj, py_str}; py_cache!({ let filters = py_obj!("warnings.filters"); - let saved_warnings = PySequence_List(filters); + let saved_warnings = py::seq::to_list(filters).as_object(); let warnings_set = py_obj!("warnings.simplefilter").call_one(py_str!("ignore")); if !warnings_set.is_null() { warnings_set.decref(); } else { - PyErr_Clear(); + py::err::clear(); } let globals = py_exec!( @@ -163,12 +172,12 @@ unsafe fn template_code() -> *mut PyObject { unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { unsafe { let tc = template_code(); - let template_consts = PyObject_GetAttrString(tc, crate::cstr!("co_consts")); + let template_consts = tc.getattr_cstr(crate::cstr!("co_consts")); if template_consts.is_null() { return ptr::null_mut(); } - let n = PyTuple_Size(template_consts); + let n = py::tuple::size(template_consts); if n < 0 { template_consts.decref(); return ptr::null_mut(); @@ -176,10 +185,10 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { let mut sentinel_idx: Py_ssize_t = -1; for i in 0..n { - let item = PyTuple_GetItem(template_consts, i); + let item = py::tuple::get_item(template_consts, i); if !item.is_null() - && PyUnicode_Check(item) != 0 - && PyUnicode_CompareWithASCIIString(item, crate::cstr!("copium.deepcopy")) == 0 + && item.is_unicode() + && py::unicode::compare_ascii(item, crate::cstr!("copium.deepcopy")) == 0 { sentinel_idx = i; break; @@ -188,14 +197,14 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { if sentinel_idx < 0 { template_consts.decref(); - PyErr_SetString( + py::err::set_string( PyExc_RuntimeError, crate::cstr!("copium.patch: sentinel not found"), ); return ptr::null_mut(); } - let new_consts = PyList_New(n); + let new_consts = py::list::new(n).as_object(); if new_consts.is_null() { template_consts.decref(); return ptr::null_mut(); @@ -205,14 +214,14 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { let item = if j == sentinel_idx { target } else { - PyTuple_GetItem(template_consts, j) + py::tuple::get_item(template_consts, j) }; if item.is_null() { new_consts.decref(); template_consts.decref(); return ptr::null_mut(); } - if PyList_SetItem(new_consts, j, item.newref()) < 0 { + if py::list::set_item(new_consts, j, item.newref()) < 0 { new_consts.decref(); template_consts.decref(); return ptr::null_mut(); @@ -220,25 +229,26 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { } template_consts.decref(); - let consts_tuple = PyList_AsTuple(new_consts); + let consts_tuple = py::list::as_tuple(new_consts).as_object(); new_consts.decref(); if consts_tuple.is_null() { return ptr::null_mut(); } - let replace = PyObject_GetAttrString(tc, crate::cstr!("replace")); + let replace = tc.getattr_cstr(crate::cstr!("replace")); if replace.is_null() { consts_tuple.decref(); return ptr::null_mut(); } - let kwargs = PyDict_New(); + let kwargs = py::dict::new().as_object(); if kwargs.is_null() { replace.decref(); consts_tuple.decref(); return ptr::null_mut(); } - if PyDict_SetItemString(kwargs, crate::cstr!("co_consts"), consts_tuple) < 0 { + if (kwargs as *mut PyDictObject).set_item_cstr(crate::cstr!("co_consts"), consts_tuple) < 0 + { kwargs.decref(); replace.decref(); consts_tuple.decref(); @@ -246,13 +256,13 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { } consts_tuple.decref(); - let empty = PyTuple_New(0); + let empty = py::tuple::new(0).as_object(); if empty.is_null() { kwargs.decref(); replace.decref(); return ptr::null_mut(); } - let new_code = PyObject_Call(replace, empty, kwargs); + let new_code = replace.call_with_kwargs(empty, kwargs); empty.decref(); replace.decref(); kwargs.decref(); @@ -263,28 +273,28 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { #[cfg(not(Py_3_12))] unsafe fn cleanup_patch_attrs(fn_ptr: *mut PyObject) { unsafe { - PyObject_DelAttrString(fn_ptr, crate::cstr!("__copium_original__")); - PyErr_Clear(); - PyObject_DelAttrString(fn_ptr, crate::cstr!("__wrapped__")); - PyErr_Clear(); + fn_ptr.del_attr_cstr(crate::cstr!("__copium_original__")); + py::err::clear(); + fn_ptr.del_attr_cstr(crate::cstr!("__wrapped__")); + py::err::clear(); } } #[cfg(not(Py_3_12))] unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObject) -> i32 { unsafe { - let current_code = PyObject_GetAttrString(fn_ptr, crate::cstr!("__code__")); + let current_code = fn_ptr.getattr_cstr(crate::cstr!("__code__")); if current_code.is_null() { return -1; } - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__copium_original__"), current_code) < 0 { + if fn_ptr.set_attr_cstr(crate::cstr!("__copium_original__"), current_code) < 0 { current_code.decref(); return -1; } current_code.decref(); - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__wrapped__"), target) < 0 { + if fn_ptr.set_attr_cstr(crate::cstr!("__wrapped__"), target) < 0 { cleanup_patch_attrs(fn_ptr); return -1; } @@ -295,7 +305,7 @@ unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObj return -1; } - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__code__"), new_code) < 0 { + if fn_ptr.set_attr_cstr(crate::cstr!("__code__"), new_code) < 0 { new_code.decref(); cleanup_patch_attrs(fn_ptr); return -1; @@ -308,17 +318,17 @@ unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObj #[cfg(not(Py_3_12))] unsafe fn unapply_patch(_py: Python<'_>, fn_ptr: *mut PyObject) -> i32 { unsafe { - let original_code = PyObject_GetAttrString(fn_ptr, crate::cstr!("__copium_original__")); + let original_code = fn_ptr.getattr_cstr(crate::cstr!("__copium_original__")); if original_code.is_null() { - PyErr_Clear(); - PyErr_SetString( + py::err::clear(); + py::err::set_string( PyExc_RuntimeError, crate::cstr!("copium.patch: not applied"), ); return -1; } - if PyObject_SetAttrString(fn_ptr, crate::cstr!("__code__"), original_code) < 0 { + if fn_ptr.set_attr_cstr(crate::cstr!("__code__"), original_code) < 0 { original_code.decref(); return -1; } diff --git a/src/py/boolean.rs b/src/py/boolean.rs new file mode 100644 index 0000000..b46132d --- /dev/null +++ b/src/py/boolean.rs @@ -0,0 +1,6 @@ +use pyo3_ffi::*; + +#[inline(always)] +pub unsafe fn from_bool(value: bool) -> *mut PyObject { + pyo3_ffi::PyBool_FromLong(value as libc::c_long) +} diff --git a/src/py/bytearray.rs b/src/py/bytearray.rs new file mode 100644 index 0000000..904c85a --- /dev/null +++ b/src/py/bytearray.rs @@ -0,0 +1,33 @@ +use pyo3_ffi::*; + +pub unsafe trait PyBufPtr: Sized { + unsafe fn length(self) -> Py_ssize_t; + unsafe fn as_mut_ptr(self) -> *mut u8; + + #[inline(always)] + unsafe fn len(self) -> Py_ssize_t { + self.length() + } + + #[inline(always)] + unsafe fn as_ptr(self) -> *mut u8 { + self.as_mut_ptr() + } +} + +unsafe impl PyBufPtr for *mut PyByteArrayObject { + #[inline(always)] + unsafe fn length(self) -> Py_ssize_t { + pyo3_ffi::PyByteArray_Size(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn as_mut_ptr(self) -> *mut u8 { + pyo3_ffi::PyByteArray_AsString(self as *mut PyObject) as *mut u8 + } +} + +#[inline(always)] +pub unsafe fn new(length: Py_ssize_t) -> *mut PyByteArrayObject { + pyo3_ffi::PyByteArray_FromStringAndSize(std::ptr::null(), length) as *mut PyByteArrayObject +} diff --git a/src/py/call.rs b/src/py/call.rs new file mode 100644 index 0000000..d6a1e3e --- /dev/null +++ b/src/py/call.rs @@ -0,0 +1,45 @@ +#![allow(unused_macros)] + +macro_rules! function { + ($callable:expr, $format_string:expr $(, $argument:expr )* $(,)?) => {{ + #[allow(unused_unsafe)] + unsafe { + $crate::py::ffi::PyObject_CallFunction( + ($callable) as *mut ::pyo3_ffi::PyObject, + ($format_string).as_ptr() + $(, $argument )* + ) + } + }}; +} + +pub(crate) use function; + +macro_rules! function_obj_args { + ($callable:expr $(, $argument:expr )* $(,)?) => {{ + #[allow(unused_unsafe)] + unsafe { + $crate::py::ffi::PyObject_CallFunctionObjArgs( + ($callable) as *mut ::pyo3_ffi::PyObject + $(, $argument )* + ) + } + }}; +} + +pub(crate) use function_obj_args; + +macro_rules! method_obj_args { + ($callable:expr, $name:expr $(, $argument:expr )* $(,)?) => {{ + #[allow(unused_unsafe)] + unsafe { + $crate::py::ffi::PyObject_CallMethodObjArgs( + ($callable) as *mut ::pyo3_ffi::PyObject, + ($name) as *mut ::pyo3_ffi::PyObject + $(, $argument )* + ) + } + }}; +} + +pub(crate) use method_obj_args; diff --git a/src/py/capsule.rs b/src/py/capsule.rs new file mode 100644 index 0000000..61708f6 --- /dev/null +++ b/src/py/capsule.rs @@ -0,0 +1,24 @@ +use pyo3_ffi::*; +use std::ffi::{c_void, CStr}; + +use super::PyTypeInfo; + +pub unsafe trait PyCapsulePtr { + unsafe fn get_pointer(self, name: &CStr) -> *mut c_void; +} + +unsafe impl PyCapsulePtr for *mut T { + #[inline(always)] + unsafe fn get_pointer(self, name: &CStr) -> *mut c_void { + pyo3_ffi::PyCapsule_GetPointer(self as *mut PyObject, name.as_ptr()) + } +} + +#[inline(always)] +pub unsafe fn new( + pointer: *mut c_void, + name: &CStr, + destructor: Option, +) -> *mut PyObject { + pyo3_ffi::PyCapsule_New(pointer, name.as_ptr(), destructor) +} diff --git a/src/py/critical_section.rs b/src/py/critical_section.rs new file mode 100644 index 0000000..cedce12 --- /dev/null +++ b/src/py/critical_section.rs @@ -0,0 +1,40 @@ +use pyo3_ffi::*; + +use super::PyTypeInfo; + +#[cfg(Py_GIL_DISABLED)] +#[inline(always)] +pub unsafe fn begin(critical_section: &mut PyCriticalSection, object: *mut T) { + pyo3_ffi::PyCriticalSection_Begin(critical_section, object as *mut PyObject) +} + +#[cfg(not(Py_GIL_DISABLED))] +#[inline(always)] +pub unsafe fn begin(critical_section: &mut (), object: *mut T) { + let _ = critical_section; + let _ = object; +} + +#[cfg(Py_GIL_DISABLED)] +#[inline(always)] +pub unsafe fn end(critical_section: &mut PyCriticalSection) { + pyo3_ffi::PyCriticalSection_End(critical_section) +} + +#[cfg(not(Py_GIL_DISABLED))] +#[inline(always)] +pub unsafe fn end(critical_section: &mut ()) { + let _ = critical_section; +} + +#[cfg(Py_GIL_DISABLED)] +#[inline(always)] +pub unsafe fn end2(critical_section: &mut PyCriticalSection2) { + pyo3_ffi::PyCriticalSection2_End(critical_section) +} + +#[cfg(not(Py_GIL_DISABLED))] +#[inline(always)] +pub unsafe fn end2(critical_section: &mut ()) { + let _ = critical_section; +} diff --git a/src/py/dict.rs b/src/py/dict.rs new file mode 100644 index 0000000..d9737a7 --- /dev/null +++ b/src/py/dict.rs @@ -0,0 +1,208 @@ +use pyo3_ffi::*; +use std::ffi::CStr; +use std::os::raw::c_int; + +use super::{ffi, PyTypeInfo}; + +#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] +pub type PyDictWatchCallback = Option< + unsafe extern "C" fn( + event: i32, + dict: *mut PyObject, + key: *mut PyObject, + new_value: *mut PyObject, + ) -> i32, +>; + +pub unsafe trait PyMapPtr: Sized { + unsafe fn size(self) -> Py_ssize_t; + unsafe fn set_item(self, key: *mut K, value: *mut V) -> c_int; + unsafe fn steal_item(self, key: *mut K, value: *mut V) + -> c_int; + unsafe fn get_item(self, key: *mut K) -> *mut PyObject; + unsafe fn borrow_item_cstr(self, key: &CStr) -> *mut PyObject; + unsafe fn dict_copy(self) -> *mut PyDictObject; + unsafe fn merge(self, other: *mut T, override_existing: bool) -> c_int; + unsafe fn dict_next( + self, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + value: &mut *mut PyObject, + ) -> bool; + unsafe fn set_item_cstr(self, key: &CStr, value: *mut V) -> c_int; + unsafe fn watch(self, watcher_id: i32) -> c_int; + unsafe fn unwatch(self, watcher_id: i32) -> c_int; + + #[inline(always)] + unsafe fn len(self) -> Py_ssize_t { + self.size() + } + + #[inline(always)] + unsafe fn set_item_steal_two( + self, + key: *mut K, + value: *mut V, + ) -> c_int { + self.steal_item(key, value) + } +} + +unsafe impl PyMapPtr for *mut PyDictObject { + #[inline(always)] + unsafe fn size(self) -> Py_ssize_t { + pyo3_ffi::PyDict_Size(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn set_item(self, key: *mut K, value: *mut V) -> c_int { + pyo3_ffi::PyDict_SetItem( + self as *mut PyObject, + key as *mut PyObject, + value as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn steal_item( + self, + key: *mut K, + value: *mut V, + ) -> c_int { + ffi::_PyDict_SetItem_Take2( + self as *mut PyObject, + key as *mut PyObject, + value as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn get_item(self, key: *mut K) -> *mut PyObject { + pyo3_ffi::PyDict_GetItemWithError(self as *mut PyObject, key as *mut PyObject) + } + + #[inline(always)] + unsafe fn borrow_item_cstr(self, key: &CStr) -> *mut PyObject { + pyo3_ffi::PyDict_GetItemString(self as *mut PyObject, key.as_ptr()) + } + + #[inline(always)] + unsafe fn dict_copy(self) -> *mut PyDictObject { + pyo3_ffi::PyDict_Copy(self as *mut PyObject) as *mut PyDictObject + } + + #[inline(always)] + unsafe fn merge(self, other: *mut T, override_existing: bool) -> c_int { + pyo3_ffi::PyDict_Merge( + self as *mut PyObject, + other as *mut PyObject, + override_existing as c_int, + ) + } + + #[inline(always)] + unsafe fn dict_next( + self, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + value: &mut *mut PyObject, + ) -> bool { + pyo3_ffi::PyDict_Next(self as *mut PyObject, position, key, value) != 0 + } + + #[inline(always)] + unsafe fn set_item_cstr(self, key: &CStr, value: *mut V) -> c_int { + pyo3_ffi::PyDict_SetItemString( + self as *mut PyObject, + key.as_ptr(), + value as *mut PyObject, + ) + } + + #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] + #[inline(always)] + unsafe fn watch(self, watcher_id: i32) -> c_int { + ffi::PyDict_Watch(watcher_id, self as *mut PyObject) + } + + #[cfg(not(all(Py_3_14, not(Py_GIL_DISABLED))))] + #[inline(always)] + unsafe fn watch(self, watcher_id: i32) -> c_int { + let _ = self; + let _ = watcher_id; + -1 + } + + #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] + #[inline(always)] + unsafe fn unwatch(self, watcher_id: i32) -> c_int { + ffi::PyDict_Unwatch(watcher_id, self as *mut PyObject) + } + + #[cfg(not(all(Py_3_14, not(Py_GIL_DISABLED))))] + #[inline(always)] + unsafe fn unwatch(self, watcher_id: i32) -> c_int { + let _ = self; + let _ = watcher_id; + -1 + } +} + +#[inline(always)] +pub unsafe fn new() -> *mut PyDictObject { + pyo3_ffi::PyDict_New() as *mut PyDictObject +} + +#[inline(always)] +pub unsafe fn new_presized(length: Py_ssize_t) -> *mut PyDictObject { + ffi::_PyDict_NewPresized(length) as *mut PyDictObject +} + +#[inline(always)] +pub unsafe fn size(dictionary: *mut D) -> Py_ssize_t { + pyo3_ffi::PyDict_Size(dictionary as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn next( + dictionary: *mut D, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + value: &mut *mut PyObject, +) -> bool { + pyo3_ffi::PyDict_Next(dictionary as *mut PyObject, position, key, value) != 0 +} + +#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] +#[inline(always)] +pub unsafe fn add_watcher(callback: PyDictWatchCallback) -> i32 { + ffi::PyDict_AddWatcher(callback) +} + +#[cfg(not(all(Py_3_14, not(Py_GIL_DISABLED))))] +#[inline(always)] +pub unsafe fn add_watcher( + callback: Option< + unsafe extern "C" fn( + event: i32, + dict: *mut PyObject, + key: *mut PyObject, + new_value: *mut PyObject, + ) -> i32, + >, +) -> i32 { + let _ = callback; + -1 +} + +#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] +#[inline(always)] +pub unsafe fn clear_watcher(watcher_id: i32) { + let _ = ffi::PyDict_ClearWatcher(watcher_id); +} + +#[cfg(not(all(Py_3_14, not(Py_GIL_DISABLED))))] +#[inline(always)] +pub unsafe fn clear_watcher(watcher_id: i32) { + let _ = watcher_id; +} diff --git a/src/py/err.rs b/src/py/err.rs new file mode 100644 index 0000000..0e993ec --- /dev/null +++ b/src/py/err.rs @@ -0,0 +1,104 @@ +use pyo3_ffi::*; +use std::ffi::CStr; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +#[inline(always)] +pub unsafe fn set(exception: *mut E, value: *mut T) { + pyo3_ffi::PyErr_SetObject(exception as *mut PyObject, value as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn set_string(exception: *mut E, message: &CStr) { + pyo3_ffi::PyErr_SetString(exception as *mut PyObject, message.as_ptr()) +} + +macro_rules! format { + ($exception:expr, $format_string:expr $(, $argument:expr )* $(,)?) => {{ + #[allow(unused_unsafe)] + unsafe { + $crate::py::ffi::PyErr_Format( + ($exception) as *mut ::pyo3_ffi::PyObject, + ($format_string).as_ptr() + $(, $argument )* + ) + } + }}; +} + +pub(crate) use format; + +#[inline(always)] +pub unsafe fn occurred() -> *mut PyObject { + pyo3_ffi::PyErr_Occurred() +} + +#[inline(always)] +pub unsafe fn clear() { + pyo3_ffi::PyErr_Clear() +} + +#[inline(always)] +pub unsafe fn fetch() -> (*mut PyObject, *mut PyObject, *mut PyObject) { + let mut type_pointer = std::ptr::null_mut(); + let mut value = std::ptr::null_mut(); + let mut traceback = std::ptr::null_mut(); + #[allow(deprecated)] + unsafe { + pyo3_ffi::PyErr_Fetch(&mut type_pointer, &mut value, &mut traceback); + } + (type_pointer, value, traceback) +} + +#[inline(always)] +pub unsafe fn restore(type_pointer: *mut PyObject, value: *mut PyObject, traceback: *mut PyObject) { + #[allow(deprecated)] + unsafe { + pyo3_ffi::PyErr_Restore(type_pointer, value, traceback); + } +} + +#[inline(always)] +pub unsafe fn normalize( + type_pointer: &mut *mut PyObject, + value: &mut *mut PyObject, + traceback: &mut *mut PyObject, +) { + #[allow(deprecated)] + unsafe { + pyo3_ffi::PyErr_NormalizeException(type_pointer, value, traceback); + } +} + +#[inline(always)] +pub unsafe fn matches(error: *mut E, against: *mut A) -> bool { + pyo3_ffi::PyErr_GivenExceptionMatches(error as *mut PyObject, against as *mut PyObject) != 0 +} + +#[inline(always)] +pub unsafe fn matches_current(against: *mut A) -> bool { + pyo3_ffi::PyErr_ExceptionMatches(against as *mut PyObject) != 0 +} + +#[inline(always)] +pub unsafe fn warn(category: *mut C, message: &CStr, stack_level: i32) -> c_int { + pyo3_ffi::PyErr_WarnEx( + category as *mut PyObject, + message.as_ptr(), + stack_level as Py_ssize_t, + ) +} + +#[inline(always)] +pub unsafe fn set_traceback( + exception: *mut E, + traceback: *mut T, +) -> c_int { + pyo3_ffi::PyException_SetTraceback(exception as *mut PyObject, traceback as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn set_cause(exception: *mut E, cause: *mut C) { + pyo3_ffi::PyException_SetCause(exception as *mut PyObject, cause as *mut PyObject) +} diff --git a/src/py/eval.rs b/src/py/eval.rs new file mode 100644 index 0000000..eb4b05b --- /dev/null +++ b/src/py/eval.rs @@ -0,0 +1,30 @@ +use pyo3_ffi::*; +use std::ffi::CStr; + +use super::PyTypeInfo; + +#[inline(always)] +pub unsafe fn builtins() -> *mut PyDictObject { + pyo3_ffi::PyEval_GetBuiltins() as *mut PyDictObject +} + +#[inline(always)] +pub unsafe fn run_string( + code: &CStr, + start: i32, + globals: *mut G, + locals: *mut L, +) -> *mut PyObject { + pyo3_ffi::PyRun_StringFlags( + code.as_ptr(), + start, + globals as *mut PyObject, + locals as *mut PyObject, + std::ptr::null_mut(), + ) +} + +#[inline(always)] +pub unsafe fn current_frame() -> *mut PyFrameObject { + pyo3_ffi::PyEval_GetFrame() +} diff --git a/src/py/ffi.rs b/src/py/ffi.rs new file mode 100644 index 0000000..17f6801 --- /dev/null +++ b/src/py/ffi.rs @@ -0,0 +1,207 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(unused_imports)] +#![allow(unused_unsafe)] + +use core::ffi::{c_char, CStr}; +use libc::c_ulong; +use pyo3_ffi::*; +use std::os::raw::c_int; + +#[cfg(Py_GIL_DISABLED)] +use core::sync::atomic::Ordering; + +pub use pyo3_ffi::{_PyWeakref_RefType, PyEllipsis_Type, PyProperty_Type, Py_None}; + +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { + pub static mut PyMethod_Type: PyTypeObject; + pub static mut _PyNone_Type: PyTypeObject; + pub static mut _PyNotImplemented_Type: PyTypeObject; +} + +#[repr(C)] +pub struct PyMethodObject { + pub ob_base: PyObject, + pub im_func: *mut PyObject, + pub im_self: *mut PyObject, + pub im_weakreflist: *mut PyObject, + pub vectorcall: vectorcallfunc, +} + +#[inline(always)] +pub unsafe fn PyMethod_GET_FUNCTION(method_object: *mut PyObject) -> *mut PyObject { + unsafe { (*(method_object as *mut PyMethodObject)).im_func } +} + +#[inline(always)] +pub unsafe fn PyMethod_GET_SELF(method_object: *mut PyObject) -> *mut PyObject { + unsafe { (*(method_object as *mut PyMethodObject)).im_self } +} + +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { + pub fn PyMethod_New(function: *mut PyObject, self_object: *mut PyObject) -> *mut PyObject; +} + +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { + pub fn PyErr_Format(exception: *mut PyObject, format: *const c_char, ...) -> *mut PyObject; + pub fn PyUnicode_FromFormat(format: *const c_char, ...) -> *mut PyObject; + pub fn PySequence_Fast(object: *mut PyObject, message: *const c_char) -> *mut PyObject; + pub fn PyObject_CallFunction( + callable: *mut PyObject, + format: *const c_char, + ... + ) -> *mut PyObject; + pub fn PyObject_CallFunctionObjArgs(callable: *mut PyObject, ...) -> *mut PyObject; + pub fn PyObject_CallMethodObjArgs( + callable: *mut PyObject, + name: *mut PyObject, + ... + ) -> *mut PyObject; + #[cfg(Py_3_12)] + pub fn PyFunction_SetVectorcall(callable: *mut PyObject, vectorcall: vectorcallfunc); + pub fn PyVectorcall_Function(callable: *mut PyObject) -> Option; +} + +extern "C" { + pub fn _PyDict_NewPresized(minused: Py_ssize_t) -> *mut PyObject; + pub fn _PySet_NextEntry( + set: *mut PyObject, + pos: *mut Py_ssize_t, + key: *mut *mut PyObject, + hash: *mut Py_hash_t, + ) -> c_int; +} + +#[cfg(Py_3_13)] +extern "C" { + pub fn _PyDict_SetItem_Take2( + dictionary: *mut PyObject, + key: *mut PyObject, + value: *mut PyObject, + ) -> c_int; +} + +#[cfg(not(Py_3_13))] +#[inline(always)] +pub unsafe fn _PyDict_SetItem_Take2( + dictionary: *mut PyObject, + key: *mut PyObject, + value: *mut PyObject, +) -> c_int { + unsafe { + let status = PyDict_SetItem(dictionary, key, value); + Py_DECREF(key); + Py_DECREF(value); + status + } +} + +#[cfg(any(Py_3_13, Py_3_14))] +extern "C" { + pub fn PyObject_GetOptionalAttr( + object: *mut PyObject, + name: *mut PyObject, + result: *mut *mut PyObject, + ) -> c_int; +} + +#[cfg(not(any(Py_3_13, Py_3_14)))] +extern "C" { + fn _PyObject_LookupAttr( + object: *mut PyObject, + name: *mut PyObject, + result: *mut *mut PyObject, + ) -> c_int; +} + +#[cfg(not(any(Py_3_13, Py_3_14)))] +#[inline(always)] +pub unsafe fn PyObject_GetOptionalAttr( + object: *mut PyObject, + name: *mut PyObject, + result: *mut *mut PyObject, +) -> c_int { + unsafe { _PyObject_LookupAttr(object, name, result) } +} + +#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] +extern "C" { + pub fn PyDict_AddWatcher( + callback: Option< + unsafe extern "C" fn( + event: i32, + dict: *mut PyObject, + key: *mut PyObject, + new_value: *mut PyObject, + ) -> i32, + >, + ) -> i32; + pub fn PyDict_ClearWatcher(watcher_id: i32) -> i32; + pub fn PyDict_Watch(watcher_id: i32, dict: *mut PyObject) -> i32; + pub fn PyDict_Unwatch(watcher_id: i32, dict: *mut PyObject) -> i32; +} + +#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +extern "C" { + pub fn PyIter_NextItem(iterator: *mut PyObject, item: *mut *mut PyObject) -> i32; +} + +#[inline(always)] +pub unsafe fn PySequence_Fast_GET_SIZE(object: *mut PyObject) -> Py_ssize_t { + unsafe { Py_SIZE(object) } +} + +#[cfg(Py_GIL_DISABLED)] +#[inline(always)] +pub unsafe fn tp_flags_of(type_object: *mut PyTypeObject) -> c_ulong { + unsafe { (*type_object).tp_flags.load(Ordering::Relaxed) } +} + +#[cfg(not(Py_GIL_DISABLED))] +#[inline(always)] +pub unsafe fn tp_flags_of(type_object: *mut PyTypeObject) -> c_ulong { + unsafe { (*type_object).tp_flags } +} + +#[inline(always)] +pub unsafe fn PySequence_Fast_GET_ITEM( + object: *mut PyObject, + index: Py_ssize_t, +) -> *mut PyObject { + unsafe { + if (tp_flags_of((*object).ob_type) & (Py_TPFLAGS_LIST_SUBCLASS as c_ulong)) != 0 { + *(*(object as *mut PyListObject)).ob_item.add(index as usize) + } else { + *(*(object as *mut PyTupleObject)) + .ob_item + .as_ptr() + .add(index as usize) + } + } +} + +#[inline(always)] +pub fn PyVectorcall_NARGS(nargsf: usize) -> Py_ssize_t { + const VECTORCALL_OFFSET_BIT: usize = 1usize << (usize::BITS - 1); + (nargsf & !VECTORCALL_OFFSET_BIT) as Py_ssize_t +} + +#[macro_export] +macro_rules! cstr { + ($string_literal:literal) => {{ + #[allow(unused_unsafe)] + unsafe { + ::std::ffi::CStr::from_bytes_with_nul_unchecked( + concat!($string_literal, "\0").as_bytes(), + ) + } + }}; +} + +#[inline(always)] +pub fn ptr_from_cstr(value: &CStr) -> *const c_char { + value.as_ptr() +} diff --git a/src/py/frame.rs b/src/py/frame.rs new file mode 100644 index 0000000..df69330 --- /dev/null +++ b/src/py/frame.rs @@ -0,0 +1,24 @@ +use pyo3_ffi::*; + +pub unsafe trait PyFramePtr { + unsafe fn back(self) -> *mut PyFrameObject; + unsafe fn code(self) -> *mut PyCodeObject; + unsafe fn line_number(self) -> i32; +} + +unsafe impl PyFramePtr for *mut PyFrameObject { + #[inline(always)] + unsafe fn back(self) -> *mut PyFrameObject { + pyo3_ffi::PyFrame_GetBack(self) + } + + #[inline(always)] + unsafe fn code(self) -> *mut PyCodeObject { + pyo3_ffi::PyFrame_GetCode(self) + } + + #[inline(always)] + unsafe fn line_number(self) -> i32 { + pyo3_ffi::PyFrame_GetLineNumber(self) + } +} diff --git a/src/py/gc.rs b/src/py/gc.rs new file mode 100644 index 0000000..cdffff6 --- /dev/null +++ b/src/py/gc.rs @@ -0,0 +1,30 @@ +use pyo3_ffi::*; +use std::ffi::c_void; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +#[inline(always)] +pub unsafe fn new(type_object: *mut PyTypeObject) -> *mut T { + pyo3_ffi::PyObject_GC_New(type_object) +} + +#[inline(always)] +pub unsafe fn track(pointer: *mut c_void) { + pyo3_ffi::PyObject_GC_Track(pointer) +} + +#[inline(always)] +pub unsafe fn untrack(pointer: *mut c_void) { + pyo3_ffi::PyObject_GC_UnTrack(pointer) +} + +#[inline(always)] +pub unsafe fn delete(pointer: *mut c_void) { + pyo3_ffi::PyObject_GC_Del(pointer) +} + +#[inline(always)] +pub unsafe fn call_finalizer_from_dealloc(object: *mut T) -> c_int { + pyo3_ffi::PyObject_CallFinalizerFromDealloc(object as *mut PyObject) +} diff --git a/src/py/list.rs b/src/py/list.rs new file mode 100644 index 0000000..c2ed5e0 --- /dev/null +++ b/src/py/list.rs @@ -0,0 +1,72 @@ +use pyo3_ffi::*; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +pub unsafe trait PyMutSeqPtr: Sized { + unsafe fn append(self, item: *mut T) -> c_int; + unsafe fn insert(self, index: Py_ssize_t, item: *mut T) -> c_int; + unsafe fn to_tuple(self) -> *mut PyTupleObject; + + #[inline(always)] + unsafe fn as_tuple(self) -> *mut PyTupleObject { + self.to_tuple() + } +} + +unsafe impl PyMutSeqPtr for *mut PyListObject { + #[inline(always)] + unsafe fn append(self, item: *mut T) -> c_int { + pyo3_ffi::PyList_Append(self as *mut PyObject, item as *mut PyObject) + } + + #[inline(always)] + unsafe fn insert(self, index: Py_ssize_t, item: *mut T) -> c_int { + pyo3_ffi::PyList_Insert(self as *mut PyObject, index, item as *mut PyObject) + } + + #[inline(always)] + unsafe fn to_tuple(self) -> *mut PyTupleObject { + pyo3_ffi::PyList_AsTuple(self as *mut PyObject) as *mut PyTupleObject + } +} + +#[inline(always)] +pub unsafe fn new(length: Py_ssize_t) -> *mut PyListObject { + pyo3_ffi::PyList_New(length) as *mut PyListObject +} + +#[inline(always)] +pub unsafe fn append(list: *mut L, item: *mut V) -> c_int { + pyo3_ffi::PyList_Append(list as *mut PyObject, item as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn set_item(list: *mut L, index: Py_ssize_t, item: *mut PyObject) -> c_int { + pyo3_ffi::PyList_SetItem(list as *mut PyObject, index, item) +} + +#[inline(always)] +pub unsafe fn borrow_item(list: *mut L, index: Py_ssize_t) -> *mut PyObject { + pyo3_ffi::PyList_GET_ITEM(list as *mut PyObject, index) +} + +#[inline(always)] +pub unsafe fn size(list: *mut L) -> Py_ssize_t { + pyo3_ffi::PyList_GET_SIZE(list as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn insert(list: *mut L, index: Py_ssize_t, item: *mut V) -> c_int { + pyo3_ffi::PyList_Insert(list as *mut PyObject, index, item as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn check(list: *mut L) -> bool { + pyo3_ffi::PyList_Check(list as *mut PyObject) != 0 +} + +#[inline(always)] +pub unsafe fn as_tuple(list: *mut L) -> *mut PyTupleObject { + pyo3_ffi::PyList_AsTuple(list as *mut PyObject) as *mut PyTupleObject +} diff --git a/src/py/long.rs b/src/py/long.rs new file mode 100644 index 0000000..1c4959b --- /dev/null +++ b/src/py/long.rs @@ -0,0 +1,40 @@ +use pyo3_ffi::*; +use std::ffi::c_void; + +use super::PyTypeInfo; + +pub unsafe trait PyLongPtr { + unsafe fn as_i64(self) -> i64; +} + +unsafe impl PyLongPtr for *mut PyLongObject { + #[inline(always)] + unsafe fn as_i64(self) -> i64 { + pyo3_ffi::PyLong_AsLong(self as *mut PyObject) as i64 + } +} + +#[inline(always)] +pub unsafe fn from_i64(value: i64) -> *mut PyLongObject { + pyo3_ffi::PyLong_FromLong(value as libc::c_long) as *mut PyLongObject +} + +#[inline(always)] +pub unsafe fn as_i64(value: *mut T) -> i64 { + pyo3_ffi::PyLong_AsLong(value as *mut PyObject) as i64 +} + +#[inline(always)] +pub unsafe fn from_ptr(pointer: *mut T) -> *mut PyLongObject { + pyo3_ffi::PyLong_FromVoidPtr(pointer as *mut c_void) as *mut PyLongObject +} + +#[inline(always)] +pub unsafe fn as_ptr(value: *mut T) -> *mut c_void { + pyo3_ffi::PyLong_AsVoidPtr(value as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn check(value: *mut T) -> bool { + pyo3_ffi::PyLong_Check(value as *mut PyObject) != 0 +} diff --git a/src/py/method.rs b/src/py/method.rs new file mode 100644 index 0000000..2a8a7d5 --- /dev/null +++ b/src/py/method.rs @@ -0,0 +1,29 @@ +use pyo3_ffi::*; + +use super::{ffi, PyTypeInfo}; + +pub unsafe trait PyBoundMethodPtr { + unsafe fn function(self) -> *mut PyObject; + unsafe fn self_obj(self) -> *mut PyObject; +} + +unsafe impl PyBoundMethodPtr for *mut ffi::PyMethodObject { + #[inline(always)] + unsafe fn function(self) -> *mut PyObject { + ffi::PyMethod_GET_FUNCTION(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn self_obj(self) -> *mut PyObject { + ffi::PyMethod_GET_SELF(self as *mut PyObject) + } +} + +#[inline(always)] +pub unsafe fn new( + function: *mut F, + self_object: *mut S, +) -> *mut ffi::PyMethodObject { + ffi::PyMethod_New(function as *mut PyObject, self_object as *mut PyObject) + as *mut ffi::PyMethodObject +} diff --git a/src/py/mod.rs b/src/py/mod.rs new file mode 100644 index 0000000..4419f41 --- /dev/null +++ b/src/py/mod.rs @@ -0,0 +1,114 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(unused_imports)] +#![allow(unused_unsafe)] + +use pyo3_ffi::*; + +use crate::memo::{Memo_Type, PyMemoObject}; + +pub mod boolean; +pub mod bytearray; +pub mod call; +pub mod capsule; +pub mod critical_section; +pub mod dict; +pub mod err; +pub mod eval; +pub mod ffi; +pub mod frame; +pub mod gc; +pub mod list; +pub mod long; +pub mod method; +pub mod module; +pub mod object; +pub mod seq; +pub mod set; +pub mod tuple; +pub mod type_object; +pub mod unicode; +pub mod vectorcall; + +pub use boolean::*; +pub use bytearray::*; +pub use capsule::*; +pub use critical_section::*; +pub use dict::*; +pub use err::*; +pub use eval::*; +pub use frame::*; +pub use gc::*; +pub use list::*; +pub use long::*; +pub use method::*; +pub use module::*; +pub use object::*; +pub use seq::*; +pub use set::*; +pub use tuple::*; +pub use type_object::*; +pub use unicode::*; +pub use vectorcall::*; + +pub unsafe trait PyTypeInfo: Sized { + fn type_ptr() -> *mut PyTypeObject; + + #[inline(always)] + fn is(type_pointer: *mut PyTypeObject) -> bool { + type_pointer == Self::type_ptr() + } + + #[inline(always)] + unsafe fn cast_exact(object: *mut PyObject, class: *mut PyTypeObject) -> Option<*mut Self> { + if Self::is(class) { + Some(object as *mut Self) + } else { + None + } + } + + #[inline(always)] + unsafe fn cast_unchecked(object: *mut PyObject) -> *mut Self { + object as *mut Self + } +} + +macro_rules! pytype { + ($($rust_type:ty => $ffi_type:path),+ $(,)?) => {$( + unsafe impl PyTypeInfo for $rust_type { + #[inline(always)] + fn type_ptr() -> *mut PyTypeObject { + std::ptr::addr_of_mut!($ffi_type) + } + } + )+} +} + +pub struct PyFrozensetObject; + +pytype! { + PyObject => PyBaseObject_Type, + PyUnicodeObject => PyUnicode_Type, + PyListObject => PyList_Type, + PyTupleObject => PyTuple_Type, + PyDictObject => PyDict_Type, + PySetObject => PySet_Type, + PyByteArrayObject => PyByteArray_Type, + PyFrozensetObject => PyFrozenSet_Type, + ffi::PyMethodObject => ffi::PyMethod_Type, + PyMemoObject => Memo_Type, + PySliceObject => PySlice_Type, + PyLongObject => PyLong_Type, + PyCodeObject => PyCode_Type, +} + +#[inline(always)] +pub unsafe fn none() -> *mut PyObject { + ffi::Py_None() +} + +#[inline(always)] +pub unsafe fn ellipsis() -> *mut PyObject { + pyo3_ffi::Py_Ellipsis() +} diff --git a/src/py/module.rs b/src/py/module.rs new file mode 100644 index 0000000..ab81edf --- /dev/null +++ b/src/py/module.rs @@ -0,0 +1,48 @@ +use pyo3_ffi::*; +use std::ffi::CStr; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +#[inline(always)] +pub unsafe fn create(definition: *mut PyModuleDef) -> *mut PyObject { + pyo3_ffi::PyModule_Create(definition) +} + +#[inline(always)] +pub unsafe fn def_init(definition: *mut PyModuleDef) -> *mut PyObject { + pyo3_ffi::PyModuleDef_Init(definition) +} + +#[inline(always)] +pub unsafe fn add_object( + module: *mut M, + name: &CStr, + object: *mut V, +) -> c_int { + pyo3_ffi::PyModule_AddObject(module as *mut PyObject, name.as_ptr(), object as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn add_string_constant( + module: *mut M, + name: &CStr, + value: &CStr, +) -> c_int { + pyo3_ffi::PyModule_AddStringConstant(module as *mut PyObject, name.as_ptr(), value.as_ptr()) +} + +#[inline(always)] +pub unsafe fn get_name(module: *mut M) -> *mut PyUnicodeObject { + pyo3_ffi::PyModule_GetNameObject(module as *mut PyObject) as *mut PyUnicodeObject +} + +#[inline(always)] +pub unsafe fn import(name: &CStr) -> *mut PyObject { + pyo3_ffi::PyImport_ImportModule(name.as_ptr()) +} + +#[inline(always)] +pub unsafe fn get_module_dict() -> *mut PyDictObject { + pyo3_ffi::PyImport_GetModuleDict() as *mut PyDictObject +} diff --git a/src/py/object.rs b/src/py/object.rs new file mode 100644 index 0000000..1c4c219 --- /dev/null +++ b/src/py/object.rs @@ -0,0 +1,362 @@ +use libc::c_ulong; +use pyo3_ffi::*; +use std::ffi::{c_void, CStr}; +use std::os::raw::c_int; +use std::ptr; + +use super::{ffi, PyTypeInfo}; + +pub unsafe trait PyObjectPtr: Sized { + unsafe fn id(self) -> *mut PyLongObject; + unsafe fn as_object(self) -> *mut PyObject; + unsafe fn class(self) -> *mut PyTypeObject; + unsafe fn refcount(self) -> Py_ssize_t; + unsafe fn incref(self); + unsafe fn decref(self); + unsafe fn decref_if_nonnull(self); + unsafe fn newref(self) -> *mut PyObject; + + unsafe fn getattr(self, name: *mut N) -> *mut PyObject; + unsafe fn getattr_cstr(self, name: &CStr) -> *mut PyObject; + unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) + -> c_int; + unsafe fn set_attr(self, name: *mut N, value: *mut V) + -> c_int; + unsafe fn set_attr_cstr(self, name: &CStr, value: *mut V) -> c_int; + unsafe fn del_attr(self, name: *mut N) -> c_int; + unsafe fn del_attr_cstr(self, name: &CStr) -> c_int; + unsafe fn has_attr_cstr(self, name: &CStr) -> bool; + + unsafe fn call(self) -> *mut PyObject; + unsafe fn call_one(self, argument: *mut A) -> *mut PyObject; + unsafe fn call_with(self, args: *mut A) -> *mut PyObject; + unsafe fn call_with_kwargs( + self, + args: *mut A, + kwargs: *mut K, + ) -> *mut PyObject; + + unsafe fn setitem(self, key: *mut K, value: *mut V) -> c_int; + unsafe fn getitem(self, key: *mut K) -> *mut PyObject; + unsafe fn get_iter(self) -> *mut PyObject; + unsafe fn iter_next(self) -> *mut PyObject; + + unsafe fn repr(self) -> *mut PyUnicodeObject; + unsafe fn str_(self) -> *mut PyUnicodeObject; + unsafe fn is_callable(self) -> bool; + + unsafe fn is_type(self) -> bool; + unsafe fn is_tuple(self) -> bool; + unsafe fn is_dict(self) -> bool; + unsafe fn is_unicode(self) -> bool; + unsafe fn is_bytes(self) -> bool; + unsafe fn is_none(self) -> bool; + + #[inline(always)] + unsafe fn decref_nullable(self) { + self.decref_if_nonnull() + } + + #[inline(always)] + unsafe fn get_optional_attr( + self, + name: *mut N, + result: &mut *mut PyObject, + ) -> c_int { + self.getattr_opt(name, result) + } + +} + +unsafe impl PyObjectPtr for *mut T { + #[inline(always)] + unsafe fn id(self) -> *mut PyLongObject { + pyo3_ffi::PyLong_FromVoidPtr(self as *mut c_void) as *mut PyLongObject + } + + #[inline(always)] + unsafe fn as_object(self) -> *mut PyObject { + self as *mut PyObject + } + + #[inline(always)] + unsafe fn class(self) -> *mut PyTypeObject { + (*(self as *mut PyObject)).ob_type + } + + #[inline(always)] + unsafe fn refcount(self) -> Py_ssize_t { + pyo3_ffi::Py_REFCNT(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn incref(self) { + pyo3_ffi::Py_INCREF(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn decref(self) { + pyo3_ffi::Py_DECREF(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn decref_if_nonnull(self) { + if !self.is_null() { + self.decref(); + } + } + + #[inline(always)] + unsafe fn newref(self) -> *mut PyObject { + pyo3_ffi::Py_NewRef(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn getattr(self, name: *mut N) -> *mut PyObject { + pyo3_ffi::PyObject_GetAttr(self as *mut PyObject, name as *mut PyObject) + } + + #[inline(always)] + unsafe fn getattr_cstr(self, name: &CStr) -> *mut PyObject { + pyo3_ffi::PyObject_GetAttrString(self as *mut PyObject, name.as_ptr()) + } + + #[inline(always)] + unsafe fn getattr_opt( + self, + name: *mut N, + result: &mut *mut PyObject, + ) -> c_int { + ffi::PyObject_GetOptionalAttr(self as *mut PyObject, name as *mut PyObject, result) + } + + #[inline(always)] + unsafe fn set_attr( + self, + name: *mut N, + value: *mut V, + ) -> c_int { + pyo3_ffi::PyObject_SetAttr( + self as *mut PyObject, + name as *mut PyObject, + value as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn set_attr_cstr(self, name: &CStr, value: *mut V) -> c_int { + pyo3_ffi::PyObject_SetAttrString( + self as *mut PyObject, + name.as_ptr(), + value as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn del_attr(self, name: *mut N) -> c_int { + pyo3_ffi::PyObject_SetAttr( + self as *mut PyObject, + name as *mut PyObject, + ptr::null_mut(), + ) + } + + #[inline(always)] + unsafe fn del_attr_cstr(self, name: &CStr) -> c_int { + pyo3_ffi::PyObject_DelAttrString(self as *mut PyObject, name.as_ptr()) + } + + #[inline(always)] + unsafe fn has_attr_cstr(self, name: &CStr) -> bool { + pyo3_ffi::PyObject_HasAttrString(self as *mut PyObject, name.as_ptr()) != 0 + } + + #[inline(always)] + unsafe fn call(self) -> *mut PyObject { + pyo3_ffi::PyObject_CallNoArgs(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn call_one(self, argument: *mut A) -> *mut PyObject { + pyo3_ffi::PyObject_CallOneArg(self as *mut PyObject, argument as *mut PyObject) + } + + #[inline(always)] + unsafe fn call_with(self, args: *mut A) -> *mut PyObject { + pyo3_ffi::PyObject_CallObject(self as *mut PyObject, args as *mut PyObject) + } + + #[inline(always)] + unsafe fn call_with_kwargs( + self, + args: *mut A, + kwargs: *mut K, + ) -> *mut PyObject { + pyo3_ffi::PyObject_Call( + self as *mut PyObject, + args as *mut PyObject, + kwargs as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn setitem(self, key: *mut K, value: *mut V) -> c_int { + pyo3_ffi::PyObject_SetItem( + self as *mut PyObject, + key as *mut PyObject, + value as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn getitem(self, key: *mut K) -> *mut PyObject { + pyo3_ffi::PyObject_GetItem(self as *mut PyObject, key as *mut PyObject) + } + + #[inline(always)] + unsafe fn get_iter(self) -> *mut PyObject { + pyo3_ffi::PyObject_GetIter(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn iter_next(self) -> *mut PyObject { + pyo3_ffi::PyIter_Next(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn repr(self) -> *mut PyUnicodeObject { + pyo3_ffi::PyObject_Repr(self as *mut PyObject) as *mut PyUnicodeObject + } + + #[inline(always)] + unsafe fn str_(self) -> *mut PyUnicodeObject { + pyo3_ffi::PyObject_Str(self as *mut PyObject) as *mut PyUnicodeObject + } + + #[inline(always)] + unsafe fn is_callable(self) -> bool { + pyo3_ffi::PyCallable_Check(self as *mut PyObject) != 0 + } + + #[inline(always)] + unsafe fn is_type(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_TYPE_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_tuple(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_TUPLE_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_dict(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_DICT_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_unicode(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_UNICODE_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_bytes(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_BYTES_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_none(self) -> bool { + self as *mut PyObject == ffi::Py_None() + } +} + +#[inline(always)] +pub unsafe fn getattr_cstr(object: *mut O, name: &CStr) -> *mut PyObject { + object.getattr_cstr(name) +} + +#[inline(always)] +pub unsafe fn set_attr_cstr( + object: *mut O, + name: &CStr, + value: *mut V, +) -> c_int { + object.set_attr_cstr(name, value) +} + +#[inline(always)] +pub unsafe fn del_attr_cstr(object: *mut O, name: &CStr) -> c_int { + object.del_attr_cstr(name) +} + +#[inline(always)] +pub unsafe fn has_attr_cstr(object: *mut O, name: &CStr) -> bool { + object.has_attr_cstr(name) +} + +#[inline(always)] +pub unsafe fn call_no_args(object: *mut O) -> *mut PyObject { + object.call() +} + +#[inline(always)] +pub unsafe fn call_one_arg( + object: *mut O, + argument: *mut A, +) -> *mut PyObject { + object.call_one(argument) +} + +#[inline(always)] +pub unsafe fn call_with(object: *mut O, args: *mut A) -> *mut PyObject { + object.call_with(args) +} + +#[inline(always)] +pub unsafe fn call_with_kwargs( + object: *mut O, + args: *mut A, + kwargs: *mut K, +) -> *mut PyObject { + object.call_with_kwargs(args, kwargs) +} + +#[inline(always)] +pub unsafe fn get_iter(object: *mut O) -> *mut PyObject { + object.get_iter() +} + +#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +#[inline(always)] +pub unsafe fn iter_next_item( + iterator: *mut I, + item: &mut *mut PyObject, +) -> i32 { + ffi::PyIter_NextItem(iterator as *mut PyObject, item) +} + +#[inline(always)] +pub unsafe fn repr(object: *mut O) -> *mut PyUnicodeObject { + object.repr() +} + +#[inline(always)] +pub unsafe fn str_(object: *mut O) -> *mut PyUnicodeObject { + object.str_() +} + +pub unsafe trait PyObjectSlotPtr { + unsafe fn clear(self); +} + +unsafe impl PyObjectSlotPtr for *mut *mut T { + #[inline(always)] + unsafe fn clear(self) { + if self.is_null() { + return; + } + + let old_value = *self; + *self = ptr::null_mut(); + old_value.decref_if_nonnull(); + } +} diff --git a/src/py/seq.rs b/src/py/seq.rs new file mode 100644 index 0000000..4c5a932 --- /dev/null +++ b/src/py/seq.rs @@ -0,0 +1,120 @@ +use pyo3_ffi::*; +use std::ffi::CStr; +use std::hint::unlikely; +use std::os::raw::c_int; +use std::ptr; + +use super::{ffi, object::PyObjectPtr, PyTypeInfo}; + +#[inline(always)] +unsafe fn is_valid_index(index: Py_ssize_t, limit: Py_ssize_t) -> bool { + (index as usize) < (limit as usize) +} + +pub unsafe trait PySeqPtr: Sized { + unsafe fn length(&self) -> Py_ssize_t; + unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject; + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject); + + #[inline(always)] + unsafe fn borrow_item(self, index: Py_ssize_t) -> *mut PyObject { + if unlikely(!is_valid_index(index, self.length())) { + return ptr::null_mut(); + } + self.borrow_item_unchecked(index) + } + + #[inline(always)] + unsafe fn steal_item(self, index: Py_ssize_t, value: *mut PyObject) -> c_int { + if unlikely(!is_valid_index(index, self.length())) { + return -1; + } + self.steal_item_unchecked(index, value); + 0 + } + + #[inline(always)] + unsafe fn own_item(self, index: Py_ssize_t) -> *mut PyObject { + if unlikely(!is_valid_index(index, self.length())) { + return ptr::null_mut(); + } + self.borrow_item_unchecked(index).newref() + } + + #[inline(always)] + unsafe fn get_borrowed_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + self.borrow_item_unchecked(index) + } + + #[inline(always)] + unsafe fn set_slot_steal_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { + self.steal_item_unchecked(index, value) + } + + #[inline(always)] + unsafe fn get_owned_check_bounds(self, index: Py_ssize_t) -> *mut PyObject { + self.own_item(index) + } +} + +unsafe impl PySeqPtr for *mut PyListObject { + #[inline(always)] + unsafe fn length(&self) -> Py_ssize_t { + pyo3_ffi::PyList_GET_SIZE(*self as *mut PyObject) + } + + #[inline(always)] + unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + pyo3_ffi::PyList_GET_ITEM(self as *mut PyObject, index) + } + + #[inline(always)] + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { + pyo3_ffi::PyList_SET_ITEM(self as *mut PyObject, index, value) + } +} + +unsafe impl PySeqPtr for *mut PyTupleObject { + #[inline(always)] + unsafe fn length(&self) -> Py_ssize_t { + pyo3_ffi::PyTuple_GET_SIZE(*self as *mut PyObject) + } + + #[inline(always)] + unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + pyo3_ffi::PyTuple_GET_ITEM(self as *mut PyObject, index) + } + + #[inline(always)] + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { + pyo3_ffi::PyTuple_SET_ITEM(self as *mut PyObject, index, value) + } +} + +#[inline(always)] +pub unsafe fn fast(object: *mut T, message: &CStr) -> *mut PyObject { + ffi::PySequence_Fast(object as *mut PyObject, message.as_ptr()) +} + +#[inline(always)] +pub unsafe fn fast_borrow_item_unchecked( + fast_sequence: *mut T, + index: Py_ssize_t, +) -> *mut PyObject { + ffi::PySequence_Fast_GET_ITEM(fast_sequence as *mut PyObject, index) +} + +#[inline(always)] +pub unsafe fn fast_length(fast_sequence: *mut T) -> Py_ssize_t { + ffi::PySequence_Fast_GET_SIZE(fast_sequence as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn to_tuple(object: *mut T) -> *mut PyTupleObject { + pyo3_ffi::PySequence_Tuple(object as *mut PyObject) as *mut PyTupleObject +} + +#[inline(always)] +pub unsafe fn to_list(object: *mut T) -> *mut PyListObject { + pyo3_ffi::PySequence_List(object as *mut PyObject) as *mut PyListObject +} diff --git a/src/py/set.rs b/src/py/set.rs new file mode 100644 index 0000000..6bc423f --- /dev/null +++ b/src/py/set.rs @@ -0,0 +1,85 @@ +use pyo3_ffi::*; +use std::os::raw::c_int; +use std::ptr; + +use super::{ffi, PyFrozensetObject, PyTypeInfo}; + +pub unsafe trait PySetPtr: Sized { + unsafe fn size(self) -> Py_ssize_t; + unsafe fn next_entry( + self, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + hash: &mut Py_hash_t, + ) -> bool; + + #[inline(always)] + unsafe fn len(self) -> Py_ssize_t { + self.size() + } +} + +pub unsafe trait PyMutSetPtr: Sized { + unsafe fn add(self, item: *mut T) -> c_int; + + #[inline(always)] + unsafe fn add_item(self, item: *mut T) -> c_int { + self.add(item) + } +} + +unsafe impl PySetPtr for *mut PySetObject { + #[inline(always)] + unsafe fn size(self) -> Py_ssize_t { + pyo3_ffi::PySet_Size(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn next_entry( + self, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + hash: &mut Py_hash_t, + ) -> bool { + ffi::_PySet_NextEntry(self as *mut PyObject, position, key, hash) != 0 + } +} + +unsafe impl PySetPtr for *mut PyFrozensetObject { + #[inline(always)] + unsafe fn size(self) -> Py_ssize_t { + pyo3_ffi::PySet_Size(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn next_entry( + self, + position: &mut Py_ssize_t, + key: &mut *mut PyObject, + hash: &mut Py_hash_t, + ) -> bool { + ffi::_PySet_NextEntry(self as *mut PyObject, position, key, hash) != 0 + } +} + +unsafe impl PyMutSetPtr for *mut PySetObject { + #[inline(always)] + unsafe fn add(self, item: *mut T) -> c_int { + pyo3_ffi::PySet_Add(self as *mut PyObject, item as *mut PyObject) + } +} + +#[inline(always)] +pub unsafe fn new() -> *mut PySetObject { + pyo3_ffi::PySet_New(ptr::null_mut()) as *mut PySetObject +} + +#[inline(always)] +pub unsafe fn from(iterable: *mut T) -> *mut PySetObject { + pyo3_ffi::PySet_New(iterable as *mut PyObject) as *mut PySetObject +} + +#[inline(always)] +pub unsafe fn frozen_from(iterable: *mut T) -> *mut PyFrozensetObject { + pyo3_ffi::PyFrozenSet_New(iterable as *mut PyObject) as *mut PyFrozensetObject +} diff --git a/src/py/tuple.rs b/src/py/tuple.rs new file mode 100644 index 0000000..73476b9 --- /dev/null +++ b/src/py/tuple.rs @@ -0,0 +1,29 @@ +use pyo3_ffi::*; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +#[inline(always)] +pub unsafe fn new(length: Py_ssize_t) -> *mut PyTupleObject { + pyo3_ffi::PyTuple_New(length) as *mut PyTupleObject +} + +#[inline(always)] +pub unsafe fn size(tuple: *mut T) -> Py_ssize_t { + pyo3_ffi::PyTuple_GET_SIZE(tuple as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn get_item(tuple: *mut T, index: Py_ssize_t) -> *mut PyObject { + pyo3_ffi::PyTuple_GET_ITEM(tuple as *mut PyObject, index) +} + +#[inline(always)] +pub unsafe fn set_item(tuple: *mut T, index: Py_ssize_t, item: *mut PyObject) -> c_int { + pyo3_ffi::PyTuple_SetItem(tuple as *mut PyObject, index, item) +} + +#[inline(always)] +pub unsafe fn check(tuple: *mut T) -> bool { + pyo3_ffi::PyTuple_Check(tuple as *mut PyObject) != 0 +} diff --git a/src/py/type_object.rs b/src/py/type_object.rs new file mode 100644 index 0000000..6b90ce8 --- /dev/null +++ b/src/py/type_object.rs @@ -0,0 +1,71 @@ +use libc::c_ulong; +use pyo3_ffi::*; +use std::os::raw::c_int; + +use super::ffi; + +pub unsafe trait PyTypeObjectPtr { + unsafe fn is_literal_immutable(self) -> bool; + unsafe fn is_builtin_immutable(self) -> bool; + unsafe fn is_stdlib_immutable(self) -> bool; + unsafe fn is_type_subclass(self) -> bool; + unsafe fn is_atomic_immutable(self) -> bool; + unsafe fn is_immutable_collection(self) -> bool; +} + +unsafe impl PyTypeObjectPtr for *mut PyTypeObject { + #[inline(always)] + unsafe fn is_literal_immutable(self) -> bool { + (self == std::ptr::addr_of_mut!(ffi::_PyNone_Type)) + | (self == std::ptr::addr_of_mut!(PyLong_Type)) + | (self == std::ptr::addr_of_mut!(PyUnicode_Type)) + | (self == std::ptr::addr_of_mut!(PyBool_Type)) + | (self == std::ptr::addr_of_mut!(PyFloat_Type)) + | (self == std::ptr::addr_of_mut!(PyBytes_Type)) + } + + #[inline(always)] + unsafe fn is_builtin_immutable(self) -> bool { + (self == std::ptr::addr_of_mut!(PyRange_Type)) + | (self == std::ptr::addr_of_mut!(PyFunction_Type)) + | (self == std::ptr::addr_of_mut!(PyCFunction_Type)) + | (self == std::ptr::addr_of_mut!(ffi::PyProperty_Type)) + | (self == std::ptr::addr_of_mut!(ffi::_PyWeakref_RefType)) + | (self == std::ptr::addr_of_mut!(PyCode_Type)) + | (self == std::ptr::addr_of_mut!(ffi::_PyNotImplemented_Type)) + | (self == std::ptr::addr_of_mut!(ffi::PyEllipsis_Type)) + | (self == std::ptr::addr_of_mut!(PyComplex_Type)) + } + + #[inline(always)] + unsafe fn is_stdlib_immutable(self) -> bool { + (self == crate::py_type!("re.Pattern")) + || (self == crate::py_type!("decimal.Decimal")) + || (self == crate::py_type!("fractions.Fraction")) + } + + #[inline(always)] + unsafe fn is_type_subclass(self) -> bool { + (ffi::tp_flags_of(self) & (Py_TPFLAGS_TYPE_SUBCLASS as c_ulong)) != 0 + } + + #[inline(always)] + unsafe fn is_atomic_immutable(self) -> bool { + self.is_literal_immutable() + || self.is_builtin_immutable() + || self.is_type_subclass() + || self.is_stdlib_immutable() + } + + #[inline(always)] + unsafe fn is_immutable_collection(self) -> bool { + (self == std::ptr::addr_of_mut!(PyTuple_Type)) + | (self == std::ptr::addr_of_mut!(PyFrozenSet_Type)) + | (self == std::ptr::addr_of_mut!(PySlice_Type)) + } +} + +#[inline(always)] +pub unsafe fn ready(type_object: *mut PyTypeObject) -> c_int { + pyo3_ffi::PyType_Ready(type_object) +} diff --git a/src/py/unicode.rs b/src/py/unicode.rs new file mode 100644 index 0000000..53af6c5 --- /dev/null +++ b/src/py/unicode.rs @@ -0,0 +1,135 @@ +use pyo3_ffi::*; +use std::ffi::CStr; +use std::os::raw::c_int; + +use super::PyTypeInfo; + +pub unsafe trait PyUnicodePtr { + unsafe fn as_utf8<'a>(self) -> &'a CStr; + unsafe fn byte_length(self) -> Py_ssize_t; + unsafe fn compare_ascii(self, string: &CStr) -> i32; + unsafe fn join(self, sequence: *mut T) -> *mut PyUnicodeObject; + unsafe fn tailmatch( + self, + substring: *mut PyUnicodeObject, + start: Py_ssize_t, + end: Py_ssize_t, + direction: i32, + ) -> bool; +} + +unsafe impl PyUnicodePtr for *mut PyUnicodeObject { + #[inline(always)] + unsafe fn as_utf8<'a>(self) -> &'a CStr { + CStr::from_ptr(pyo3_ffi::PyUnicode_AsUTF8(self as *mut PyObject)) + } + + #[inline(always)] + unsafe fn byte_length(self) -> Py_ssize_t { + pyo3_ffi::PyUnicode_GET_LENGTH(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn compare_ascii(self, string: &CStr) -> i32 { + pyo3_ffi::PyUnicode_CompareWithASCIIString(self as *mut PyObject, string.as_ptr()) + } + + #[inline(always)] + unsafe fn join(self, sequence: *mut T) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_Join(self as *mut PyObject, sequence as *mut PyObject) + as *mut PyUnicodeObject + } + + #[inline(always)] + unsafe fn tailmatch( + self, + substring: *mut PyUnicodeObject, + start: Py_ssize_t, + end: Py_ssize_t, + direction: i32, + ) -> bool { + pyo3_ffi::PyUnicode_Tailmatch( + self as *mut PyObject, + substring as *mut PyObject, + start, + end, + direction as c_int, + ) != 0 + } +} + +#[inline(always)] +pub unsafe fn from_cstr(value: &CStr) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_FromString(value.as_ptr()) as *mut PyUnicodeObject +} + +#[inline(always)] +pub unsafe fn from_cstr_and_size(value: &CStr, size: Py_ssize_t) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_FromStringAndSize(value.as_ptr(), size) as *mut PyUnicodeObject +} + +#[inline(always)] +pub unsafe fn from_str_and_size(value: &str) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_FromStringAndSize(value.as_ptr().cast(), value.len() as Py_ssize_t) + as *mut PyUnicodeObject +} + +#[inline(always)] +pub unsafe fn as_utf8<'a, T: PyTypeInfo>(value: *mut T) -> &'a CStr { + CStr::from_ptr(pyo3_ffi::PyUnicode_AsUTF8(value as *mut PyObject)) +} + +#[inline(always)] +pub unsafe fn byte_length(value: *mut T) -> Py_ssize_t { + pyo3_ffi::PyUnicode_GET_LENGTH(value as *mut PyObject) +} + +#[inline(always)] +pub unsafe fn compare_ascii(value: *mut T, string: &CStr) -> i32 { + pyo3_ffi::PyUnicode_CompareWithASCIIString(value as *mut PyObject, string.as_ptr()) +} + +#[inline(always)] +pub unsafe fn join( + separator: *mut S, + sequence: *mut Q, +) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_Join(separator as *mut PyObject, sequence as *mut PyObject) + as *mut PyUnicodeObject +} + +#[inline(always)] +pub unsafe fn tailmatch( + value: *mut T, + substring: *mut S, + start: Py_ssize_t, + end: Py_ssize_t, + direction: i32, +) -> bool { + pyo3_ffi::PyUnicode_Tailmatch( + value as *mut PyObject, + substring as *mut PyObject, + start, + end, + direction as c_int, + ) != 0 +} + +macro_rules! from_format { + ($format_string:expr $(, $argument:expr )* $(,)?) => {{ + #[allow(unused_unsafe)] + unsafe { + $crate::py::ffi::PyUnicode_FromFormat( + ($format_string).as_ptr() + $(, $argument )* + ) as *mut ::pyo3_ffi::PyUnicodeObject + } + }}; +} + +pub(crate) use from_format; + +#[inline(always)] +pub unsafe fn intern(value: &CStr) -> *mut PyUnicodeObject { + pyo3_ffi::PyUnicode_InternFromString(value.as_ptr()) as *mut PyUnicodeObject +} diff --git a/src/py/vectorcall.rs b/src/py/vectorcall.rs new file mode 100644 index 0000000..23b6872 --- /dev/null +++ b/src/py/vectorcall.rs @@ -0,0 +1,36 @@ +use pyo3_ffi::*; + +use super::{ffi, PyTypeInfo}; + +pub unsafe trait PyVectorcallPtr { + unsafe fn vectorcall_function(self) -> Option; +} + +unsafe impl PyVectorcallPtr for *mut T { + #[inline(always)] + unsafe fn vectorcall_function(self) -> Option { + ffi::PyVectorcall_Function(self as *mut PyObject) + } +} + +#[inline(always)] +pub fn nargs(nargsf: usize) -> Py_ssize_t { + ffi::PyVectorcall_NARGS(nargsf) +} + +#[inline(always)] +pub unsafe fn set_function_vectorcall( + function: *mut F, + vectorcall: vectorcallfunc, +) { + #[cfg(Py_3_12)] + unsafe { + ffi::PyFunction_SetVectorcall(function as *mut PyObject, vectorcall); + } + + #[cfg(not(Py_3_12))] + { + let _ = function; + let _ = vectorcall; + } +} diff --git a/src/recursion.rs b/src/recursion.rs index 0d64642..630bae1 100644 --- a/src/recursion.rs +++ b/src/recursion.rs @@ -2,6 +2,8 @@ use core::hint::{likely, unlikely}; use pyo3_ffi::*; use std::ptr; +use crate::py; + const STACKCHECK_STRIDE: u32 = 32; const STACK_SAFETY_MARGIN: usize = 256 * 1024; @@ -124,7 +126,7 @@ pub unsafe fn enter() -> i32 { if unlikely(sp <= unsafe { STACK_LOW }) { unsafe { DEPTH -= 1; - PyErr_Format( + py::err::format!( PyExc_RecursionError, crate::cstr!("Stack overflow (depth %u) while deep copying an object"), d, diff --git a/src/reduce.rs b/src/reduce.rs index df70cc3..1809f6c 100644 --- a/src/reduce.rs +++ b/src/reduce.rs @@ -4,8 +4,8 @@ use std::ptr; use pyo3_ffi::*; use crate::deepcopy; -use crate::ffi_ext; use crate::memo::Memo; +use crate::py; use crate::py_obj; use crate::py_str; use crate::types::*; @@ -24,38 +24,30 @@ macro_rules! bail { pub(crate) unsafe fn chain_type_error(msg: *mut PyObject) { unsafe { - let mut cause_type: *mut PyObject = ptr::null_mut(); - let mut cause_val: *mut PyObject = ptr::null_mut(); - let mut cause_tb: *mut PyObject = ptr::null_mut(); - - #[allow(deprecated)] - PyErr_Fetch(&mut cause_type, &mut cause_val, &mut cause_tb); + let (mut cause_type, mut cause_val, mut cause_tb) = py::err::fetch(); if !cause_val.is_null() { - #[allow(deprecated)] - PyErr_NormalizeException(&mut cause_type, &mut cause_val, &mut cause_tb); + py::err::normalize(&mut cause_type, &mut cause_val, &mut cause_tb); } let new_exc = PyExc_TypeError.call_one(msg); msg.decref(); if new_exc.is_null() { - #[allow(deprecated)] - PyErr_Restore(cause_type, cause_val, cause_tb); + py::err::restore(cause_type, cause_val, cause_tb); return; } if !cause_val.is_null() { - PyException_SetCause(cause_val, new_exc); - #[allow(deprecated)] - PyErr_Restore(cause_type, cause_val, cause_tb); + py::err::set_cause(cause_val, new_exc); + py::err::restore(cause_type, cause_val, cause_tb); return; } cause_type.decref_nullable(); cause_tb.decref_nullable(); - PyErr_SetObject(PyExc_TypeError, new_exc); + py::err::set(PyExc_TypeError, new_exc); new_exc.decref(); } } @@ -72,7 +64,7 @@ pub(crate) unsafe fn try_reduce_via_registry( return ptr::null_mut(); } if !reducer.is_callable() { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, crate::cstr!("copyreg.dispatch_table value is not callable"), ); @@ -87,7 +79,7 @@ pub(crate) unsafe fn call_reduce_method_preferring_ex(obj: *mut PyObject) -> *mu let mut reduce_ex: *mut PyObject = ptr::null_mut(); let has = obj.get_optional_attr(py_str!("__reduce_ex__"), &mut reduce_ex); if has > 0 { - let four = PyLong_FromLong(4); + let four = py::long::from_i64(4).as_object(); let res = reduce_ex.call_one(four); four.decref(); reduce_ex.decref(); @@ -106,7 +98,7 @@ pub(crate) unsafe fn call_reduce_method_preferring_ex(obj: *mut PyObject) -> *mu if has < 0 { return ptr::null_mut(); } - PyErr_SetString( + py::err::set_string( py_obj!("copy.Error"), crate::cstr!("un(deep)copyable object (no reduce protocol)"), ); @@ -147,7 +139,7 @@ pub(crate) unsafe fn validate_reduce_tuple( if reduce_result.is_unicode() || reduce_result.is_bytes() { return (ReduceKind::String, empty); } - PyErr_SetString( + py::err::set_string( PyExc_TypeError, crate::cstr!("__reduce__ must return a tuple or str"), ); @@ -157,7 +149,7 @@ pub(crate) unsafe fn validate_reduce_tuple( let tup = reduce_result as *mut PyTupleObject; let size = tup.length(); if size < 2 || size > 5 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, crate::cstr!("tuple returned by __reduce__ must contain 2 through 5 elements"), ); @@ -166,7 +158,7 @@ pub(crate) unsafe fn validate_reduce_tuple( let callable = tup.get_borrowed_unchecked(0); let mut argtup = tup.get_borrowed_unchecked(1); - let none = ffi_ext::Py_None(); + let none = py::none(); let state_raw = if size >= 3 { tup.get_borrowed_unchecked(2) } else { @@ -184,9 +176,9 @@ pub(crate) unsafe fn validate_reduce_tuple( }; if !argtup.is_tuple() { - let coerced = PySequence_Tuple(argtup); + let coerced = py::seq::to_tuple(argtup).as_object(); if coerced.is_null() { - let msg = ffi_ext::PyUnicode_FromFormat( + let msg = py::unicode::from_format!( crate::cstr!( "second element of the tuple returned by %s.__reduce__ must be a tuple, not %.200s" ), @@ -194,7 +186,7 @@ pub(crate) unsafe fn validate_reduce_tuple( (*argtup.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg); + chain_type_error(msg.as_object()); } return (ReduceKind::Error, empty); } @@ -240,7 +232,7 @@ unsafe fn call_tp_new( match (*cls).tp_new { Some(f) => f(cls, args, kwargs), None => { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_TypeError, crate::cstr!("cannot create '%.200s' instances: tp_new is NULL"), (*cls).tp_name, @@ -256,7 +248,7 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m let tup = argtup as *mut PyTupleObject; let nargs = tup.length(); if nargs < 1 { - PyErr_SetString( + py::err::set_string( PyExc_TypeError, crate::cstr!("__newobj__ requires at least 1 argument"), ); @@ -265,7 +257,7 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m let cls = tup.get_borrowed_unchecked(0); if !cls.is_type() { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_TypeError, crate::cstr!("__newobj__ arg 1 must be a type, not %.200s"), (*cls.class()).tp_name, @@ -299,7 +291,7 @@ unsafe fn reconstruct_newobj_ex( unsafe { let tup = argtup as *mut PyTupleObject; if tup.length() != 3 { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_TypeError, crate::cstr!("__newobj_ex__ requires 3 arguments, got %zd"), tup.length(), @@ -312,7 +304,7 @@ unsafe fn reconstruct_newobj_ex( let mut kwargs = tup.get_borrowed_unchecked(2); if !cls.is_type() { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_TypeError, crate::cstr!("__newobj_ex__ arg 1 must be a type, not %.200s"), (*cls.class()).tp_name, @@ -324,9 +316,9 @@ unsafe fn reconstruct_newobj_ex( let mut coerced_kwargs: *mut PyDictObject = ptr::null_mut(); if !args.is_tuple() { - coerced_args = PySequence_Tuple(args); + coerced_args = py::seq::to_tuple(args).as_object(); if coerced_args.is_null() { - let msg = ffi_ext::PyUnicode_FromFormat( + let msg = py::unicode::from_format!( crate::cstr!( "__newobj_ex__ args in %s.__reduce__ result must be a tuple, not %.200s" ), @@ -334,7 +326,7 @@ unsafe fn reconstruct_newobj_ex( (*args.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg); + chain_type_error(msg.as_object()); } return ptr::null_mut(); } @@ -347,8 +339,8 @@ unsafe fn reconstruct_newobj_ex( coerced_args.decref_nullable(); return ptr::null_mut(); } - if coerced_kwargs.merge(kwargs, 1) < 0 { - let msg = ffi_ext::PyUnicode_FromFormat( + if coerced_kwargs.merge(kwargs, true) < 0 { + let msg = py::unicode::from_format!( crate::cstr!( "__newobj_ex__ kwargs in %s.__reduce__ result must be a dict, not %.200s" ), @@ -356,7 +348,7 @@ unsafe fn reconstruct_newobj_ex( (*kwargs.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg); + chain_type_error(msg.as_object()); } coerced_args.decref_nullable(); coerced_kwargs.decref(); @@ -476,10 +468,10 @@ unsafe fn apply_dict_state( copied.decref(); return -1; } - let ret = (instance_dict as *mut PyDictObject).merge(copied, 1); + let ret = (instance_dict as *mut PyDictObject).merge(copied, true); instance_dict.decref(); if ret < 0 { - let msg = ffi_ext::PyUnicode_FromFormat( + let msg = py::unicode::from_format!( crate::cstr!( "dict state from %s.__reduce__ must be a dict or mapping, got %.200s" ), @@ -487,7 +479,7 @@ unsafe fn apply_dict_state( (*copied.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg); + chain_type_error(msg.as_object()); } } copied.decref(); @@ -505,7 +497,7 @@ unsafe fn apply_dict_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) != 0 { + while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) { if instance_dict.setitem(key, value) < 0 { ret = -1; break; @@ -537,7 +529,7 @@ unsafe fn apply_slot_state( if !copied.is_dict() { let items_attr = copied.getattr_cstr(crate::cstr!("items")); if items_attr.is_null() { - let msg = ffi_ext::PyUnicode_FromFormat( + let msg = py::unicode::from_format!( crate::cstr!( "slot state from %s.__reduce__ must be a dict or have an items() method, got %.200s" ), @@ -545,7 +537,7 @@ unsafe fn apply_slot_state( (*copied.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg); + chain_type_error(msg.as_object()); } copied.decref(); return -1; @@ -569,16 +561,16 @@ unsafe fn apply_slot_state( if pair.is_null() { break; } - let seq = ffi_ext::PySequence_Fast(pair, crate::cstr!("items() must return pairs")); + let seq = py::seq::fast(pair, crate::cstr!("items() must return pairs")); pair.decref(); if seq.is_null() { ret = -1; break; } - if ffi_ext::PySequence_Fast_GET_SIZE(seq) != 2 { + if py::seq::fast_length(seq) != 2 { seq.decref(); - if PyErr_Occurred().is_null() { - PyErr_SetString( + if py::err::occurred().is_null() { + py::err::set_string( PyExc_ValueError, crate::cstr!("not enough values to unpack"), ); @@ -586,8 +578,8 @@ unsafe fn apply_slot_state( ret = -1; break; } - let k = ffi_ext::PySequence_Fast_GET_ITEM(seq, 0); - let v = ffi_ext::PySequence_Fast_GET_ITEM(seq, 1); + let k = py::seq::fast_borrow_item_unchecked(seq, 0); + let v = py::seq::fast_borrow_item_unchecked(seq, 1); let rc = instance.set_attr(k, v); seq.decref(); if rc < 0 { @@ -595,7 +587,7 @@ unsafe fn apply_slot_state( break; } } - if ret == 0 && !PyErr_Occurred().is_null() { + if ret == 0 && !py::err::occurred().is_null() { ret = -1; } iterator.decref(); @@ -607,7 +599,7 @@ unsafe fn apply_slot_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) != 0 { + while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) { if instance.set_attr(key, value) < 0 { ret = -1; break; @@ -687,7 +679,7 @@ unsafe fn apply_listitems( result.decref(); } - if ret == 0 && !PyErr_Occurred().is_null() { + if ret == 0 && !py::err::occurred().is_null() { ret = -1; } iterator.decref(); @@ -724,25 +716,24 @@ unsafe fn apply_dictitems( key = ptup.get_borrowed_unchecked(0).newref(); value = ptup.get_borrowed_unchecked(1).newref(); } else { - let seq = - ffi_ext::PySequence_Fast(pair, crate::cstr!("cannot unpack non-sequence")); + let seq = py::seq::fast(pair, crate::cstr!("cannot unpack non-sequence")); if seq.is_null() { pair.decref(); ret = -1; break; } - let n = ffi_ext::PySequence_Fast_GET_SIZE(seq); + let n = py::seq::fast_length(seq); if n != 2 { seq.decref(); pair.decref(); if n < 2 { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_ValueError, crate::cstr!("not enough values to unpack (expected 2, got %zd)"), n, ); } else { - ffi_ext::PyErr_Format( + py::err::format!( PyExc_ValueError, crate::cstr!("too many values to unpack (expected 2, got %zd)"), n, @@ -751,8 +742,8 @@ unsafe fn apply_dictitems( ret = -1; break; } - key = ffi_ext::PySequence_Fast_GET_ITEM(seq, 0).newref(); - value = ffi_ext::PySequence_Fast_GET_ITEM(seq, 1).newref(); + key = py::seq::fast_borrow_item_unchecked(seq, 0).newref(); + value = py::seq::fast_borrow_item_unchecked(seq, 1).newref(); seq.decref(); } pair.decref(); @@ -784,7 +775,7 @@ unsafe fn apply_dictitems( } } - if ret == 0 && !PyErr_Occurred().is_null() { + if ret == 0 && !py::err::occurred().is_null() { ret = -1; } iterator.decref(); @@ -803,7 +794,7 @@ pub unsafe fn reconstruct( unsafe { let mut reduce_result = try_reduce_via_registry(original, tp); if reduce_result.is_null() { - if !PyErr_Occurred().is_null() { + if !py::err::occurred().is_null() { return ptr::null_mut(); } reduce_result = call_reduce_method_preferring_ex(original); diff --git a/src/state.rs b/src/state.rs index e473e4f..cc18f19 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,10 @@ use pyo3_ffi::*; use std::ptr; -use crate::types::PyObjectPtr; +use crate::cstr; +use crate::py; +use crate::types::{PyObjectPtr, PySeqPtr}; +use crate::py::unicode::PyUnicodePtr; #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -42,7 +45,7 @@ pub unsafe fn init() -> i32 { unsafe { let s = std::ptr::addr_of_mut!(STATE); - (*s).sentinel = PyList_New(0); + (*s).sentinel = py::list::new(0).as_object(); if (*s).sentinel.is_null() { return -1; } @@ -57,7 +60,7 @@ unsafe fn parse_ignored_errors_from_environment() -> *mut PyObject { unsafe { let environment_value = match std::env::var("COPIUM_NO_MEMO_FALLBACK_WARNING") { Ok(value) if !value.is_empty() => value, - _ => return PyTuple_New(0), + _ => return py::tuple::new(0).as_object(), }; let ignored_error_parts: Vec<&str> = environment_value @@ -65,29 +68,22 @@ unsafe fn parse_ignored_errors_from_environment() -> *mut PyObject { .filter(|part| !part.is_empty()) .collect(); - let tuple = PyTuple_New(ignored_error_parts.len() as isize); + let tuple = py::tuple::new(ignored_error_parts.len() as isize); if tuple.is_null() { return ptr::null_mut(); } for (index, ignored_error_part) in ignored_error_parts.iter().enumerate() { - let item = PyUnicode_FromStringAndSize( - ignored_error_part.as_ptr() as *const core::ffi::c_char, - ignored_error_part.len() as isize, - ); + let item = py::unicode::from_str_and_size(ignored_error_part); if item.is_null() { tuple.decref(); return ptr::null_mut(); } - if PyTuple_SetItem(tuple, index as isize, item) < 0 { - item.decref(); - tuple.decref(); - return ptr::null_mut(); - } + tuple.steal_item_unchecked(index as isize, item.as_object()); } - tuple + tuple.as_object() } } @@ -102,13 +98,13 @@ pub unsafe fn update_suppress_warnings(new_tuple: *mut PyObject) -> i32 { (*s).ignored_errors_joined.decref_nullable(); (*s).ignored_errors_joined = ptr::null_mut(); - if PyTuple_GET_SIZE(new_tuple) > 0 { - let separator = PyUnicode_FromString(cstr!("::")); + if (new_tuple as *mut PyTupleObject).length() > 0 { + let separator = py::unicode::from_cstr(cstr!("::")); if separator.is_null() { return -1; } - (*s).ignored_errors_joined = PyUnicode_Join(separator, new_tuple); + (*s).ignored_errors_joined = separator.join(new_tuple).as_object(); separator.decref(); if (*s).ignored_errors_joined.is_null() { return -1; diff --git a/src/types.rs b/src/types.rs index f350073..5028ea1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,575 +1,57 @@ -use libc::c_ulong; -use pyo3_ffi::*; -use std::ffi::c_void; -use std::hint::unlikely; -use std::os::raw::{c_char, c_int}; -use std::ptr; - -use crate::compat; -use crate::ffi_ext; -use crate::ffi_ext::*; -use crate::memo::{Memo_Type, PyMemoObject}; -use crate::py_type; - -pub unsafe trait PyTypeInfo: Sized { - fn type_ptr() -> *mut PyTypeObject; - #[inline(always)] - fn is(tp: *mut PyTypeObject) -> bool { - tp == Self::type_ptr() - } - - #[inline(always)] - unsafe fn cast_exact(object: *mut PyObject, cls: *mut PyTypeObject) -> Option<*mut Self> { - if Self::is(cls) { - Some(object as *mut Self) - } else { - None - } - } - - #[inline(always)] - unsafe fn cast_unchecked(object: *mut PyObject) -> *mut Self { - object as *mut Self - } -} - -macro_rules! pytype { - ($($rust:ty => $ffi:ident),+ $(,)?) => {$( - unsafe impl PyTypeInfo for $rust { - #[inline(always)] - fn type_ptr() -> *mut PyTypeObject { - std::ptr::addr_of_mut!($ffi) - } - } - )+} -} -pub struct PyFrozensetObject; - -pytype! { - PyObject => PyBaseObject_Type, - PyListObject => PyList_Type, - PyTupleObject => PyTuple_Type, - PyDictObject => PyDict_Type, - PySetObject => PySet_Type, - PyByteArrayObject => PyByteArray_Type, - PyFrozensetObject => PyFrozenSet_Type, - PyMethodObject => PyMethod_Type, - PyMemoObject => Memo_Type, - PySliceObject => PySlice_Type, - PyLongObject => PyLong_Type, -} - -// ── PyObjectPtr — pointer methods on every *mut T ───────────── - -pub unsafe trait PyObjectPtr { - unsafe fn id(self) -> *mut PyLongObject; - unsafe fn as_object(self) -> *mut PyObject; - unsafe fn refcount(self) -> Py_ssize_t; - unsafe fn incref(self); - unsafe fn decref(self); - unsafe fn decref_nullable(self); - unsafe fn newref(self) -> *mut PyObject; - unsafe fn class(self) -> *mut PyTypeObject; - - unsafe fn getattr(self, name: *mut PyObject) -> *mut PyObject; - unsafe fn getattr_cstr(self, name: *const c_char) -> *mut PyObject; - unsafe fn get_optional_attr(self, name: *mut PyObject, out: &mut *mut PyObject) -> c_int; - unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int; - unsafe fn set_attr_cstr(self, name: *const c_char, value: *mut PyObject) -> c_int; - unsafe fn del_attr_cstr(self, name: *const c_char) -> c_int; - unsafe fn has_attr_cstr(self, name: *const c_char) -> c_int; - - unsafe fn call(self) -> *mut PyObject; - unsafe fn call_one(self, arg: *mut PyObject) -> *mut PyObject; - unsafe fn call_with(self, args: *mut PyObject) -> *mut PyObject; - unsafe fn call_with_kwargs(self, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject; - - unsafe fn setitem(self, key: *mut PyObject, value: *mut PyObject) -> c_int; - unsafe fn getitem(self, key: *mut PyObject) -> *mut PyObject; - unsafe fn get_iter(self) -> *mut PyObject; - unsafe fn iter_next(self) -> *mut PyObject; - - unsafe fn repr(self) -> *mut PyObject; - unsafe fn str_(self) -> *mut PyObject; - unsafe fn is_callable(self) -> bool; - - unsafe fn is_type(self) -> bool; - unsafe fn is_tuple(self) -> bool; - unsafe fn is_dict(self) -> bool; - unsafe fn is_unicode(self) -> bool; - unsafe fn is_bytes(self) -> bool; - unsafe fn is_none(self) -> bool; -} - -unsafe impl PyObjectPtr for *mut T { - #[inline(always)] - unsafe fn id(self) -> *mut PyLongObject { - PyLong_FromVoidPtr(self as *mut c_void) as _ - } - #[inline(always)] - unsafe fn as_object(self) -> *mut PyObject { - self as *mut PyObject - } - #[inline(always)] - unsafe fn refcount(self) -> Py_ssize_t { - Py_REFCNT(self as *mut PyObject) - } - #[inline(always)] - unsafe fn incref(self) { - Py_INCREF(self as *mut PyObject) - } - #[inline(always)] - unsafe fn decref(self) { - Py_DECREF(self as *mut PyObject) - } - #[inline(always)] - unsafe fn decref_nullable(self) { - if !self.is_null() { - self.decref(); - } - } - #[inline(always)] - unsafe fn newref(self) -> *mut PyObject { - Py_NewRef(self as *mut PyObject) - } - #[inline(always)] - unsafe fn class(self) -> *mut PyTypeObject { - (*(self as *mut PyObject)).ob_type - } - - #[inline(always)] - unsafe fn getattr(self, name: *mut PyObject) -> *mut PyObject { - PyObject_GetAttr(self as *mut PyObject, name) - } - #[inline(always)] - unsafe fn getattr_cstr(self, name: *const c_char) -> *mut PyObject { - PyObject_GetAttrString(self as *mut PyObject, name) - } - #[inline(always)] - unsafe fn get_optional_attr(self, name: *mut PyObject, out: &mut *mut PyObject) -> c_int { - compat::PyObject_GetOptionalAttr(self as *mut PyObject, name, out) - } - #[inline(always)] - unsafe fn set_attr(self, name: *mut PyObject, value: *mut PyObject) -> c_int { - PyObject_SetAttr(self as *mut PyObject, name, value) - } - #[inline(always)] - unsafe fn set_attr_cstr(self, name: *const c_char, value: *mut PyObject) -> c_int { - PyObject_SetAttrString(self as *mut PyObject, name, value) - } - #[inline(always)] - unsafe fn del_attr_cstr(self, name: *const c_char) -> c_int { - PyObject_SetAttrString(self as *mut PyObject, name, ptr::null_mut()) - } - #[inline(always)] - unsafe fn has_attr_cstr(self, name: *const c_char) -> c_int { - PyObject_HasAttrString(self as *mut PyObject, name) - } +pub use crate::py::ffi::PyMethodObject; +pub use crate::py::object::{PyObjectPtr, PyObjectSlotPtr}; +pub use crate::py::type_object::PyTypeObjectPtr; +pub use crate::py::{ + PyFrozensetObject, PyTypeInfo, + bytearray::PyBufPtr, + dict::PyMapPtr, + list::PyMutSeqPtr, + method::PyBoundMethodPtr, + seq::PySeqPtr, + set::{PyMutSetPtr, PySetPtr}, +}; - #[inline(always)] - unsafe fn call(self) -> *mut PyObject { - PyObject_CallNoArgs(self as *mut PyObject) - } - #[inline(always)] - unsafe fn call_one(self, arg: *mut PyObject) -> *mut PyObject { - PyObject_CallOneArg(self as *mut PyObject, arg) - } - #[inline(always)] - unsafe fn call_with(self, args: *mut PyObject) -> *mut PyObject { - PyObject_CallObject(self as *mut PyObject, args) - } - #[inline(always)] - unsafe fn call_with_kwargs(self, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject { - PyObject_Call(self as *mut PyObject, args, kwargs) - } - - #[inline(always)] - unsafe fn setitem(self, key: *mut PyObject, value: *mut PyObject) -> c_int { - PyObject_SetItem(self as *mut PyObject, key, value) - } - #[inline(always)] - unsafe fn getitem(self, key: *mut PyObject) -> *mut PyObject { - PyObject_GetItem(self as *mut PyObject, key) - } - #[inline(always)] - unsafe fn get_iter(self) -> *mut PyObject { - PyObject_GetIter(self as *mut PyObject) - } - #[inline(always)] - unsafe fn iter_next(self) -> *mut PyObject { - PyIter_Next(self as *mut PyObject) - } - - #[inline(always)] - unsafe fn repr(self) -> *mut PyObject { - PyObject_Repr(self as *mut PyObject) - } - #[inline(always)] - unsafe fn str_(self) -> *mut PyObject { - PyObject_Str(self as *mut PyObject) - } - #[inline(always)] - unsafe fn is_callable(self) -> bool { - PyCallable_Check(self as *mut PyObject) != 0 - } - - #[inline(always)] - unsafe fn is_type(self) -> bool { - (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_TYPE_SUBCLASS as c_ulong)) != 0 - } - #[inline(always)] - unsafe fn is_tuple(self) -> bool { - (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_TUPLE_SUBCLASS as c_ulong)) != 0 - } - #[inline(always)] - unsafe fn is_dict(self) -> bool { - (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_DICT_SUBCLASS as c_ulong)) != 0 - } - #[inline(always)] - unsafe fn is_unicode(self) -> bool { - (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_UNICODE_SUBCLASS as c_ulong)) != 0 - } - #[inline(always)] - unsafe fn is_bytes(self) -> bool { - (crate::ffi_ext::tp_flags_of(self.class()) & (Py_TPFLAGS_BYTES_SUBCLASS as c_ulong)) != 0 - } - #[inline(always)] - unsafe fn is_none(self) -> bool { - self as *mut PyObject == ffi_ext::Py_None() - } -} - -pub unsafe trait PyObjectSlotPtr { - unsafe fn clear(self); -} - -unsafe impl PyObjectSlotPtr for *mut *mut T { - #[inline(always)] - unsafe fn clear(self) { - if self.is_null() { - return; - } - - let old_value = *self; - *self = ptr::null_mut(); - old_value.decref_nullable(); - } -} - -// ── Constructors ─────────────────────────────────────────── +use pyo3_ffi::*; #[inline(always)] -pub unsafe fn py_list_new(n: Py_ssize_t) -> *mut PyListObject { - PyList_New(n) as _ +pub unsafe fn py_list_new(length: Py_ssize_t) -> *mut PyListObject { + crate::py::list::new(length) } #[inline(always)] -pub unsafe fn py_tuple_new(n: Py_ssize_t) -> *mut PyTupleObject { - PyTuple_New(n) as _ +pub unsafe fn py_tuple_new(length: Py_ssize_t) -> *mut PyTupleObject { + crate::py::tuple::new(length) } #[inline(always)] -pub unsafe fn py_dict_new(n: Py_ssize_t) -> *mut PyDictObject { - compat::_PyDict_NewPresized(n) as _ +pub unsafe fn py_dict_new(length: Py_ssize_t) -> *mut PyDictObject { + crate::py::dict::new_presized(length) } #[inline(always)] pub unsafe fn py_set_new() -> *mut PySetObject { - PySet_New(ptr::null_mut()) as _ + crate::py::set::new() } #[inline(always)] pub unsafe fn py_set_from(iterable: *mut T) -> *mut PySetObject { - PySet_New(iterable as _) as _ + crate::py::set::from(iterable) } #[inline(always)] pub unsafe fn frozenset_from(iterable: *mut T) -> *mut PyFrozensetObject { - PyFrozenSet_New(iterable as _) as _ + crate::py::set::frozen_from(iterable) } #[inline(always)] -pub unsafe fn py_bytearray_new(n: Py_ssize_t) -> *mut PyByteArrayObject { - PyByteArray_FromStringAndSize(ptr::null(), n) as _ +pub unsafe fn py_bytearray_new(length: Py_ssize_t) -> *mut PyByteArrayObject { + crate::py::bytearray::new(length) } #[inline(always)] -pub unsafe fn py_method_new( - func: *mut PyObject, - self_: *mut PyObject, -) -> *mut ffi_ext::PyMethodObject { - ffi_ext::PyMethod_New(func, self_) as _ -} - -// ── Indexed sequence ops (list, tuple) ───────────────────── -#[inline(always)] -unsafe fn valid_index(i: Py_ssize_t, limit: Py_ssize_t) -> bool { - (i as usize) < (limit as usize) -} -pub unsafe trait PySeqPtr: Sized { - unsafe fn length(&self) -> Py_ssize_t; - unsafe fn get_borrowed_unchecked(self, i: Py_ssize_t) -> *mut PyObject; - unsafe fn set_slot_steal_unchecked(self, i: Py_ssize_t, v: *mut PyObject); - - #[inline(always)] - unsafe fn get_owned_check_bounds(self, i: Py_ssize_t) -> *mut PyObject { - if unlikely(!valid_index(i, self.length())) { - return ptr::null_mut(); - } - self.get_borrowed_unchecked(i).newref() - } -} - -unsafe impl PySeqPtr for *mut PyListObject { - #[inline(always)] - unsafe fn length(&self) -> Py_ssize_t { - PyList_GET_SIZE(*self as _) - } - #[inline(always)] - unsafe fn get_borrowed_unchecked(self, i: Py_ssize_t) -> *mut PyObject { - PyList_GET_ITEM(self as _, i) - } - #[inline(always)] - unsafe fn set_slot_steal_unchecked(self, i: Py_ssize_t, v: *mut PyObject) { - PyList_SET_ITEM(self as _, i, v) - } -} - -unsafe impl PySeqPtr for *mut PyTupleObject { - #[inline(always)] - unsafe fn length(&self) -> Py_ssize_t { - PyTuple_GET_SIZE(*self as *mut PyObject) - } - #[inline(always)] - unsafe fn get_borrowed_unchecked(self, i: Py_ssize_t) -> *mut PyObject { - PyTuple_GET_ITEM(self as _, i) - } - #[inline(always)] - unsafe fn set_slot_steal_unchecked(self, i: Py_ssize_t, v: *mut PyObject) { - PyTuple_SET_ITEM(self as _, i, v) - } -} - -// ── Mutable sequence ops (list only) ─────────────────────── - -pub unsafe trait PyMutSeqPtr { - unsafe fn append(self, item: *mut T) -> c_int; - unsafe fn as_tuple(self) -> *mut PyTupleObject; -} - -unsafe impl PyMutSeqPtr for *mut PyListObject { - #[inline(always)] - unsafe fn append(self, item: *mut T) -> c_int { - PyList_Append(self as _, item as _) - } - #[inline(always)] - unsafe fn as_tuple(self) -> *mut PyTupleObject { - PyList_AsTuple(self as _) as _ - } -} - -// ── Mapping ops (dict) ───────────────────────────────────── - -pub unsafe trait PyMapPtr { - unsafe fn len(self) -> Py_ssize_t; - unsafe fn set_item(self, k: *mut KT, v: *mut VT) -> c_int; - unsafe fn set_item_steal_two(self, k: *mut KT, v: *mut VT) -> c_int; - /// Returns borrowed ref or null. - unsafe fn get_item(self, k: *mut PyObject) -> *mut PyObject; - unsafe fn dict_copy(self) -> *mut PyDictObject; - unsafe fn merge(self, other: *mut T, override_: c_int) -> c_int; - unsafe fn dict_next( - self, - pos: &mut Py_ssize_t, - key: &mut *mut PyObject, - value: &mut *mut PyObject, - ) -> c_int; -} - -unsafe impl PyMapPtr for *mut PyDictObject { - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - PyDict_Size(self as _) - } - #[inline(always)] - unsafe fn set_item(self, k: *mut KT, v: *mut VT) -> c_int { - PyDict_SetItem(self as _, k as _, v as _) - } - #[inline(always)] - unsafe fn set_item_steal_two(self, k: *mut KT, v: *mut VT) -> c_int { - compat::_PyDict_SetItem_Take2(self as _, k as _, v as _) - } - #[inline(always)] - unsafe fn get_item(self, k: *mut PyObject) -> *mut PyObject { - PyDict_GetItemWithError(self as _, k) - } - #[inline(always)] - unsafe fn dict_copy(self) -> *mut PyDictObject { - PyDict_Copy(self as _) as _ - } - #[inline(always)] - unsafe fn merge(self, other: *mut T, override_: c_int) -> c_int { - PyDict_Merge(self as _, other as _, override_) - } - #[inline(always)] - unsafe fn dict_next( - self, - pos: &mut Py_ssize_t, - key: &mut *mut PyObject, - value: &mut *mut PyObject, - ) -> c_int { - PyDict_Next(self as _, pos, key, value) - } -} - -pub unsafe trait PySetPtr { - unsafe fn len(self) -> Py_ssize_t; - unsafe fn next_entry( - self, - pos: &mut Py_ssize_t, - key: &mut *mut PyObject, - hash: &mut Py_hash_t, - ) -> c_int; -} - -unsafe impl PySetPtr for *mut PySetObject { - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - PySet_Size(self as _) - } - #[inline(always)] - unsafe fn next_entry( - self, - pos: &mut Py_ssize_t, - key: &mut *mut PyObject, - hash: &mut Py_hash_t, - ) -> c_int { - _PySet_NextEntry(self as _, pos, key, hash) - } -} - -unsafe impl PySetPtr for *mut PyFrozensetObject { - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - PySet_Size(self as _) - } - #[inline(always)] - unsafe fn next_entry( - self, - pos: &mut Py_ssize_t, - key: &mut *mut PyObject, - hash: &mut Py_hash_t, - ) -> c_int { - _PySet_NextEntry(self as _, pos, key, hash) - } -} - -pub unsafe trait PyMutSetPtr { - unsafe fn add_item(self, item: *mut PyObject) -> c_int; -} - -unsafe impl PyMutSetPtr for *mut PySetObject { - #[inline(always)] - unsafe fn add_item(self, item: *mut PyObject) -> c_int { - PySet_Add(self as _, item) - } -} - -// ── Buffer ops (bytearray) ───────────────────────────────── - -pub unsafe trait PyBufPtr { - unsafe fn len(self) -> Py_ssize_t; - unsafe fn as_ptr(self) -> *mut c_char; -} - -unsafe impl PyBufPtr for *mut PyByteArrayObject { - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - PyByteArray_Size(self as _) - } - #[inline(always)] - unsafe fn as_ptr(self) -> *mut c_char { - PyByteArray_AsString(self as _) - } -} - -// ── Bound method ops ─────────────────────────────────────── - -pub unsafe trait PyBoundMethodPtr { - unsafe fn function(self) -> *mut PyObject; - unsafe fn self_obj(self) -> *mut PyObject; -} - -unsafe impl PyBoundMethodPtr for *mut ffi_ext::PyMethodObject { - #[inline(always)] - unsafe fn function(self) -> *mut PyObject { - ffi_ext::PyMethod_GET_FUNCTION(self as _) - } - #[inline(always)] - unsafe fn self_obj(self) -> *mut PyObject { - ffi_ext::PyMethod_GET_SELF(self as _) - } -} - -// ── Immutability checks ──────────────────────────────────── - -pub unsafe trait PyTypeObjectPtr { - unsafe fn is_literal_immutable(self) -> bool; - unsafe fn is_builtin_immutable(self) -> bool; - unsafe fn is_stdlib_immutable(self) -> bool; - unsafe fn is_type_subclass(self) -> bool; - unsafe fn is_atomic_immutable(self) -> bool; - unsafe fn is_immutable_collection(self) -> bool; -} - -unsafe impl PyTypeObjectPtr for *mut PyTypeObject { - #[inline(always)] - unsafe fn is_literal_immutable(self) -> bool { - (self == std::ptr::addr_of_mut!(_PyNone_Type)) - | (self == std::ptr::addr_of_mut!(PyLong_Type)) - | (self == std::ptr::addr_of_mut!(PyUnicode_Type)) - | (self == std::ptr::addr_of_mut!(PyBool_Type)) - | (self == std::ptr::addr_of_mut!(PyFloat_Type)) - | (self == std::ptr::addr_of_mut!(PyBytes_Type)) - } - - #[inline(always)] - unsafe fn is_builtin_immutable(self) -> bool { - (self == std::ptr::addr_of_mut!(PyRange_Type)) - | (self == std::ptr::addr_of_mut!(PyFunction_Type)) - | (self == std::ptr::addr_of_mut!(PyCFunction_Type)) - | (self == std::ptr::addr_of_mut!(ffi_ext::PyProperty_Type)) - | (self == std::ptr::addr_of_mut!(ffi_ext::_PyWeakref_RefType)) - | (self == std::ptr::addr_of_mut!(PyCode_Type)) - | (self == std::ptr::addr_of_mut!(_PyNotImplemented_Type)) - | (self == std::ptr::addr_of_mut!(ffi_ext::PyEllipsis_Type)) - | (self == std::ptr::addr_of_mut!(PyComplex_Type)) - } - - #[inline(always)] - unsafe fn is_stdlib_immutable(self) -> bool { - (self == py_type!("re.Pattern")) - || (self == py_type!("decimal.Decimal")) - || (self == py_type!("fractions.Fraction")) - } - - #[inline(always)] - unsafe fn is_immutable_collection(self) -> bool { - (self == std::ptr::addr_of_mut!(PyTuple_Type)) - | (self == std::ptr::addr_of_mut!(PyFrozenSet_Type)) - | (self == std::ptr::addr_of_mut!(PySlice_Type)) - } - - #[inline(always)] - unsafe fn is_type_subclass(self) -> bool { - (crate::ffi_ext::tp_flags_of(self) & (Py_TPFLAGS_TYPE_SUBCLASS as c_ulong)) != 0 - } - - #[inline(always)] - unsafe fn is_atomic_immutable(self) -> bool { - self.is_literal_immutable() - || self.is_builtin_immutable() - || self.is_type_subclass() - || self.is_stdlib_immutable() - } +pub unsafe fn py_method_new( + function: *mut F, + self_object: *mut S, +) -> *mut PyMethodObject { + crate::py::method::new(function, self_object) } From 85d117a48152dd9a347369eb66f86cc0527969e6 Mon Sep 17 00:00:00 2001 From: Bobronium Date: Fri, 20 Mar 2026 14:14:31 +0400 Subject: [PATCH 03/10] Fix calling --- src/about.rs | 121 +++++++++++++----------------------------------- src/fallback.rs | 16 ++----- src/memo/any.rs | 49 +++++++------------- src/py/call.rs | 92 ++++++++++++++++++++++++++++++++---- 4 files changed, 134 insertions(+), 144 deletions(-) diff --git a/src/about.rs b/src/about.rs index 021678d..852a187 100644 --- a/src/about.rs +++ b/src/about.rs @@ -48,35 +48,17 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { return -1; } - let version_info_name = py::unicode::from_cstr(crate::cstr!("VersionInfo")); - let version_info_fields = py::list::new(6); - if version_info_name.is_null() || version_info_fields.is_null() { - version_info_name.decref_if_nonnull(); - version_info_fields.decref_if_nonnull(); - namedtuple.decref(); - module.decref(); - return -1; - } - version_info_fields.steal_item_unchecked(0, py::unicode::from_cstr(crate::cstr!("major")).as_object()); - version_info_fields.steal_item_unchecked(1, py::unicode::from_cstr(crate::cstr!("minor")).as_object()); - version_info_fields.steal_item_unchecked(2, py::unicode::from_cstr(crate::cstr!("patch")).as_object()); - version_info_fields.steal_item_unchecked(3, py::unicode::from_cstr(crate::cstr!("pre")).as_object()); - version_info_fields.steal_item_unchecked(4, py::unicode::from_cstr(crate::cstr!("dev")).as_object()); - version_info_fields.steal_item_unchecked(5, py::unicode::from_cstr(crate::cstr!("local")).as_object()); - - let version_info_args = py::tuple::new(2); - if version_info_args.is_null() { - version_info_name.decref(); - version_info_fields.decref(); - namedtuple.decref(); - module.decref(); - return -1; - } - version_info_args.steal_item_unchecked(0, version_info_name.as_object()); - version_info_args.steal_item_unchecked(1, version_info_fields.as_object()); - - let vi_cls = namedtuple.call_with(version_info_args); - version_info_args.decref(); + let vi_cls = crate::py::call::call_function!( + namedtuple, + crate::cstr!("s[ssssss]"), + crate::cstr!("VersionInfo"), + crate::cstr!("major"), + crate::cstr!("minor"), + crate::cstr!("patch"), + crate::cstr!("pre"), + crate::cstr!("dev"), + crate::cstr!("local"), + ); if vi_cls.is_null() { namedtuple.decref(); module.decref(); @@ -103,26 +85,17 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { let build_hash = env!("COPIUM_BUILD_HASH"); let local_cstring = CString::new(build_hash).unwrap(); - let version_info_value_args = py::tuple::new(6); - if version_info_value_args.is_null() { - namedtuple.decref(); - vi_cls.decref(); - module.decref(); - return -1; - } - version_info_value_args.steal_item_unchecked(0, py::long::from_i64(major).as_object()); - version_info_value_args.steal_item_unchecked(1, py::long::from_i64(minor).as_object()); - version_info_value_args.steal_item_unchecked(2, py::long::from_i64(patch).as_object()); - version_info_value_args.steal_item_unchecked(3, py::none().newref()); - version_info_value_args.steal_item_unchecked(4, py::none().newref()); - version_info_value_args.steal_item_unchecked( - 5, - py::unicode::from_cstr(local_cstring.as_c_str()).as_object(), + let vi = crate::py::call::call_function!( + vi_cls, + crate::cstr!("lllOOs"), + major, + minor, + patch, + py::none(), + py::none(), + local_cstring.as_c_str(), ); - let vi = vi_cls.call_with(version_info_value_args); - version_info_value_args.decref(); - py::module::add_object(module, crate::cstr!("VersionInfo"), vi_cls); if !vi.is_null() { py::module::add_object(module, crate::cstr!("__version_tuple__"), vi); @@ -136,55 +109,25 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { local_cstring.as_c_str(), ); - let author_name = py::unicode::from_cstr(crate::cstr!("Author")); - let author_fields = py::list::new(2); - if author_name.is_null() || author_fields.is_null() { - author_name.decref_if_nonnull(); - author_fields.decref_if_nonnull(); - namedtuple.decref(); - vi_cls.decref(); - module.decref(); - return -1; - } - author_fields.steal_item_unchecked(0, py::unicode::from_cstr(crate::cstr!("name")).as_object()); - author_fields.steal_item_unchecked(1, py::unicode::from_cstr(crate::cstr!("email")).as_object()); - - let author_type_args = py::tuple::new(2); - if author_type_args.is_null() { - author_name.decref(); - author_fields.decref(); - namedtuple.decref(); - vi_cls.decref(); - module.decref(); - return -1; - } - author_type_args.steal_item_unchecked(0, author_name.as_object()); - author_type_args.steal_item_unchecked(1, author_fields.as_object()); - - let author_cls = namedtuple.call_with(author_type_args); - author_type_args.decref(); + let author_cls = crate::py::call::call_function!( + namedtuple, + crate::cstr!("s[ss]"), + crate::cstr!("Author"), + crate::cstr!("name"), + crate::cstr!("email"), + ); namedtuple.decref(); if author_cls.is_null() { module.decref(); return -1; } - let author_value_args = py::tuple::new(2); - if author_value_args.is_null() { - author_cls.decref(); - module.decref(); - return -1; - } - author_value_args.steal_item_unchecked( - 0, - py::unicode::from_cstr(crate::cstr!("Arseny Boykov (Bobronium)")).as_object(), - ); - author_value_args.steal_item_unchecked( - 1, - py::unicode::from_cstr(crate::cstr!("hi@bobronium.me")).as_object(), + let author = crate::py::call::call_function!( + author_cls, + crate::cstr!("ss"), + crate::cstr!("Arseny Boykov (Bobronium)"), + crate::cstr!("hi@bobronium.me"), ); - let author = author_cls.call_with(author_value_args); - author_value_args.decref(); py::module::add_object(module, crate::cstr!("Author"), author_cls); if !author.is_null() { diff --git a/src/fallback.rs b/src/fallback.rs index fd0bb6a..17d2b8b 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -5,7 +5,7 @@ use crate::memo::{MemoCheckpoint, PyMemoObject}; use crate::py; use crate::py::frame::PyFramePtr; use crate::state::{OnIncompatible, STATE}; -use crate::types::{PyObjectPtr, PySeqPtr}; +use crate::types::PyObjectPtr; macro_rules! cleanup_traceback_build { ($parts:expr, $traceback_module:expr, $format_exception:expr, $traceback_lines:expr, $empty_string:expr, $caller_string:expr) => {{ @@ -243,7 +243,7 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { continue; } - filename = (code as *mut PyObject).getattr_cstr(crate::cstr!("co_filename")); + filename = (code as *mut PyObject).getattr_cstr(c"co_filename"); if filename.is_null() { py::err::clear(); } @@ -273,14 +273,7 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { break; } - let getline_args = py::tuple::new(2); - if getline_args.is_null() { - break; - } - getline_args.steal_item_unchecked(0, filename.newref()); - getline_args.steal_item_unchecked(1, line_number_object.newref()); - line = getline.call_with(getline_args); - getline_args.decref(); + line = py::call::function_obj_args!(getline, filename, line_number_object); if line.is_null() { py::err::clear(); line = py::unicode::from_cstr(crate::cstr!("")).as_object(); @@ -745,8 +738,6 @@ pub unsafe fn maybe_retry_with_dict_memo( ) -> *mut PyObject { unsafe { let mut result: *mut PyObject = ptr::null_mut(); - let (mut exception_type, mut exception_value, mut exception_traceback) = - py::err::fetch(); let mut error_identifier: *mut PyObject = ptr::null_mut(); if !py::err::matches_current(PyExc_TypeError) @@ -759,6 +750,7 @@ pub unsafe fn maybe_retry_with_dict_memo( return ptr::null_mut(); } + let (mut exception_type, mut exception_value, mut exception_traceback) = py::err::fetch(); py::err::normalize( &mut exception_type, &mut exception_value, diff --git a/src/memo/any.rs b/src/memo/any.rs index 2500663..3ece911 100644 --- a/src/memo/any.rs +++ b/src/memo/any.rs @@ -2,7 +2,7 @@ use pyo3_ffi::*; use std::ptr; use super::Memo; -use crate::types::{py_list_new, py_tuple_new, PyMutSeqPtr, PyObjectPtr, PySeqPtr, PyTypeInfo}; +use crate::types::{py_list_new, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; use crate::{py_cache, py_eval, py_str}; pub struct AnyMemo { @@ -30,28 +30,20 @@ impl AnyMemo { return -1; } - let getter = self.object.getattr(py_str!("get")); - if getter.is_null() { - pykey.decref(); - return -1; - } - let args = py_tuple_new(2); - if args.is_null() { - getter.decref(); - pykey.decref(); - return -1; - } - args.steal_item_unchecked(0, pykey.as_object()); - args.steal_item_unchecked(1, sentinel.newref()); - let existing = getter.call_with(args); - getter.decref(); - args.decref(); + let existing = crate::py::call::method_obj_args!( + self.object, + py_str!("get"), + pykey, + sentinel, + ); if existing.is_null() { + pykey.decref(); return -1; } if existing != sentinel { self.keepalive = existing as *mut PyListObject; + pykey.decref(); return 0; } @@ -88,22 +80,13 @@ impl Memo for AnyMemo { return ((), ptr::null_mut()); } - let getter = self.object.getattr(py_str!("get")); - if getter.is_null() { - pykey.decref(); - return ((), ptr::null_mut()); - } - let args = py_tuple_new(2); - if args.is_null() { - getter.decref(); - pykey.decref(); - return ((), ptr::null_mut()); - } - args.steal_item_unchecked(0, pykey.as_object()); - args.steal_item_unchecked(1, sentinel.newref()); - let found = getter.call_with(args); - getter.decref(); - args.decref(); + let found = crate::py::call::method_obj_args!( + self.object, + py_str!("get"), + pykey, + sentinel, + ); + pykey.decref(); if found.is_null() { return ((), ptr::null_mut()); diff --git a/src/py/call.rs b/src/py/call.rs index d6a1e3e..c18cc77 100644 --- a/src/py/call.rs +++ b/src/py/call.rs @@ -1,45 +1,117 @@ #![allow(unused_macros)] -macro_rules! function { +use std::ffi::CStr; +use std::os::raw::c_char; + +pub trait IntoPythonCallArgument { + type ForeignType; + + fn into_python_call_argument(self) -> Self::ForeignType; +} + +impl IntoPythonCallArgument for &CStr { + type ForeignType = *const c_char; + + #[inline(always)] + fn into_python_call_argument(self) -> Self::ForeignType { + self.as_ptr() + } +} + +impl IntoPythonCallArgument for *mut T { + type ForeignType = *mut T; + + #[inline(always)] + fn into_python_call_argument(self) -> Self::ForeignType { + self + } +} + +impl IntoPythonCallArgument for *const T { + type ForeignType = *const T; + + #[inline(always)] + fn into_python_call_argument(self) -> Self::ForeignType { + self + } +} + +macro_rules! impl_into_python_call_argument_for_numbers { + ($($number_type:ty),+ $(,)?) => { + $( + impl IntoPythonCallArgument for $number_type { + type ForeignType = $number_type; + + #[inline(always)] + fn into_python_call_argument(self) -> Self::ForeignType { + self + } + } + )+ + }; +} + +impl_into_python_call_argument_for_numbers!( + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + isize, + usize, + f32, + f64, +); + +macro_rules! call_function { ($callable:expr, $format_string:expr $(, $argument:expr )* $(,)?) => {{ #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallFunction( ($callable) as *mut ::pyo3_ffi::PyObject, ($format_string).as_ptr() - $(, $argument )* + $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* ) } }}; } -pub(crate) use function; +pub(crate) use call_function; -macro_rules! function_obj_args { +macro_rules! call_function_obj_args { ($callable:expr $(, $argument:expr )* $(,)?) => {{ #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallFunctionObjArgs( ($callable) as *mut ::pyo3_ffi::PyObject - $(, $argument )* + $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* + , std::ptr::null_mut::<::pyo3_ffi::PyObject>() ) } }}; } -pub(crate) use function_obj_args; +pub(crate) use call_function_obj_args; -macro_rules! method_obj_args { +macro_rules! call_method_obj_args { ($callable:expr, $name:expr $(, $argument:expr )* $(,)?) => {{ #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallMethodObjArgs( ($callable) as *mut ::pyo3_ffi::PyObject, - ($name) as *mut ::pyo3_ffi::PyObject - $(, $argument )* + $crate::py::call::IntoPythonCallArgument::into_python_call_argument($name) + as *mut ::pyo3_ffi::PyObject + $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* + , std::ptr::null_mut::<::pyo3_ffi::PyObject>() ) } }}; } -pub(crate) use method_obj_args; +pub(crate) use call_method_obj_args; +pub(crate) use call_function as function; +pub(crate) use call_function_obj_args as function_obj_args; +pub(crate) use call_method_obj_args as method_obj_args; From 0882c30666d749dc5154113fe50dc7ef2a13fff1 Mon Sep 17 00:00:00 2001 From: Bobronium Date: Fri, 20 Mar 2026 14:54:24 +0400 Subject: [PATCH 04/10] Replace py::none() and py::ellipsis() with NoneObject and EllipsisObject --- src/deepcopy.rs | 2 +- src/py/ffi.rs | 2 ++ src/py/mod.rs | 15 ++++++--------- src/py/object.rs | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/deepcopy.rs b/src/deepcopy.rs index 028ecb5..a5ac40a 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -153,8 +153,8 @@ impl PyDeepCopy for *mut PyListObject { let sz = self.length(); let copied = check!(py_list_new(sz)); + let ellipsis = py::EllipsisObject; for i in 0..sz { - let ellipsis = Py_Ellipsis(); #[cfg(not(any(Py_3_12, Py_3_12, Py_3_13, Py_3_14)))] ellipsis.incref(); copied.set_slot_steal_unchecked(i, ellipsis); diff --git a/src/py/ffi.rs b/src/py/ffi.rs index 17f6801..a57f5ba 100644 --- a/src/py/ffi.rs +++ b/src/py/ffi.rs @@ -18,6 +18,8 @@ extern "C" { pub static mut PyMethod_Type: PyTypeObject; pub static mut _PyNone_Type: PyTypeObject; pub static mut _PyNotImplemented_Type: PyTypeObject; + pub static mut _Py_EllipsisObject: PyObject; + pub static mut _Py_NoneStruct: PyObject; } #[repr(C)] diff --git a/src/py/mod.rs b/src/py/mod.rs index 4419f41..08c49dc 100644 --- a/src/py/mod.rs +++ b/src/py/mod.rs @@ -3,6 +3,7 @@ #![allow(unused_imports)] #![allow(unused_unsafe)] +use std::ptr::addr_of_mut; use pyo3_ffi::*; use crate::memo::{Memo_Type, PyMemoObject}; @@ -50,6 +51,7 @@ pub use tuple::*; pub use type_object::*; pub use unicode::*; pub use vectorcall::*; +use crate::py::ffi::{_Py_EllipsisObject, _Py_NoneStruct}; pub unsafe trait PyTypeInfo: Sized { fn type_ptr() -> *mut PyTypeObject; @@ -103,12 +105,7 @@ pytype! { PyCodeObject => PyCode_Type, } -#[inline(always)] -pub unsafe fn none() -> *mut PyObject { - ffi::Py_None() -} - -#[inline(always)] -pub unsafe fn ellipsis() -> *mut PyObject { - pyo3_ffi::Py_Ellipsis() -} +#[allow(non_upper_case_globals)] +pub const NoneObject: *mut PyObject = unsafe { &raw const _Py_NoneStruct as *const _ as *mut _ }; +#[allow(non_upper_case_globals)] +pub const EllipsisObject: *mut PyObject = unsafe { &raw const _Py_EllipsisObject as *const _ as *mut _ }; diff --git a/src/py/object.rs b/src/py/object.rs index 1c4c219..60a7793 100644 --- a/src/py/object.rs +++ b/src/py/object.rs @@ -3,7 +3,7 @@ use pyo3_ffi::*; use std::ffi::{c_void, CStr}; use std::os::raw::c_int; use std::ptr; - +use crate::py; use super::{ffi, PyTypeInfo}; pub unsafe trait PyObjectPtr: Sized { @@ -265,7 +265,7 @@ unsafe impl PyObjectPtr for *mut T { #[inline(always)] unsafe fn is_none(self) -> bool { - self as *mut PyObject == ffi::Py_None() + self as *mut PyObject == py::NoneObject } } From 3a500427780c8ed2129bd1f50249a90ecda703cf Mon Sep 17 00:00:00 2001 From: Bobronium Date: Fri, 20 Mar 2026 20:16:35 +0400 Subject: [PATCH 05/10] Move all CPython FFI to py crate --- src/about.rs | 18 ++++++------- src/cache.rs | 21 +++++++-------- src/compat.rs | 4 --- src/config.rs | 4 +-- src/copy.rs | 14 +++++----- src/critical_section.rs | 9 ++++--- src/deepcopy.rs | 35 ++++++++++++------------- src/dict_iter.rs | 11 +++----- src/extra.rs | 4 +-- src/fallback.rs | 9 +++---- src/ffi_ext.rs | 14 ---------- src/lib.rs | 13 +++------- src/memo/any.rs | 9 +++---- src/memo/dict.rs | 6 ++--- src/memo/mod.rs | 3 +-- src/memo/native.rs | 6 ++--- src/memo/pytype.rs | 14 +++++----- src/memo/table.rs | 3 +-- src/memo/tss.rs | 2 +- src/patch.rs | 8 +++--- src/py/call.rs | 12 ++++----- src/py/err.rs | 2 +- src/py/ffi.rs | 24 +++++++++++++++-- src/py/mod.rs | 38 ++++++++++----------------- src/py/unicode.rs | 2 +- src/recursion.rs | 3 +-- src/reduce.rs | 13 ++++------ src/state.rs | 5 +--- src/types.rs | 57 ----------------------------------------- 29 files changed, 128 insertions(+), 235 deletions(-) delete mode 100644 src/compat.rs delete mode 100644 src/ffi_ext.rs delete mode 100644 src/types.rs diff --git a/src/about.rs b/src/about.rs index 852a187..e0113ad 100644 --- a/src/about.rs +++ b/src/about.rs @@ -1,9 +1,7 @@ -use pyo3_ffi::*; use std::ffi::CString; use std::ptr; -use crate::py; -use crate::types::{PyObjectPtr, PySeqPtr}; +use crate::py::{self, *}; static mut ABOUT_METHODS: [PyMethodDef; 1] = [PyMethodDef::zeroed()]; @@ -48,7 +46,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { return -1; } - let vi_cls = crate::py::call::call_function!( + let vi_cls = py::call::call_function!( namedtuple, crate::cstr!("s[ssssss]"), crate::cstr!("VersionInfo"), @@ -85,14 +83,14 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { let build_hash = env!("COPIUM_BUILD_HASH"); let local_cstring = CString::new(build_hash).unwrap(); - let vi = crate::py::call::call_function!( + let vi = py::call::call_function!( vi_cls, crate::cstr!("lllOOs"), major, minor, patch, - py::none(), - py::none(), + py::NoneObject, + py::NoneObject, local_cstring.as_c_str(), ); @@ -101,7 +99,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { py::module::add_object(module, crate::cstr!("__version_tuple__"), vi); } - py::module::add_object(module, crate::cstr!("__commit_id__"), py::none().newref()); + py::module::add_object(module, crate::cstr!("__commit_id__"), py::NoneObject.newref()); py::module::add_string_constant( module, @@ -109,7 +107,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { local_cstring.as_c_str(), ); - let author_cls = crate::py::call::call_function!( + let author_cls = py::call::call_function!( namedtuple, crate::cstr!("s[ss]"), crate::cstr!("Author"), @@ -122,7 +120,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { return -1; } - let author = crate::py::call::call_function!( + let author = py::call::call_function!( author_cls, crate::cstr!("ss"), crate::cstr!("Arseny Boykov (Bobronium)"), diff --git a/src/cache.rs b/src/cache.rs index ef06c16..8eadb84 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -4,10 +4,7 @@ use std::mem::MaybeUninit; use std::os::raw::c_int; use std::ptr; -use pyo3_ffi::*; - -use crate::py; -use crate::types::{PyMapPtr, PyObjectPtr}; +use crate::py::{self, *}; // ── Slot types ───────────────────────────────────────────── @@ -322,10 +319,10 @@ macro_rules! __py_obj_impl { #[macro_export] macro_rules! py_obj { ($path:literal) => { - $crate::__py_obj_impl!(required, *mut ::pyo3_ffi::PyObject, $path) + $crate::__py_obj_impl!(required, *mut $crate::py::PyObject, $path) }; (? $path:literal) => { - $crate::__py_obj_impl!(optional, *mut ::pyo3_ffi::PyObject, $path) + $crate::__py_obj_impl!(optional, *mut $crate::py::PyObject, $path) }; ($T:ty, $path:literal) => { $crate::__py_obj_impl!(required, *mut $T, $path) @@ -346,9 +343,9 @@ macro_rules! py_type { if val.is_null() { return -1; } - if !$crate::types::PyObjectPtr::is_type(val) { + if !$crate::py::PyObjectPtr::is_type(val) { $crate::py::err::set_string( - ::pyo3_ffi::PyExc_TypeError, + $crate::py::PyExc_TypeError, unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked( concat!("py_type!(\"", $path, "\"): resolved to non-type\0") @@ -356,7 +353,7 @@ macro_rules! py_type { ) }, ); - $crate::types::PyObjectPtr::decref(val); + $crate::py::PyObjectPtr::decref(val); return -1; } SLOT.set(val); @@ -366,7 +363,7 @@ macro_rules! py_type { ::inventory::submit! { $crate::cache::ObjEntry { init_fn: __init } } - unsafe { SLOT.get() as *mut ::pyo3_ffi::PyTypeObject } + unsafe { SLOT.get() as *mut $crate::py::PyTypeObject } }}; } @@ -379,8 +376,8 @@ macro_rules! py_cache { #[allow(unused_unsafe)] unsafe fn __init() -> ::std::os::raw::c_int { - let val: *mut ::pyo3_ffi::PyObject = - (|| -> *mut ::pyo3_ffi::PyObject { unsafe { $($body)+ } })(); + let val: *mut $crate::py::PyObject = + (|| -> *mut $crate::py::PyObject { unsafe { $($body)+ } })(); if val.is_null() { return -1; } diff --git a/src/compat.rs b/src/compat.rs deleted file mode 100644 index 2b3e842..0000000 --- a/src/compat.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(non_snake_case)] -#![allow(unused_imports)] - -pub use crate::py::ffi::{_PyDict_NewPresized, _PyDict_SetItem_Take2, PyObject_GetOptionalAttr}; diff --git a/src/config.rs b/src/config.rs index 17e8981..f3b1cec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,11 +1,9 @@ use pyo3::exceptions::{PyRuntimeError, PyTypeError, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; -use pyo3_ffi::PyObject; -use crate::py; +use crate::py::{self, PyObject, PyObjectPtr}; use crate::state::{MemoMode, OnIncompatible, STATE}; -use crate::types::PyObjectPtr; // ══════════════════════════════════════════════════════════════ // copium.config.apply(**kwargs) diff --git a/src/copy.rs b/src/copy.rs index 823d1e0..ed93c0d 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -1,9 +1,7 @@ use crate::deepcopy::PyResult; -use crate::py; +use crate::py::{self, *}; use crate::reduce::{self, ReduceKind}; -use crate::types::*; use crate::{py_str}; -use pyo3_ffi::*; use std::os::raw::c_int; use std::ptr; @@ -50,7 +48,7 @@ impl PyCopy for *mut PyListObject { unsafe fn copy(self) -> PyResult { unsafe { let size = self.length(); - let copied = check!(py_list_new(size)); + let copied = check!(py::list::new(size)); for index in 0..size { let item = self.get_borrowed_unchecked(index); @@ -71,15 +69,15 @@ impl PyCopy for *mut PyDictObject { impl PyCopy for *mut PySetObject { unsafe fn copy(self) -> PyResult { - unsafe { PyResult::ok(check!(py_set_from(self))) } + unsafe { PyResult::ok(check!(py::set::from(self))) } } } impl PyCopy for *mut PyByteArrayObject { unsafe fn copy(self) -> PyResult { unsafe { - let size = crate::types::PyBufPtr::len(self); - let copied = check!(py_bytearray_new(size)); + let size = PyBufPtr::len(self); + let copied = check!(py::bytearray::new(size)); if size > 0 { ptr::copy_nonoverlapping(self.as_ptr(), copied.as_ptr(), size as usize); } @@ -457,7 +455,7 @@ unsafe fn copy_via_reduce(object: *mut PyObject) -> PyResult { let mut reduce_result = reduce::try_reduce_via_registry(object, class); if reduce_result.is_null() { - if !crate::py::err::occurred().is_null() { + if !py::err::occurred().is_null() { return PyResult::error(); } diff --git a/src/critical_section.rs b/src/critical_section.rs index 4a89229..51475a0 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -37,10 +37,13 @@ //! but races are possible and the state of an object may change "underneath" a suspended thread in //! possibly surprising ways. -use crate::types::PyTypeInfo; +use crate::py::PyTypeInfo; #[cfg(Py_GIL_DISABLED)] -struct CSGuard(pyo3_ffi::PyCriticalSection); +use crate::py::{PyCriticalSection, PyCriticalSection2}; + +#[cfg(Py_GIL_DISABLED)] +struct CSGuard(PyCriticalSection); #[cfg(Py_GIL_DISABLED)] impl Drop for CSGuard { @@ -52,7 +55,7 @@ impl Drop for CSGuard { } #[cfg(Py_GIL_DISABLED)] -struct CS2Guard(pyo3_ffi::PyCriticalSection2); +struct CS2Guard(PyCriticalSection2); #[cfg(Py_GIL_DISABLED)] impl Drop for CS2Guard { diff --git a/src/deepcopy.rs b/src/deepcopy.rs index a5ac40a..8d056e1 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -1,14 +1,11 @@ -use pyo3_ffi::*; use std::hint::{likely, unlikely}; use std::ptr; use crate::critical_section::with_critical_section_raw; use crate::dict_iter::DictIterGuard; use crate::memo::Memo; -use crate::py; -use crate::{ffi_ext::*, py_str}; - -use crate::types::*; +use crate::py::{self, *}; +use crate::py_str; #[repr(transparent)] #[derive(Clone, Copy)] @@ -112,7 +109,7 @@ pub unsafe fn deepcopy(object: *mut PyObject, memo: &mut M) -> PyResult if !found.is_null() { return PyResult::ok(found); } - if M::RECALL_CAN_ERROR && unlikely(!crate::py::err::occurred().is_null()) { + if M::RECALL_CAN_ERROR && unlikely(!py::err::occurred().is_null()) { return PyResult::error(); } @@ -151,11 +148,11 @@ impl PyDeepCopy for *mut PyListObject { unsafe fn deepcopy(self, memo: &mut M, probe: M::Probe) -> PyResult { unsafe { let sz = self.length(); - let copied = check!(py_list_new(sz)); + let copied = check!(py::list::new(sz)); let ellipsis = py::EllipsisObject; for i in 0..sz { - #[cfg(not(any(Py_3_12, Py_3_12, Py_3_13, Py_3_14)))] + #[cfg(not(Py_3_12))] ellipsis.incref(); copied.set_slot_steal_unchecked(i, ellipsis); } @@ -192,10 +189,10 @@ impl PyDeepCopy for *mut PyListObject { if unlikely(copied.length() != sz) { size_changed = true; } else { - #[cfg(not(any(Py_3_12, Py_3_13, Py_3_14)))] + #[cfg(not(Py_3_12))] let old_item = copied.get_borrowed_unchecked(i); copied.set_slot_steal_unchecked(i, raw); - #[cfg(not(any(Py_3_12, Py_3_13, Py_3_14)))] + #[cfg(not(Py_3_12))] old_item.decref(); } }); @@ -220,7 +217,7 @@ impl PyDeepCopy for *mut PyTupleObject { unsafe fn deepcopy(self, memo: &mut M, probe: M::Probe) -> PyResult { unsafe { let sz = self.length(); - let copied = check!(py_tuple_new(sz)); + let copied = check!(py::tuple::new(sz)); let mut all_same = true; for i in 0..sz { @@ -261,7 +258,7 @@ impl PyDeepCopy for *mut PyTupleObject { impl PyDeepCopy for *mut PyDictObject { unsafe fn deepcopy(self, memo: &mut M, probe: M::Probe) -> PyResult { unsafe { - let copied = check!(py_dict_new(self.len())); + let copied = check!(py::dict::new_presized(self.len())); if memo.memoize(self, copied, &probe) < 0 { copied.decref(); @@ -323,7 +320,7 @@ impl PyDeepCopy for *mut PySetObject { if sz < 0 { return PyResult::error(); } - let snapshot = check!(py_tuple_new(sz)); + let snapshot = check!(py::tuple::new(sz)); let mut i: Py_ssize_t = 0; with_critical_section_raw(self, || { @@ -337,7 +334,7 @@ impl PyDeepCopy for *mut PySetObject { } }); - let copied = py_set_new(); + let copied = py::set::new(); if copied.is_null() { snapshot.decref(); return PyResult::error(); @@ -390,7 +387,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { return PyResult::error(); } - let snapshot = check!(py_tuple_new(sz)); + let snapshot = check!(py::tuple::new(sz)); let mut pos: Py_ssize_t = 0; let mut item: *mut PyObject = ptr::null_mut(); @@ -402,7 +399,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { i += 1; } - let items = py_tuple_new(i); + let items = py::tuple::new(i); if items.is_null() { snapshot.decref(); return PyResult::error(); @@ -420,7 +417,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { } snapshot.decref(); - let copied = frozenset_from(items); + let copied = py::set::frozen_from(items); items.decref(); if copied.is_null() { return PyResult::error(); @@ -440,7 +437,7 @@ impl PyDeepCopy for *mut PyByteArrayObject { unsafe fn deepcopy(self, memo: &mut M, probe: M::Probe) -> PyResult { unsafe { let sz = self.len(); - let copied = check!(py_bytearray_new(sz)); + let copied = check!(py::bytearray::new(sz)); if sz > 0 { ptr::copy_nonoverlapping(self.as_ptr(), copied.as_ptr(), sz as usize); @@ -469,7 +466,7 @@ impl PyDeepCopy for *mut PyMethodObject { } let copied_self_raw = copied_self.into_raw(); - let copied = py_method_new(func, copied_self_raw); + let copied = py::method::new(func, copied_self_raw); copied_self_raw.decref(); if copied.is_null() { diff --git a/src/dict_iter.rs b/src/dict_iter.rs index f50de93..2f37d9c 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -1,13 +1,8 @@ -use pyo3_ffi::*; use std::hint::{likely, unlikely}; use std::ptr; #[cfg(Py_GIL_DISABLED)] use crate::{py_cache_typed, py_obj}; -use crate::py; -#[cfg(Py_GIL_DISABLED)] -use crate::py::vectorcall::PyVectorcallPtr; -use crate::types::PyObjectPtr; -use crate::py::dict::PyMapPtr; +use crate::py::{self, *}; #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] static mut G_DICT_WATCHER_ID: i32 = -1; @@ -110,8 +105,8 @@ pub struct DictIterGuard { impl DictIterGuard { #[cfg(all(Py_3_14, Py_GIL_DISABLED))] #[inline(always)] - fn cached_dict_items_vc() -> pyo3_ffi::vectorcallfunc { - py_cache_typed!(pyo3_ffi::vectorcallfunc, { + fn cached_dict_items_vc() -> vectorcallfunc { + py_cache_typed!(vectorcallfunc, { match py_obj!("dict.items").vectorcall_function() { Some(f) => Some(f), None => { diff --git a/src/extra.rs b/src/extra.rs index df7a409..3450f2c 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -1,9 +1,7 @@ -use pyo3_ffi::*; use std::ptr; use crate::deepcopy; -use crate::py; -use crate::types::{PyObjectPtr, PySeqPtr, PyTypeObjectPtr}; +use crate::py::{self, *}; unsafe extern "C" fn py_replicate( _self: *mut PyObject, diff --git a/src/fallback.rs b/src/fallback.rs index 17d2b8b..43dabe7 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -1,11 +1,8 @@ -use pyo3_ffi::*; use std::ptr; use crate::memo::{MemoCheckpoint, PyMemoObject}; -use crate::py; -use crate::py::frame::PyFramePtr; +use crate::py::{self, *}; use crate::state::{OnIncompatible, STATE}; -use crate::types::PyObjectPtr; macro_rules! cleanup_traceback_build { ($parts:expr, $traceback_module:expr, $format_exception:expr, $traceback_lines:expr, $empty_string:expr, $caller_string:expr) => {{ @@ -204,11 +201,11 @@ unsafe fn make_expression_with_memo(expression: *mut PyObject) -> *mut PyObject }; if let Some(prefix) = expression_text.strip_suffix(",)") { - return new_unicode_from_string(&format!("{prefix}, memo={{}})")); + return new_unicode_from_string(&std::format!("{prefix}, memo={{}})")); } if let Some(prefix) = expression_text.strip_suffix(')') { - return new_unicode_from_string(&format!("{prefix}, memo={{}})")); + return new_unicode_from_string(&std::format!("{prefix}, memo={{}})")); } ptr::null_mut() diff --git a/src/ffi_ext.rs b/src/ffi_ext.rs deleted file mode 100644 index f895e0c..0000000 --- a/src/ffi_ext.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(non_snake_case)] -#![allow(unused_imports)] - -pub use crate::py::ffi::{ - _PyNone_Type, _PyNotImplemented_Type, _PySet_NextEntry, _PyWeakref_RefType, PyEllipsis_Type, - PyErr_Format, PyMethod_GET_FUNCTION, PyMethod_GET_SELF, PyMethod_New, PyMethod_Type, - PyMethodObject, PyObject_CallFunction, PyObject_CallFunctionObjArgs, - PyObject_CallMethodObjArgs, PySequence_Fast, PySequence_Fast_GET_ITEM, - PySequence_Fast_GET_SIZE, PyUnicode_FromFormat, PyVectorcall_Function, PyVectorcall_NARGS, - Py_None, tp_flags_of, -}; - -#[cfg(Py_3_12)] -pub use crate::py::ffi::PyFunction_SetVectorcall; diff --git a/src/lib.rs b/src/lib.rs index e656dba..354604c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,16 +2,13 @@ #![feature(likely_unlikely)] use core::ffi::{c_void, CStr}; -use pyo3_ffi::*; use std::hint::{likely, unlikely}; use std::ptr; mod py; -mod ffi_ext; mod about; #[allow(dead_code)] mod cache; -mod compat; mod config; mod copy; mod critical_section; @@ -24,10 +21,8 @@ mod patch; mod recursion; mod reduce; mod state; -mod types; -use crate::py::seq::PySeqPtr; +use py::*; use crate::memo::PyMemoObject; -use crate::types::{py_dict_new, PyMapPtr, PyObjectPtr, PyTypeInfo, PyTypeObjectPtr}; use memo::{AnyMemo, DictMemo}; use state::{MemoMode, STATE}; // ══════════════════════════════════════════════════════════════ @@ -50,7 +45,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( ) -> *mut PyObject { unsafe { let mut obj: *mut PyObject = ptr::null_mut(); - let mut memo_arg: *mut PyObject = py::none(); + let mut memo_arg: *mut PyObject = py::NoneObject; // ── Fast path: no keyword arguments ───────────────── let kwcount = if kwnames.is_null() { @@ -141,7 +136,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( } // ── Dispatch based on memo type ───────────────────── - if likely(memo_arg == py::none()) { + if likely(memo_arg == py::NoneObject) { let tp = obj.class(); if tp.is_atomic_immutable() { return obj.newref(); @@ -158,7 +153,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( } // memo="dict" config - let dict = py_dict_new(0); + let dict = py::dict::new_presized(0); if dict.is_null() { return ptr::null_mut(); } diff --git a/src/memo/any.rs b/src/memo/any.rs index 3ece911..0a38b3c 100644 --- a/src/memo/any.rs +++ b/src/memo/any.rs @@ -1,8 +1,7 @@ -use pyo3_ffi::*; use std::ptr; use super::Memo; -use crate::types::{py_list_new, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; +use crate::py::{self, *}; use crate::{py_cache, py_eval, py_str}; pub struct AnyMemo { @@ -30,7 +29,7 @@ impl AnyMemo { return -1; } - let existing = crate::py::call::method_obj_args!( + let existing = py::call::method_obj_args!( self.object, py_str!("get"), pykey, @@ -49,7 +48,7 @@ impl AnyMemo { existing.decref(); - let list = py_list_new(0); + let list = py::list::new(0); if list.is_null() { pykey.decref(); return -1; @@ -80,7 +79,7 @@ impl Memo for AnyMemo { return ((), ptr::null_mut()); } - let found = crate::py::call::method_obj_args!( + let found = py::call::method_obj_args!( self.object, py_str!("get"), pykey, diff --git a/src/memo/dict.rs b/src/memo/dict.rs index 2b3a6be..5258d11 100644 --- a/src/memo/dict.rs +++ b/src/memo/dict.rs @@ -1,9 +1,7 @@ -use pyo3_ffi::*; use std::ptr; use super::Memo; -use crate::py; -use crate::types::{py_list_new, PyMapPtr, PyMutSeqPtr, PyObjectPtr, PyTypeInfo}; +use crate::py::{self, *}; pub struct DictMemo { pub dict: *mut PyDictObject, @@ -42,7 +40,7 @@ impl DictMemo { return -1; } - let list = py_list_new(0); + let list = py::list::new(0); if list.is_null() { pykey.decref(); return -1; diff --git a/src/memo/mod.rs b/src/memo/mod.rs index 47c224e..c7ec7a6 100644 --- a/src/memo/mod.rs +++ b/src/memo/mod.rs @@ -5,7 +5,6 @@ mod pytype; mod table; mod tss; -use pyo3_ffi::*; use std::ptr; pub use any::AnyMemo; @@ -14,7 +13,7 @@ pub use native::PyMemoObject; pub use pytype::{memo_ready_type, Memo_Type}; pub use table::{KeepaliveVec, MemoTable, UndoLog}; pub use tss::{cleanup_memo, get_memo, pymemo_alloc}; -use crate::types::PyTypeInfo; +use crate::py::{PyObject, PyTypeInfo}; pub type MemoCheckpoint = usize; diff --git a/src/memo/native.rs b/src/memo/native.rs index 0bf327c..254be7b 100644 --- a/src/memo/native.rs +++ b/src/memo/native.rs @@ -1,8 +1,6 @@ use super::{KeepaliveVec, Memo, MemoCheckpoint, MemoTable, UndoLog}; use crate::memo::table::{hash_pointer, TOMBSTONE}; -use crate::py; -use crate::types::{PyMapPtr, PyObjectPtr, PyTypeInfo}; -use pyo3_ffi::*; +use crate::py::{self, *}; use std::ffi::c_void; use std::hint::unlikely; use std::ptr; @@ -64,7 +62,7 @@ impl PyMemoObject { #[cold] pub unsafe fn to_dict(&self) -> *mut PyObject { unsafe { - let dict = crate::types::py_dict_new(0); + let dict = py::dict::new_presized(0); if dict.is_null() { return ptr::null_mut(); } diff --git a/src/memo/pytype.rs b/src/memo/pytype.rs index 7f6c66d..d67b2c4 100644 --- a/src/memo/pytype.rs +++ b/src/memo/pytype.rs @@ -1,12 +1,10 @@ -use pyo3_ffi::*; use std::ffi::c_void; use std::ptr; use super::native::PyMemoObject; use crate::cstr; use crate::memo::table::{hash_pointer, TOMBSTONE}; -use crate::py; -use crate::types::{PyMapPtr, PyObjectPtr, PyObjectSlotPtr}; +use crate::py::{self, *}; #[allow(non_upper_case_globals)] pub static mut Memo_Type: PyTypeObject = unsafe { std::mem::zeroed() }; @@ -178,7 +176,7 @@ unsafe extern "C" fn keepalive_list_append( return ptr::null_mut(); } (*(*self_).owner).keepalive.append(arg); - py::none().newref() + py::NoneObject.newref() } } @@ -193,7 +191,7 @@ unsafe extern "C" fn keepalive_list_clear_py( return ptr::null_mut(); } (*(*self_).owner).keepalive.clear(); - py::none().newref() + py::NoneObject.newref() } } @@ -583,7 +581,7 @@ unsafe extern "C" fn memo_py_clear(obj: *mut PyObject, _: *mut PyObject) -> *mut (*self_).dict_proxy.decref(); (*self_).dict_proxy = ptr::null_mut(); } - py::none().newref() + py::NoneObject.newref() } } @@ -620,7 +618,7 @@ unsafe extern "C" fn memo_py_get( return (*args.add(1)).newref(); } - py::none().newref() + py::NoneObject.newref() } } @@ -816,7 +814,7 @@ unsafe extern "C" fn memo_py_setdefault( return found.newref(); } - let default_value = if nargs == 2 { *args.add(1) } else { py::none() }; + let default_value = if nargs == 2 { *args.add(1) } else { py::NoneObject }; if (*self_).insert_logged(key, default_value, hash_pointer(key)) < 0 { return ptr::null_mut(); } diff --git a/src/memo/table.rs b/src/memo/table.rs index 68707d1..3eab022 100644 --- a/src/memo/table.rs +++ b/src/memo/table.rs @@ -1,8 +1,7 @@ -use pyo3_ffi::*; use std::hint::likely; use std::ptr; -use crate::types::{PyObjectPtr, PyTypeInfo}; +use crate::py::{PyObject, PyObjectPtr, PyTypeInfo}; pub(crate) const TOMBSTONE: usize = usize::MAX; diff --git a/src/memo/tss.rs b/src/memo/tss.rs index 07d4632..43eed23 100644 --- a/src/memo/tss.rs +++ b/src/memo/tss.rs @@ -5,7 +5,7 @@ use std::ptr; use super::native::PyMemoObject; use super::pytype::Memo_Type; use crate::py; -use crate::types::PyObjectPtr; +use crate::py::PyObjectPtr; #[thread_local] static mut TSS_MEMO: *mut PyMemoObject = ptr::null_mut(); diff --git a/src/patch.rs b/src/patch.rs index ab346f8..a7b3ea9 100644 --- a/src/patch.rs +++ b/src/patch.rs @@ -1,14 +1,12 @@ use pyo3::exceptions::{PyRuntimeError, PyTypeError}; use pyo3::prelude::*; use pyo3::types::PyFunction; -use pyo3_ffi::*; use std::ptr; -use crate::py; -use crate::py::vectorcall::PyVectorcallPtr; -use crate::types::PyObjectPtr; +use crate::py::{self, *}; #[inline(always)] +#[cfg(Py_3_12)] fn capsule_name() -> &'static std::ffi::CStr { crate::cstr!("copium._original_vectorcall") } @@ -129,7 +127,7 @@ unsafe fn is_patched(fn_ptr: *mut PyObject) -> bool { #[cfg(not(Py_3_12))] unsafe fn template_code() -> *mut PyObject { - use crate::types::{PyMapPtr, PyObjectPtr}; + use crate::py::{self, PyMapPtr, PyObjectPtr}; use crate::{py_cache, py_exec, py_obj, py_str}; py_cache!({ let filters = py_obj!("warnings.filters"); diff --git a/src/py/call.rs b/src/py/call.rs index c18cc77..37851be 100644 --- a/src/py/call.rs +++ b/src/py/call.rs @@ -71,7 +71,7 @@ macro_rules! call_function { #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallFunction( - ($callable) as *mut ::pyo3_ffi::PyObject, + ($callable) as *mut $crate::py::PyObject, ($format_string).as_ptr() $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* ) @@ -86,9 +86,9 @@ macro_rules! call_function_obj_args { #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallFunctionObjArgs( - ($callable) as *mut ::pyo3_ffi::PyObject + ($callable) as *mut $crate::py::PyObject $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* - , std::ptr::null_mut::<::pyo3_ffi::PyObject>() + , std::ptr::null_mut::<$crate::py::PyObject>() ) } }}; @@ -101,11 +101,11 @@ macro_rules! call_method_obj_args { #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyObject_CallMethodObjArgs( - ($callable) as *mut ::pyo3_ffi::PyObject, + ($callable) as *mut $crate::py::PyObject, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($name) - as *mut ::pyo3_ffi::PyObject + as *mut $crate::py::PyObject $(, $crate::py::call::IntoPythonCallArgument::into_python_call_argument($argument) )* - , std::ptr::null_mut::<::pyo3_ffi::PyObject>() + , std::ptr::null_mut::<$crate::py::PyObject>() ) } }}; diff --git a/src/py/err.rs b/src/py/err.rs index 0e993ec..8a63bca 100644 --- a/src/py/err.rs +++ b/src/py/err.rs @@ -19,7 +19,7 @@ macro_rules! format { #[allow(unused_unsafe)] unsafe { $crate::py::ffi::PyErr_Format( - ($exception) as *mut ::pyo3_ffi::PyObject, + ($exception) as *mut $crate::py::PyObject, ($format_string).as_ptr() $(, $argument )* ) diff --git a/src/py/ffi.rs b/src/py/ffi.rs index a57f5ba..b5a5005 100644 --- a/src/py/ffi.rs +++ b/src/py/ffi.rs @@ -5,13 +5,33 @@ use core::ffi::{c_char, CStr}; use libc::c_ulong; -use pyo3_ffi::*; use std::os::raw::c_int; #[cfg(Py_GIL_DISABLED)] use core::sync::atomic::Ordering; -pub use pyo3_ffi::{_PyWeakref_RefType, PyEllipsis_Type, PyProperty_Type, Py_None}; +pub use pyo3_ffi::{ + visitproc, vectorcallfunc, METH_FASTCALL, METH_KEYWORDS, METH_NOARGS, METH_O, + PY_SSIZE_T_MAX, PyBaseObject_Type, PyBool_Type, PyByteArrayObject, PyByteArray_Type, + PyBytes_Type, PyCFunction_Type, PyCodeObject, PyCode_Type, PyComplex_Type, PyDictObject, + PyDict_Type, PyEllipsis_Type, PyExc_AssertionError, PyExc_AttributeError, + PyExc_IndexError, PyExc_KeyError, PyExc_RecursionError, PyExc_RuntimeError, + PyExc_SystemError, PyExc_TypeError, PyExc_UserWarning, PyExc_ValueError, PyFloat_Type, + PyFrozenSet_Type, PyFunction_Type, PyListObject, PyList_Type, PyLongObject, PyLong_Type, + PyMappingMethods, PyMethodDef, PyMethodDefPointer, PyModuleDef, PyModuleDef_HEAD_INIT, + PyModuleDef_Slot, PyObject, PySequenceMethods, PySetObject, PySet_Type, PySliceObject, + PySlice_Type, PyTupleObject, PyTuple_Type, PyTypeObject, PyUnicodeObject, + PyUnicode_Type, Py_eval_input, Py_file_input, Py_hash_t, Py_mod_exec, Py_ssize_t, + Py_TPFLAGS_DEFAULT, Py_TPFLAGS_HAVE_GC, Py_None, PyProperty_Type, PyRange_Type, + _PyWeakref_RefType, +}; +use pyo3_ffi::{Py_DECREF, PyDict_SetItem, Py_SIZE, Py_TPFLAGS_LIST_SUBCLASS}; + +#[cfg(Py_GIL_DISABLED)] +pub use pyo3_ffi::{PyCriticalSection, PyCriticalSection2}; + +#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +pub use pyo3_ffi::{Py_MOD_GIL_NOT_USED, Py_mod_gil}; #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { diff --git a/src/py/mod.rs b/src/py/mod.rs index 08c49dc..c61bc8e 100644 --- a/src/py/mod.rs +++ b/src/py/mod.rs @@ -3,10 +3,8 @@ #![allow(unused_imports)] #![allow(unused_unsafe)] -use std::ptr::addr_of_mut; -use pyo3_ffi::*; - use crate::memo::{Memo_Type, PyMemoObject}; +use std::ptr::addr_of_mut; pub mod boolean; pub mod bytearray; @@ -31,27 +29,19 @@ pub mod type_object; pub mod unicode; pub mod vectorcall; -pub use boolean::*; -pub use bytearray::*; -pub use capsule::*; -pub use critical_section::*; -pub use dict::*; -pub use err::*; -pub use eval::*; -pub use frame::*; -pub use gc::*; -pub use list::*; -pub use long::*; -pub use method::*; -pub use module::*; -pub use object::*; -pub use seq::*; -pub use set::*; -pub use tuple::*; -pub use type_object::*; -pub use unicode::*; -pub use vectorcall::*; -use crate::py::ffi::{_Py_EllipsisObject, _Py_NoneStruct}; +pub use ffi::*; +pub use bytearray::PyBufPtr; +pub use capsule::PyCapsulePtr; +pub use dict::PyMapPtr; +pub use frame::PyFramePtr; +pub use list::PyMutSeqPtr; +pub use method::PyBoundMethodPtr; +pub use object::{PyObjectPtr, PyObjectSlotPtr}; +pub use seq::PySeqPtr; +pub use set::{PyMutSetPtr, PySetPtr}; +pub use type_object::PyTypeObjectPtr; +pub use unicode::PyUnicodePtr; +pub use vectorcall::PyVectorcallPtr; pub unsafe trait PyTypeInfo: Sized { fn type_ptr() -> *mut PyTypeObject; diff --git a/src/py/unicode.rs b/src/py/unicode.rs index 53af6c5..cb631d8 100644 --- a/src/py/unicode.rs +++ b/src/py/unicode.rs @@ -122,7 +122,7 @@ macro_rules! from_format { $crate::py::ffi::PyUnicode_FromFormat( ($format_string).as_ptr() $(, $argument )* - ) as *mut ::pyo3_ffi::PyUnicodeObject + ) as *mut $crate::py::PyUnicodeObject } }}; } diff --git a/src/recursion.rs b/src/recursion.rs index 630bae1..bbf707b 100644 --- a/src/recursion.rs +++ b/src/recursion.rs @@ -1,8 +1,7 @@ use core::hint::{likely, unlikely}; -use pyo3_ffi::*; use std::ptr; -use crate::py; +use crate::py::{self, *}; const STACKCHECK_STRIDE: u32 = 32; const STACK_SAFETY_MARGIN: usize = 256 * 1024; diff --git a/src/reduce.rs b/src/reduce.rs index 1809f6c..e1f2ab8 100644 --- a/src/reduce.rs +++ b/src/reduce.rs @@ -1,14 +1,11 @@ use std::os::raw::c_int; use std::ptr; -use pyo3_ffi::*; - use crate::deepcopy; use crate::memo::Memo; -use crate::py; +use crate::py::{self, *}; use crate::py_obj; use crate::py_str; -use crate::types::*; macro_rules! bail { ($e:expr) => {{ @@ -158,7 +155,7 @@ pub(crate) unsafe fn validate_reduce_tuple( let callable = tup.get_borrowed_unchecked(0); let mut argtup = tup.get_borrowed_unchecked(1); - let none = py::none(); + let none = py::NoneObject; let state_raw = if size >= 3 { tup.get_borrowed_unchecked(2) } else { @@ -265,7 +262,7 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m return ptr::null_mut(); } - let args = bail!(py_tuple_new(nargs - 1)); + let args = bail!(py::tuple::new(nargs - 1)); for i in 1..nargs { let arg = tup.get_borrowed_unchecked(i); @@ -334,7 +331,7 @@ unsafe fn reconstruct_newobj_ex( } if !kwargs.is_dict() { - coerced_kwargs = py_dict_new(0); + coerced_kwargs = py::dict::new_presized(0); if coerced_kwargs.is_null() { coerced_args.decref_nullable(); return ptr::null_mut(); @@ -393,7 +390,7 @@ unsafe fn reconstruct_callable( return callable.call(); } - let copied_args = bail!(py_tuple_new(nargs)); + let copied_args = bail!(py::tuple::new(nargs)); for i in 0..nargs { let arg = tup.get_borrowed_unchecked(i); diff --git a/src/state.rs b/src/state.rs index cc18f19..b8d7159 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,10 +1,7 @@ -use pyo3_ffi::*; use std::ptr; use crate::cstr; -use crate::py; -use crate::types::{PyObjectPtr, PySeqPtr}; -use crate::py::unicode::PyUnicodePtr; +use crate::py::{self, *}; #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u8)] diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 5028ea1..0000000 --- a/src/types.rs +++ /dev/null @@ -1,57 +0,0 @@ -pub use crate::py::ffi::PyMethodObject; -pub use crate::py::object::{PyObjectPtr, PyObjectSlotPtr}; -pub use crate::py::type_object::PyTypeObjectPtr; -pub use crate::py::{ - PyFrozensetObject, PyTypeInfo, - bytearray::PyBufPtr, - dict::PyMapPtr, - list::PyMutSeqPtr, - method::PyBoundMethodPtr, - seq::PySeqPtr, - set::{PyMutSetPtr, PySetPtr}, -}; - -use pyo3_ffi::*; - -#[inline(always)] -pub unsafe fn py_list_new(length: Py_ssize_t) -> *mut PyListObject { - crate::py::list::new(length) -} - -#[inline(always)] -pub unsafe fn py_tuple_new(length: Py_ssize_t) -> *mut PyTupleObject { - crate::py::tuple::new(length) -} - -#[inline(always)] -pub unsafe fn py_dict_new(length: Py_ssize_t) -> *mut PyDictObject { - crate::py::dict::new_presized(length) -} - -#[inline(always)] -pub unsafe fn py_set_new() -> *mut PySetObject { - crate::py::set::new() -} - -#[inline(always)] -pub unsafe fn py_set_from(iterable: *mut T) -> *mut PySetObject { - crate::py::set::from(iterable) -} - -#[inline(always)] -pub unsafe fn frozenset_from(iterable: *mut T) -> *mut PyFrozensetObject { - crate::py::set::frozen_from(iterable) -} - -#[inline(always)] -pub unsafe fn py_bytearray_new(length: Py_ssize_t) -> *mut PyByteArrayObject { - crate::py::bytearray::new(length) -} - -#[inline(always)] -pub unsafe fn py_method_new( - function: *mut F, - self_object: *mut S, -) -> *mut PyMethodObject { - crate::py::method::new(function, self_object) -} From c79df4aa7f84dfa9d2951b65b9bd1bfee97136f5 Mon Sep 17 00:00:00 2001 From: Bobronium Date: Sat, 21 Mar 2026 01:48:25 +0400 Subject: [PATCH 06/10] Fix free-threading build --- src/critical_section.rs | 2 +- src/dict_iter.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/critical_section.rs b/src/critical_section.rs index 51475a0..583bf72 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -37,7 +37,7 @@ //! but races are possible and the state of an object may change "underneath" a suspended thread in //! possibly surprising ways. -use crate::py::PyTypeInfo; +use crate::py::{self, PyTypeInfo}; #[cfg(Py_GIL_DISABLED)] use crate::py::{PyCriticalSection, PyCriticalSection2}; diff --git a/src/dict_iter.rs b/src/dict_iter.rs index 2f37d9c..cb65ba0 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -169,8 +169,8 @@ impl DictIterGuard { #[cfg(all(Py_3_14, Py_GIL_DISABLED))] unsafe { Self { - dict, - it: Self::new_dict_items_iterator(dict), + dict: dict as *mut PyObject, + it: Self::new_dict_items_iterator(dict as *mut PyObject), active: true, } } From 4ccfd4d49fd880a34760b9252f3633cf057420b6 Mon Sep 17 00:00:00 2001 From: Bobronium Date: Sat, 21 Mar 2026 02:02:50 +0400 Subject: [PATCH 07/10] Move critical section to py crate --- src/critical_section.rs | 85 -------------------------------------- src/deepcopy.rs | 5 +-- src/lib.rs | 1 - src/py/critical_section.rs | 37 +++++++++++++---- src/py/ffi.rs | 3 -- 5 files changed, 30 insertions(+), 101 deletions(-) delete mode 100644 src/critical_section.rs diff --git a/src/critical_section.rs b/src/critical_section.rs deleted file mode 100644 index 583bf72..0000000 --- a/src/critical_section.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Wrappers for the Python critical section API -//! -//! [Critical Sections](https://docs.python.org/3/c-api/init.html#python-critical-section-api) allow -//! access to the [`PyMutex`](https://docs.python.org/3/c-api/init.html#c.PyMutex) lock attached to -//! each Python object in the free-threaded build. They are no-ops on the GIL-enabled build. -//! -//! Provides weaker locking guarantees than traditional locks, but can in some cases be used to -//! provide guarantees similar to the GIL without the risk of deadlocks associated with traditional -//! locks. -//! -//! # Usage Notes -//! -//! The calling thread locks the per-object mutex when it enters the critical section and holds it -//! until exiting the critical section unless the critical section is suspended. Any call into the -//! CPython C API may cause the critical section to be suspended. Creating an inner critical -//! section, for example by accessing an item in a Python list or dict, will cause the outer -//! critical section to be relased while the inner critical section is active. -//! -//! As a consequence, it is only possible to lock one or two objects at a time. If you need two lock -//! two objects, you should use the variants that accept two arguments. The outer critical section -//! is suspended if you create an outer an inner critical section on two objects using the -//! single-argument variants. -//! -//! It is not currently possible to lock more than two objects simultaneously using this mechanism. -//! Taking a critical section on a container object does not lock the objects stored in the -//! container. -//! -//! Many CPython C API functions do not lock the per-object mutex on objects passed to Python. You -//! should not expect critical sections applied to built-in types to prevent concurrent -//! modification. This API is most useful for user-defined types with full control over how the -//! internal state for the type is managed. -//! -//! The caller must ensure the closure cannot implicitly release the critical section. If a -//! multithreaded program calls back into the Python interpreter in a manner that would cause the -//! critical section to be released, the per-object mutex will be unlocked and the state of the -//! object may be read from or modified by another thread. Concurrent modifications are impossible, -//! but races are possible and the state of an object may change "underneath" a suspended thread in -//! possibly surprising ways. - -use crate::py::{self, PyTypeInfo}; - -#[cfg(Py_GIL_DISABLED)] -use crate::py::{PyCriticalSection, PyCriticalSection2}; - -#[cfg(Py_GIL_DISABLED)] -struct CSGuard(PyCriticalSection); - -#[cfg(Py_GIL_DISABLED)] -impl Drop for CSGuard { - fn drop(&mut self) { - unsafe { - py::critical_section::end(&mut self.0); - } - } -} - -#[cfg(Py_GIL_DISABLED)] -struct CS2Guard(PyCriticalSection2); - -#[cfg(Py_GIL_DISABLED)] -impl Drop for CS2Guard { - fn drop(&mut self) { - unsafe { - py::critical_section::end2(&mut self.0); - } - } -} - -#[inline(always)] -pub fn with_critical_section_raw(object: *mut T, f: F) -> R -where - F: FnOnce() -> R, -{ - #[cfg(Py_GIL_DISABLED)] - { - let mut guard = CSGuard(unsafe { std::mem::zeroed() }); - unsafe { py::critical_section::begin(&mut guard.0, object) }; - f() - } - #[cfg(not(Py_GIL_DISABLED))] - { - let _ = object; - f() - } -} diff --git a/src/deepcopy.rs b/src/deepcopy.rs index 8d056e1..d50d4fa 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -1,7 +1,6 @@ use std::hint::{likely, unlikely}; use std::ptr; -use crate::critical_section::with_critical_section_raw; use crate::dict_iter::DictIterGuard; use crate::memo::Memo; use crate::py::{self, *}; @@ -185,7 +184,7 @@ impl PyDeepCopy for *mut PyListObject { let raw = item_copy.into_raw(); let mut size_changed = false; - with_critical_section_raw(copied, || { + py::critical_section::enter(copied, || { if unlikely(copied.length() != sz) { size_changed = true; } else { @@ -323,7 +322,7 @@ impl PyDeepCopy for *mut PySetObject { let snapshot = check!(py::tuple::new(sz)); let mut i: Py_ssize_t = 0; - with_critical_section_raw(self, || { + py::critical_section::enter(self, || { let mut pos: Py_ssize_t = 0; let mut item: *mut PyObject = ptr::null_mut(); let mut hash: Py_hash_t = 0; diff --git a/src/lib.rs b/src/lib.rs index 354604c..3e54877 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ mod about; mod cache; mod config; mod copy; -mod critical_section; mod deepcopy; mod dict_iter; mod extra; diff --git a/src/py/critical_section.rs b/src/py/critical_section.rs index cedce12..d2cbfa5 100644 --- a/src/py/critical_section.rs +++ b/src/py/critical_section.rs @@ -1,6 +1,4 @@ -use pyo3_ffi::*; - -use super::PyTypeInfo; +use super::{PyObject, PyTypeInfo}; #[cfg(Py_GIL_DISABLED)] #[inline(always)] @@ -8,6 +6,9 @@ pub unsafe fn begin(critical_section: &mut PyCriticalSection, obj pyo3_ffi::PyCriticalSection_Begin(critical_section, object as *mut PyObject) } +#[cfg(Py_GIL_DISABLED)] +pub use pyo3_ffi::{PyCriticalSection, PyCriticalSection2}; + #[cfg(not(Py_GIL_DISABLED))] #[inline(always)] pub unsafe fn begin(critical_section: &mut (), object: *mut T) { @@ -28,13 +29,31 @@ pub unsafe fn end(critical_section: &mut ()) { } #[cfg(Py_GIL_DISABLED)] -#[inline(always)] -pub unsafe fn end2(critical_section: &mut PyCriticalSection2) { - pyo3_ffi::PyCriticalSection2_End(critical_section) +struct CriticalSectionGuard(PyCriticalSection); + +#[cfg(Py_GIL_DISABLED)] +impl Drop for CriticalSectionGuard { + fn drop(&mut self) { + unsafe { + end(&mut self.0); + } + } } -#[cfg(not(Py_GIL_DISABLED))] #[inline(always)] -pub unsafe fn end2(critical_section: &mut ()) { - let _ = critical_section; +pub fn enter(object: *mut T, f: F) -> R +where + F: FnOnce() -> R, +{ + #[cfg(Py_GIL_DISABLED)] + { + let mut guard = CriticalSectionGuard(unsafe { std::mem::zeroed() }); + unsafe { begin(&mut guard.0, object) }; + f() + } + #[cfg(not(Py_GIL_DISABLED))] + { + let _ = object; + f() + } } diff --git a/src/py/ffi.rs b/src/py/ffi.rs index b5a5005..f456db8 100644 --- a/src/py/ffi.rs +++ b/src/py/ffi.rs @@ -27,9 +27,6 @@ pub use pyo3_ffi::{ }; use pyo3_ffi::{Py_DECREF, PyDict_SetItem, Py_SIZE, Py_TPFLAGS_LIST_SUBCLASS}; -#[cfg(Py_GIL_DISABLED)] -pub use pyo3_ffi::{PyCriticalSection, PyCriticalSection2}; - #[cfg(all(Py_3_14, Py_GIL_DISABLED))] pub use pyo3_ffi::{Py_MOD_GIL_NOT_USED, Py_mod_gil}; From d348e0d8ad654bb7e2c76b9a0c1d58178582a65e Mon Sep 17 00:00:00 2001 From: Bobronium Date: Sat, 21 Mar 2026 02:03:02 +0400 Subject: [PATCH 08/10] Fix dict-iter on free-threading --- src/dict_iter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dict_iter.rs b/src/dict_iter.rs index cb65ba0..929f377 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -68,7 +68,7 @@ unsafe fn dict_used(dict: *mut PyObject) -> Py_ssize_t { } pub struct DictIterGuard { - #[cfg(any(not(Py_3_14), Py_3_14))] + #[cfg(any(not(Py_3_14), all(Py_3_14, not(Py_GIL_DISABLED))))] dict: *mut PyObject, #[cfg(any(not(Py_3_14), all(Py_3_14, not(Py_GIL_DISABLED))))] @@ -169,7 +169,6 @@ impl DictIterGuard { #[cfg(all(Py_3_14, Py_GIL_DISABLED))] unsafe { Self { - dict: dict as *mut PyObject, it: Self::new_dict_items_iterator(dict as *mut PyObject), active: true, } From ae107e1937bc09ec12dd738d1aea9e095854f650 Mon Sep 17 00:00:00 2001 From: Bobronium Date: Sat, 21 Mar 2026 03:40:39 +0400 Subject: [PATCH 09/10] Move free-function usage to traits --- src/about.rs | 22 ++-- src/cache.rs | 8 +- src/config.rs | 19 ++-- src/copy.rs | 97 ++++++++++++----- src/deepcopy.rs | 2 +- src/dict_iter.rs | 67 +++++++----- src/extra.rs | 26 +++-- src/fallback.rs | 241 ++++++++++++++++++++++-------------------- src/lib.rs | 58 +++++----- src/memo/any.rs | 16 ++- src/memo/dict.rs | 13 ++- src/memo/native.rs | 23 ++-- src/memo/pytype.rs | 112 ++++++++++---------- src/patch.rs | 37 +++---- src/py/ffi.rs | 14 +-- src/py/long.rs | 12 ++- src/py/mod.rs | 9 +- src/py/module.rs | 33 +++++- src/py/object.rs | 85 +++++++++++---- src/py/seq.rs | 14 +-- src/py/type_object.rs | 8 +- src/py/unicode.rs | 31 +++--- src/py/vectorcall.rs | 32 +++--- src/reduce.rs | 239 ++++++++++++++++++++++++----------------- src/state.rs | 22 ++-- 25 files changed, 728 insertions(+), 512 deletions(-) diff --git a/src/about.rs b/src/about.rs index e0113ad..2661384 100644 --- a/src/about.rs +++ b/src/about.rs @@ -28,11 +28,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { let version = env!("CARGO_PKG_VERSION"); let version_cstring = CString::new(version).unwrap(); - py::module::add_string_constant( - module, - crate::cstr!("__version__"), - version_cstring.as_c_str(), - ); + module.add_module_string_constant(crate::cstr!("__version__"), version_cstring.as_c_str()); let collections = py::module::import(crate::cstr!("collections")); if collections.is_null() { @@ -94,18 +90,14 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { local_cstring.as_c_str(), ); - py::module::add_object(module, crate::cstr!("VersionInfo"), vi_cls); + module.add_module_object(crate::cstr!("VersionInfo"), vi_cls); if !vi.is_null() { - py::module::add_object(module, crate::cstr!("__version_tuple__"), vi); + module.add_module_object(crate::cstr!("__version_tuple__"), vi); } - py::module::add_object(module, crate::cstr!("__commit_id__"), py::NoneObject.newref()); + module.add_module_object(crate::cstr!("__commit_id__"), py::NoneObject.newref()); - py::module::add_string_constant( - module, - crate::cstr!("__build_hash__"), - local_cstring.as_c_str(), - ); + module.add_module_string_constant(crate::cstr!("__build_hash__"), local_cstring.as_c_str()); let author_cls = py::call::call_function!( namedtuple, @@ -126,7 +118,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { crate::cstr!("Arseny Boykov (Bobronium)"), crate::cstr!("hi@bobronium.me"), ); - py::module::add_object(module, crate::cstr!("Author"), author_cls); + module.add_module_object(crate::cstr!("Author"), author_cls); if !author.is_null() { let authors = py::tuple::new(1); @@ -135,7 +127,7 @@ pub unsafe fn create_module(parent: *mut PyObject) -> i32 { return -1; } authors.steal_item_unchecked(0, author); - py::module::add_object(module, crate::cstr!("__authors__"), authors); + module.add_module_object(crate::cstr!("__authors__"), authors); } crate::add_submodule(parent, crate::cstr!("__about__"), module) diff --git a/src/cache.rs b/src/cache.rs index 8eadb84..70285b9 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -68,7 +68,7 @@ init_phase!(StrEntry, ObjEntry, CacheEntry); // ── Primitives ───────────────────────────────────────────── pub unsafe fn intern_str(string: &CStr) -> *mut PyObject { - unsafe { py::unicode::intern(string).as_object() } + unsafe { py::unicode::intern(string).cast() } } /// Resolve a dotted path like "decimal.Decimal" or "xml.etree.ElementTree.Element". @@ -161,7 +161,7 @@ pub unsafe fn resolve_path_optional(path: &str) -> *mut PyObject { result } -unsafe fn make_globals() -> *mut PyObject { +unsafe fn make_globals() -> *mut PyDictObject { unsafe { let globals = py::dict::new(); if globals.is_null() { @@ -174,7 +174,7 @@ unsafe fn make_globals() -> *mut PyObject { globals.decref(); return ptr::null_mut(); } - globals.as_object() + globals } } @@ -202,7 +202,7 @@ pub unsafe fn exec_cstr(code: &CStr) -> *mut PyDictObject { return ptr::null_mut(); } result.decref(); - globals as *mut PyDictObject + globals } } diff --git a/src/config.rs b/src/config.rs index f3b1cec..c260659 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,7 @@ use pyo3::exceptions::{PyRuntimeError, PyTypeError, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; -use crate::py::{self, PyObject, PyObjectPtr}; +use crate::py::{self, PyObject, PyObjectPtr, PySeqPtr}; use crate::state::{MemoMode, OnIncompatible, STATE}; // ══════════════════════════════════════════════════════════════ @@ -111,18 +111,18 @@ fn apply( if let Some(suppress_warnings_object) = suppress_warnings { unsafe { let new_tuple = if suppress_warnings_object.is_none() { - py::tuple::new(0).as_object() + py::tuple::new(0) } else { - py::seq::to_tuple(suppress_warnings_object.as_ptr()).as_object() + suppress_warnings_object.as_ptr().sequence_to_tuple() }; if new_tuple.is_null() { return Err(PyErr::take(py) .unwrap_or_else(|| PyRuntimeError::new_err("PySequence_Tuple failed"))); } - let suppress_warning_count = py::tuple::size(new_tuple); + let suppress_warning_count = new_tuple.length(); for index in 0..suppress_warning_count { - let item = py::tuple::get_item(new_tuple, index); + let item = new_tuple.get_borrowed_unchecked(index); if !item.is_unicode() { let item_type_name = Bound::::from_borrowed_ptr(py, item) .get_type() @@ -173,14 +173,15 @@ fn get(py: Python<'_>) -> PyResult> { }, )?; - let sw = unsafe { + let suppress_warnings_tuple = unsafe { if !ignored_errors.is_null() { - ignored_errors.newref() + ignored_errors.newref().cast() } else { - py::tuple::new(0).as_object() + py::tuple::new(0) } }; - let sw_obj = unsafe { Bound::from_owned_ptr(py, sw) }.cast_into::()?; + let sw_obj = unsafe { Bound::from_owned_ptr(py, suppress_warnings_tuple.cast()) } + .cast_into::()?; dict.set_item("suppress_warnings", sw_obj)?; Ok(dict) diff --git a/src/copy.rs b/src/copy.rs index ed93c0d..10f4a9c 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -1,7 +1,7 @@ use crate::deepcopy::PyResult; use crate::py::{self, *}; +use crate::py_str; use crate::reduce::{self, ReduceKind}; -use crate::{py_str}; use std::os::raw::c_int; use std::ptr; @@ -110,10 +110,10 @@ impl PyCopy for *mut PyObject { unsafe fn reconstruct_shallow_instance( callable: *mut PyObject, - arguments: *mut PyObject, + arguments: *mut PyTupleObject, ) -> *mut PyObject { unsafe { - if (arguments as *mut PyTupleObject).length() == 0 { + if arguments.length() == 0 { callable.call() } else { callable.call_with(arguments) @@ -153,7 +153,8 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - return -1; } - let result = (instance_dict as *mut PyDictObject).merge(dict_state, true); + let instance_dictionary = PyDictObject::cast_unchecked(instance_dict); + let result = instance_dictionary.merge(dict_state, true); instance_dict.decref(); if result < 0 { let message = py::unicode::from_format!( @@ -164,12 +165,13 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - (*dict_state.class()).tp_name, ); if !message.is_null() { - reduce::chain_type_error(message.as_object()); + reduce::chain_typed_type_error(message); } } return result; } + let dict_state_dictionary = PyDictObject::cast_unchecked(dict_state); let instance_dict = instance.getattr(py_str!("__dict__")); if instance_dict.is_null() { return -1; @@ -180,8 +182,7 @@ unsafe fn apply_dict_state(instance: *mut PyObject, dict_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while (dict_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) - { + while dict_state_dictionary.dict_next(&mut position, &mut key, &mut value) { if instance_dict.setitem(key, value) < 0 { result = -1; break; @@ -210,7 +211,7 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - (*slot_state.class()).tp_name, ); if !message.is_null() { - reduce::chain_type_error(message.as_object()); + reduce::chain_typed_type_error(message); } return -1; } @@ -234,14 +235,14 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - break; } - let sequence = py::seq::fast(pair, crate::cstr!("items() must return pairs")); + let sequence = pair.fast_sequence(crate::cstr!("items() must return pairs")); pair.decref(); if sequence.is_null() { result = -1; break; } - if py::seq::fast_length(sequence) != 2 { + if sequence.fast_sequence_length() != 2 { sequence.decref(); if py::err::occurred().is_null() { py::err::set_string( @@ -253,8 +254,8 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - break; } - let key = py::seq::fast_borrow_item_unchecked(sequence, 0); - let value = py::seq::fast_borrow_item_unchecked(sequence, 1); + let key = sequence.fast_sequence_item_unchecked(0); + let value = sequence.fast_sequence_item_unchecked(1); let set_result = instance.set_attr(key, value); sequence.decref(); if set_result < 0 { @@ -275,8 +276,9 @@ unsafe fn apply_slot_state(instance: *mut PyObject, slot_state: *mut PyObject) - let mut position: Py_ssize_t = 0; let mut result: c_int = 0; - while (slot_state as *mut PyDictObject).dict_next(&mut position, &mut key, &mut value) - { + let slot_state_dictionary = PyDictObject::cast_unchecked(slot_state); + + while slot_state_dictionary.dict_next(&mut position, &mut key, &mut value) { if instance.set_attr(key, value) < 0 { result = -1; break; @@ -292,10 +294,12 @@ unsafe fn apply_state_tuple(instance: *mut PyObject, state: *mut PyObject) -> c_ let mut dict_state = state; let mut slot_state: *mut PyObject = ptr::null_mut(); - if state.is_tuple() && (state as *mut PyTupleObject).length() == 2 { - let tuple = state as *mut PyTupleObject; - dict_state = tuple.get_borrowed_unchecked(0); - slot_state = tuple.get_borrowed_unchecked(1); + if state.is_tuple() { + let tuple = PyTupleObject::cast_unchecked(state); + if tuple.length() == 2 { + dict_state = tuple.get_borrowed_unchecked(0); + slot_state = tuple.get_borrowed_unchecked(1); + } } if apply_dict_state(instance, dict_state) < 0 { @@ -365,21 +369,56 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> break; } - let (key, value) = if pair.is_tuple() && (pair as *mut PyTupleObject).length() == 2 { - let pair_tuple = pair as *mut PyTupleObject; - ( - pair_tuple.get_borrowed_unchecked(0).newref(), - pair_tuple.get_borrowed_unchecked(1).newref(), - ) + let (key, value) = if pair.is_tuple() { + let pair_tuple = PyTupleObject::cast_unchecked(pair); + if pair_tuple.length() == 2 { + ( + pair_tuple.get_borrowed_unchecked(0).newref(), + pair_tuple.get_borrowed_unchecked(1).newref(), + ) + } else { + let sequence = pair.fast_sequence(crate::cstr!("cannot unpack non-sequence")); + if sequence.is_null() { + pair.decref(); + result = -1; + break; + } + + let item_count = sequence.fast_sequence_length(); + if item_count != 2 { + sequence.decref(); + pair.decref(); + if item_count < 2 { + py::err::format!( + PyExc_ValueError, + crate::cstr!("not enough values to unpack (expected 2, got %zd)"), + item_count, + ); + } else { + py::err::format!( + PyExc_ValueError, + crate::cstr!("too many values to unpack (expected 2, got %zd)"), + item_count, + ); + } + result = -1; + break; + } + + let key = sequence.fast_sequence_item_unchecked(0).newref(); + let value = sequence.fast_sequence_item_unchecked(1).newref(); + sequence.decref(); + (key, value) + } } else { - let sequence = py::seq::fast(pair, crate::cstr!("cannot unpack non-sequence")); + let sequence = pair.fast_sequence(crate::cstr!("cannot unpack non-sequence")); if sequence.is_null() { pair.decref(); result = -1; break; } - let item_count = py::seq::fast_length(sequence); + let item_count = sequence.fast_sequence_length(); if item_count != 2 { sequence.decref(); pair.decref(); @@ -400,8 +439,8 @@ unsafe fn apply_dictitems(instance: *mut PyObject, dictitems: *mut PyObject) -> break; } - let key = py::seq::fast_borrow_item_unchecked(sequence, 0).newref(); - let value = py::seq::fast_borrow_item_unchecked(sequence, 1).newref(); + let key = sequence.fast_sequence_item_unchecked(0).newref(); + let value = sequence.fast_sequence_item_unchecked(1).newref(); sequence.decref(); (key, value) }; @@ -477,7 +516,7 @@ unsafe fn copy_via_reduce(object: *mut PyObject) -> PyResult { PyResult::ok(object.newref()) } ReduceKind::Tuple => { - let copied = reconstruct_shallow_instance(parts.callable, parts.argtup); + let copied = reconstruct_shallow_instance(parts.callable, parts.argument_tuple); if copied.is_null() { reduce_result.decref(); return PyResult::error(); diff --git a/src/deepcopy.rs b/src/deepcopy.rs index d50d4fa..56fa1e9 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -13,7 +13,7 @@ pub struct PyResult(pub *mut PyObject); impl PyResult { #[inline(always)] pub fn ok(p: *mut T) -> Self { - unsafe { Self(p.as_object()) } + Self(p.cast()) } #[inline(always)] pub fn error() -> Self { diff --git a/src/dict_iter.rs b/src/dict_iter.rs index 929f377..9b9f09f 100644 --- a/src/dict_iter.rs +++ b/src/dict_iter.rs @@ -1,8 +1,8 @@ -use std::hint::{likely, unlikely}; -use std::ptr; +use crate::py::{self, *}; #[cfg(Py_GIL_DISABLED)] use crate::{py_cache_typed, py_obj}; -use crate::py::{self, *}; +use std::hint::{likely, unlikely}; +use std::ptr; #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] static mut G_DICT_WATCHER_ID: i32 = -1; @@ -13,7 +13,7 @@ static mut G_DICT_WATCHER_REGISTERED: bool = false; static mut G_GUARD_LIST_HEAD: *mut DictIterGuard = ptr::null_mut(); #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -unsafe fn dict_watch_count(dict: *mut PyObject) -> i32 { +unsafe fn dict_watch_count(dict: *mut PyDictObject) -> i32 { let mut count = 0; let mut cur = unsafe { G_GUARD_LIST_HEAD }; while !cur.is_null() { @@ -32,11 +32,12 @@ unsafe extern "C" fn copium_dict_watcher_cb( _key: *mut PyObject, _new_value: *mut PyObject, ) -> i32 { + let dict = unsafe { PyDictObject::cast_unchecked(dict) }; let mut cur_guard = unsafe { G_GUARD_LIST_HEAD }; while !cur_guard.is_null() { let guard = unsafe { &mut *cur_guard }; if likely(guard.dict == dict) { - let cur = unsafe { py::dict::size(dict) }; + let cur = unsafe { dict.size() }; if unlikely(cur >= 0 && cur != guard.size0) { guard.size_changed = true; } @@ -57,19 +58,19 @@ struct PyDictObjectCompat { #[cfg(not(Py_3_14))] #[inline(always)] -unsafe fn dict_version_tag(dict: *mut PyObject) -> u64 { - unsafe { (*(dict as *mut PyDictObjectCompat)).ma_version_tag } +unsafe fn dict_version_tag(dict: *mut PyDictObject) -> u64 { + unsafe { (*(dict.cast::())).ma_version_tag } } #[cfg(not(Py_3_14))] #[inline(always)] -unsafe fn dict_used(dict: *mut PyObject) -> Py_ssize_t { - unsafe { (*(dict as *mut PyDictObjectCompat)).ma_used } +unsafe fn dict_used(dict: *mut PyDictObject) -> Py_ssize_t { + unsafe { (*(dict.cast::())).ma_used } } pub struct DictIterGuard { #[cfg(any(not(Py_3_14), all(Py_3_14, not(Py_GIL_DISABLED))))] - dict: *mut PyObject, + dict: *mut PyDictObject, #[cfg(any(not(Py_3_14), all(Py_3_14, not(Py_GIL_DISABLED))))] pos: Py_ssize_t, @@ -121,9 +122,9 @@ impl DictIterGuard { } #[cfg(all(Py_3_14, Py_GIL_DISABLED))] - unsafe fn new_dict_items_iterator(dict: *mut PyObject) -> *mut PyObject { + unsafe fn new_dict_items_iterator(dict: *mut PyDictObject) -> *mut PyObject { unsafe { - let arguments = [dict]; + let arguments = [dict.cast()]; let dict_items_view = (Self::cached_dict_items_vc())( py_obj!("dict.items"), arguments.as_ptr(), @@ -145,10 +146,10 @@ impl DictIterGuard { #[cfg(not(Py_3_14))] unsafe { Self { - dict: dict as *mut PyObject, + dict, pos: 0, - ver0: dict_version_tag(dict as _), - used0: dict_used(dict as _), + ver0: dict_version_tag(dict), + used0: dict_used(dict), } } @@ -157,7 +158,7 @@ impl DictIterGuard { Self { dict: dict as _, pos: 0, - size0: py::dict::size(dict), + size0: dict.size(), mutated: false, size_changed: false, prev: ptr::null_mut(), @@ -169,7 +170,7 @@ impl DictIterGuard { #[cfg(all(Py_3_14, Py_GIL_DISABLED))] unsafe { Self { - it: Self::new_dict_items_iterator(dict as *mut PyObject), + it: Self::new_dict_items_iterator(dict), active: true, } } @@ -213,7 +214,7 @@ impl DictIterGuard { } if unlikely(need_watch && unsafe { G_DICT_WATCHER_REGISTERED }) { - let _ = unsafe { (self.dict as *mut PyDictObject).watch(G_DICT_WATCHER_ID) }; + let _ = unsafe { self.dict.watch(G_DICT_WATCHER_ID) }; } } @@ -244,7 +245,7 @@ impl DictIterGuard { let need_unwatch = dict_watch_count(dict) == 0; if unlikely(need_unwatch && G_DICT_WATCHER_REGISTERED) { - let _ = (dict as *mut PyDictObject).unwatch(G_DICT_WATCHER_ID); + let _ = dict.unwatch(G_DICT_WATCHER_ID); } self.prev = ptr::null_mut(); @@ -275,7 +276,7 @@ impl DictIterGuard { let mut k: *mut PyObject = ptr::null_mut(); let mut v: *mut PyObject = ptr::null_mut(); - if likely(py::dict::next(self.dict, &mut self.pos, &mut k, &mut v)) { + if likely(self.dict.dict_next(&mut self.pos, &mut k, &mut v)) { let ver_now = dict_version_tag(self.dict); if unlikely(ver_now != self.ver0) { let used_now = dict_used(self.dict); @@ -308,9 +309,9 @@ impl DictIterGuard { let mut k: *mut PyObject = ptr::null_mut(); let mut v: *mut PyObject = ptr::null_mut(); - if likely(py::dict::next(self.dict, &mut self.pos, &mut k, &mut v)) { + if likely(self.dict.dict_next(&mut self.pos, &mut k, &mut v)) { if unlikely(self.mutated) { - let cur = py::dict::size(self.dict); + let cur = self.dict.size(); let size_changed_now = if likely(cur >= 0) { cur != self.size0 } else { @@ -337,7 +338,7 @@ impl DictIterGuard { } if unlikely(self.mutated) { - let cur = py::dict::size(self.dict); + let cur = self.dict.size(); let size_changed_now = if likely(cur >= 0) { cur != self.size0 } else { @@ -367,14 +368,16 @@ impl DictIterGuard { } let mut item: *mut PyObject = ptr::null_mut(); - let result = py::object::iter_next_item(self.it, &mut item); + let result = self.it.iter_next_item(&mut item); if unlikely(result != 1) { self.cleanup(); return result; } - if unlikely(item.is_null() || !py::tuple::check(item) || py::tuple::size(item) != 2) { + if unlikely( + item.is_null() || !item.is_tuple(), + ) { item.decref_nullable(); py::err::set_string( PyExc_RuntimeError, @@ -383,9 +386,19 @@ impl DictIterGuard { self.cleanup(); return -1; } + let item_tuple = PyTupleObject::cast_unchecked(item); + if unlikely(item_tuple.length() != 2) { + item.decref(); + py::err::set_string( + PyExc_RuntimeError, + crate::cstr!("dict.items() iterator returned non-pair item"), + ); + self.cleanup(); + return -1; + } - *key = py::tuple::get_item(item, 0).newref(); - *value = py::tuple::get_item(item, 1).newref(); + *key = item_tuple.get_borrowed_unchecked(0).newref(); + *value = item_tuple.get_borrowed_unchecked(1).newref(); item.decref(); 1 } diff --git a/src/extra.rs b/src/extra.rs index 3450f2c..61ffcd1 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -10,11 +10,16 @@ unsafe extern "C" fn py_replicate( kwnames: *mut PyObject, ) -> *mut PyObject { unsafe { + let keyword_names = if kwnames.is_null() { + ptr::null_mut() + } else { + PyTupleObject::cast_unchecked(kwnames) + }; if nargs != 2 { py::err::set_string(PyExc_TypeError, crate::cstr!("replicate(obj, n, /)")); return ptr::null_mut(); } - if !kwnames.is_null() && (kwnames as *mut PyTupleObject).length() > 0 { + if !keyword_names.is_null() && keyword_names.length() > 0 { py::err::set_string( PyExc_TypeError, crate::cstr!("replicate() does not accept keyword arguments"), @@ -23,7 +28,7 @@ unsafe extern "C" fn py_replicate( } let obj = *args; - let n = py::long::as_i64(*args.add(1)); + let n = (*args.add(1)).as_i64(); if n == -1 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -33,7 +38,7 @@ unsafe extern "C" fn py_replicate( } if n == 0 { - return py::list::new(0).as_object(); + return py::list::new(0).cast(); } let type_pointer = obj.class(); @@ -45,7 +50,7 @@ unsafe extern "C" fn py_replicate( for i in 0..n as Py_ssize_t { out.steal_item_unchecked(i, obj.newref()); } - return out.as_object(); + return out.cast(); } let out = py::list::new(n as Py_ssize_t); @@ -63,7 +68,7 @@ unsafe extern "C" fn py_replicate( } out.steal_item_unchecked(i, copy.into_raw()); } - out.as_object() + out.cast() } } @@ -74,6 +79,11 @@ unsafe extern "C" fn py_repeatcall( kwnames: *mut PyObject, ) -> *mut PyObject { unsafe { + let keyword_names = if kwnames.is_null() { + ptr::null_mut() + } else { + PyTupleObject::cast_unchecked(kwnames) + }; if nargs != 2 { py::err::set_string( PyExc_TypeError, @@ -81,7 +91,7 @@ unsafe extern "C" fn py_repeatcall( ); return ptr::null_mut(); } - if !kwnames.is_null() && (kwnames as *mut PyTupleObject).length() > 0 { + if !keyword_names.is_null() && keyword_names.length() > 0 { py::err::set_string( PyExc_TypeError, crate::cstr!("repeatcall() takes no keyword arguments"), @@ -95,7 +105,7 @@ unsafe extern "C" fn py_repeatcall( return ptr::null_mut(); } - let n = py::long::as_i64(*args.add(1)); + let n = (*args.add(1)).as_i64(); if n == -1 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -117,7 +127,7 @@ unsafe extern "C" fn py_repeatcall( } out.steal_item_unchecked(i, item); } - out.as_object() + out.cast() } } diff --git a/src/fallback.rs b/src/fallback.rs index 43dabe7..5b10797 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -1,3 +1,4 @@ +use std::ffi::CStr; use std::ptr; use crate::memo::{MemoCheckpoint, PyMemoObject}; @@ -41,50 +42,73 @@ macro_rules! finish_fallback_retry { }}; } -unsafe fn unicode_to_string(object: *mut PyObject) -> Option { +unsafe fn unicode_to_string(object: *mut T) -> Option { unsafe { if object.is_null() || !object.is_unicode() { return None; } - Some(py::unicode::as_utf8(object).to_string_lossy().into_owned()) + Some(object.as_utf8().to_string_lossy().into_owned()) } } -unsafe fn new_unicode_from_string(value: &str) -> *mut PyObject { - unsafe { py::unicode::from_str_and_size(value).as_object() } +unsafe fn take_unicode_or_null(object: *mut PyObject) -> *mut PyUnicodeObject { + unsafe { + if object.is_null() { + return ptr::null_mut(); + } + + if object.is_unicode() { + return PyUnicodeObject::cast_unchecked(object); + } + + object.decref(); + py::err::clear(); + ptr::null_mut() + } +} + +unsafe fn getattr_unicode_or_null( + object: *mut T, + attribute_name: &CStr, +) -> *mut PyUnicodeObject { + unsafe { take_unicode_or_null(object.getattr_cstr(attribute_name)) } +} + +unsafe fn new_unicode_from_string(value: &str) -> *mut PyUnicodeObject { + unsafe { py::unicode::from_str_and_size(value) } } unsafe fn build_error_identifier( exception_type: *mut PyObject, exception_value: *mut PyObject, -) -> *mut PyObject { +) -> *mut PyUnicodeObject { unsafe { - let mut type_name: *mut PyObject = ptr::null_mut(); - let mut message: *mut PyObject = ptr::null_mut(); + let mut type_name: *mut PyUnicodeObject = ptr::null_mut(); + let mut message: *mut PyUnicodeObject = ptr::null_mut(); if !exception_type.is_null() && exception_type.is_type() { - type_name = exception_type.getattr_cstr(crate::cstr!("__name__")); + type_name = getattr_unicode_or_null(exception_type, crate::cstr!("__name__")); } if type_name.is_null() { py::err::clear(); - type_name = py::unicode::from_cstr(crate::cstr!("Exception")).as_object(); + type_name = py::unicode::from_cstr(crate::cstr!("Exception")); if type_name.is_null() { return ptr::null_mut(); } } if !exception_value.is_null() { - message = exception_value.str_().as_object(); + message = exception_value.str_(); if message.is_null() { py::err::clear(); } } - let result = if !message.is_null() && py::unicode::byte_length(message) > 0 { - py::unicode::from_format!(crate::cstr!("%U: %U"), type_name, message).as_object() + let result = if !message.is_null() && message.byte_length() > 0 { + py::unicode::from_format!(crate::cstr!("%U: %U"), type_name, message) } else { - py::unicode::from_format!(crate::cstr!("%U: "), type_name).as_object() + py::unicode::from_format!(crate::cstr!("%U: "), type_name) }; type_name.decref_nullable(); @@ -93,16 +117,17 @@ unsafe fn build_error_identifier( } } -unsafe fn error_is_ignored(error_identifier: *mut PyObject) -> bool { +unsafe fn error_is_ignored(error_identifier: *mut PyUnicodeObject) -> bool { unsafe { if error_identifier.is_null() || STATE.ignored_errors.is_null() { return false; } - let ignored_error_count = py::tuple::size(STATE.ignored_errors); + let ignored_errors = STATE.ignored_errors; + let ignored_error_count = ignored_errors.length(); for index in 0..ignored_error_count { - let suffix = py::tuple::get_item(STATE.ignored_errors, index); - if py::unicode::tailmatch(error_identifier, suffix, 0, PY_SSIZE_T_MAX, 1) { + let suffix = ignored_errors.get_borrowed_unchecked(index); + if error_identifier.tailmatch(suffix, 0, PY_SSIZE_T_MAX, 1) { return true; } if !py::err::occurred().is_null() { @@ -114,7 +139,7 @@ unsafe fn error_is_ignored(error_identifier: *mut PyObject) -> bool { } } -unsafe fn extract_deepcopy_expression(line: *mut PyObject) -> *mut PyObject { +unsafe fn extract_deepcopy_expression(line: *mut PyObject) -> *mut PyUnicodeObject { unsafe { if line.is_null() || !line.is_unicode() { return ptr::null_mut(); @@ -189,7 +214,7 @@ unsafe fn extract_deepcopy_expression(line: *mut PyObject) -> *mut PyObject { } } -unsafe fn make_expression_with_memo(expression: *mut PyObject) -> *mut PyObject { +unsafe fn make_expression_with_memo(expression: *mut PyUnicodeObject) -> *mut PyUnicodeObject { unsafe { if expression.is_null() || !expression.is_unicode() { return ptr::null_mut(); @@ -212,45 +237,44 @@ unsafe fn make_expression_with_memo(expression: *mut PyObject) -> *mut PyObject } } -unsafe fn get_caller_frame_info() -> *mut PyObject { +unsafe fn get_caller_frame_info() -> *mut PyTupleObject { unsafe { - let mut result: *mut PyObject = ptr::null_mut(); + let mut result: *mut PyTupleObject = ptr::null_mut(); let mut linecache_module: *mut PyObject = ptr::null_mut(); let mut getline: *mut PyObject = ptr::null_mut(); - let mut line_number_object: *mut PyObject = ptr::null_mut(); - let mut line: *mut PyObject = ptr::null_mut(); - let mut stripped: *mut PyObject = ptr::null_mut(); + let mut line_number_object: *mut PyLongObject = ptr::null_mut(); + let mut line: *mut PyUnicodeObject = ptr::null_mut(); + let mut stripped: *mut PyUnicodeObject = ptr::null_mut(); let mut frame = py::eval::current_frame(); let mut code: *mut PyCodeObject = ptr::null_mut(); - let mut filename: *mut PyObject = ptr::null_mut(); - let mut name: *mut PyObject = ptr::null_mut(); + let mut filename: *mut PyUnicodeObject = ptr::null_mut(); + let mut function_name: *mut PyUnicodeObject = ptr::null_mut(); if frame.is_null() { return ptr::null_mut(); } - (frame as *mut PyObject).incref(); + frame.incref(); while !frame.is_null() { code = frame.code(); if code.is_null() { let back = frame.back(); - (frame as *mut PyObject).decref(); + frame.decref(); frame = back; continue; } - filename = (code as *mut PyObject).getattr_cstr(c"co_filename"); + filename = getattr_unicode_or_null(code, c"co_filename"); if filename.is_null() { py::err::clear(); } - - name = (code as *mut PyObject).getattr_cstr(crate::cstr!("co_name")); - if name.is_null() { + function_name = getattr_unicode_or_null(code, crate::cstr!("co_name")); + if function_name.is_null() { py::err::clear(); } - if !filename.is_null() && !name.is_null() { + if !filename.is_null() && !function_name.is_null() { let line_number = frame.line_number(); linecache_module = py::module::import(crate::cstr!("linecache")); @@ -265,15 +289,19 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { break; } - line_number_object = py::long::from_i64(line_number as i64).as_object(); + line_number_object = py::long::from_i64(line_number as i64); if line_number_object.is_null() { break; } - line = py::call::function_obj_args!(getline, filename, line_number_object); + line = take_unicode_or_null(py::call::function_obj_args!( + getline, + filename, + line_number_object + )); if line.is_null() { py::err::clear(); - line = py::unicode::from_cstr(crate::cstr!("")).as_object(); + line = py::unicode::from_cstr(crate::cstr!("")); if line.is_null() { break; } @@ -282,48 +310,48 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { let strip_method = line.getattr_cstr(crate::cstr!("strip")); if strip_method.is_null() { py::err::clear(); - stripped = py::unicode::from_cstr(crate::cstr!("")).as_object(); + stripped = py::unicode::from_cstr(crate::cstr!("")); } else { - stripped = strip_method.call(); + stripped = take_unicode_or_null(strip_method.call()); strip_method.decref(); if stripped.is_null() { py::err::clear(); - stripped = py::unicode::from_cstr(crate::cstr!("")).as_object(); + stripped = py::unicode::from_cstr(crate::cstr!("")); } } if stripped.is_null() { break; } - result = py::tuple::new(4).as_object(); + result = py::tuple::new(4); if result.is_null() { break; } filename.incref(); - if py::tuple::set_item(result, 0, filename) < 0 { + if result.steal_item(0, filename) < 0 { result.decref(); result = ptr::null_mut(); break; } filename = ptr::null_mut(); - if py::tuple::set_item(result, 1, line_number_object) < 0 { + if result.steal_item(1, line_number_object) < 0 { result.decref(); result = ptr::null_mut(); break; } line_number_object = ptr::null_mut(); - name.incref(); - if py::tuple::set_item(result, 2, name) < 0 { + function_name.incref(); + if result.steal_item(2, function_name) < 0 { result.decref(); result = ptr::null_mut(); break; } - name = ptr::null_mut(); + function_name = ptr::null_mut(); - if py::tuple::set_item(result, 3, stripped) < 0 { + if result.steal_item(3, stripped) < 0 { result.decref(); result = ptr::null_mut(); break; @@ -334,14 +362,14 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { filename.decref_nullable(); filename = ptr::null_mut(); - name.decref_nullable(); - name = ptr::null_mut(); + function_name.decref_nullable(); + function_name = ptr::null_mut(); - (code as *mut PyObject).decref(); + code.decref(); code = ptr::null_mut(); let back = frame.back(); - (frame as *mut PyObject).decref(); + frame.decref(); frame = back; } @@ -350,25 +378,25 @@ unsafe fn get_caller_frame_info() -> *mut PyObject { line_number_object.decref_nullable(); line.decref_nullable(); stripped.decref_nullable(); - (code as *mut PyObject).decref_nullable(); + code.decref_nullable(); filename.decref_nullable(); - name.decref_nullable(); - (frame as *mut PyObject).decref_nullable(); + function_name.decref_nullable(); + frame.decref_nullable(); result } } unsafe fn format_combined_traceback( - caller_info: *mut PyObject, + caller_info: *mut PyTupleObject, exception_value: *mut PyObject, -) -> *mut PyObject { +) -> *mut PyUnicodeObject { unsafe { - let mut parts: *mut PyObject = ptr::null_mut(); + let mut parts: *mut PyListObject = ptr::null_mut(); let traceback_module = py::module::import(crate::cstr!("traceback")); let mut format_exception: *mut PyObject = ptr::null_mut(); - let mut traceback_lines: *mut PyObject = ptr::null_mut(); - let mut empty_string: *mut PyObject = ptr::null_mut(); - let mut caller_string: *mut PyObject = ptr::null_mut(); + let mut traceback_lines: *mut PyListObject = ptr::null_mut(); + let mut empty_string: *mut PyUnicodeObject = ptr::null_mut(); + let mut caller_string: *mut PyUnicodeObject = ptr::null_mut(); if traceback_module.is_null() { cleanup_traceback_build!( @@ -393,19 +421,20 @@ unsafe fn format_combined_traceback( ); } - traceback_lines = format_exception.call_one(exception_value); - if traceback_lines.is_null() || !py::list::check(traceback_lines) { + let traceback_line_objects = format_exception.call_one(exception_value); + if traceback_line_objects.is_null() || !traceback_line_objects.is_list() { cleanup_traceback_build!( parts, traceback_module, format_exception, - traceback_lines, + traceback_line_objects, empty_string, caller_string ); } + traceback_lines = PyListObject::cast_unchecked(traceback_line_objects); - empty_string = py::unicode::from_cstr(crate::cstr!("")).as_object(); + empty_string = py::unicode::from_cstr(crate::cstr!("")); if empty_string.is_null() { cleanup_traceback_build!( parts, @@ -417,7 +446,7 @@ unsafe fn format_combined_traceback( ); } - parts = py::list::new(0).as_object(); + parts = py::list::new(0); if parts.is_null() { cleanup_traceback_build!( parts, @@ -429,14 +458,11 @@ unsafe fn format_combined_traceback( ); } - if !caller_info.is_null() - && py::tuple::check(caller_info) - && py::tuple::size(caller_info) == 4 - { - let filename = py::tuple::get_item(caller_info, 0); - let line_number = py::tuple::get_item(caller_info, 1); - let function_name = py::tuple::get_item(caller_info, 2); - let line = py::tuple::get_item(caller_info, 3); + if !caller_info.is_null() && caller_info.length() == 4 { + let filename = caller_info.get_borrowed_unchecked(0); + let line_number = caller_info.get_borrowed_unchecked(1); + let function_name = caller_info.get_borrowed_unchecked(2); + let line = caller_info.get_borrowed_unchecked(3); caller_string = py::unicode::from_format!( crate::cstr!(" File \"%U\", line %S, in %U\n %U\n"), @@ -444,25 +470,24 @@ unsafe fn format_combined_traceback( line_number, function_name, line, - ) - .as_object(); + ); if caller_string.is_null() { py::err::clear(); } } - let traceback_line_count = py::list::size(traceback_lines); + let traceback_line_count = traceback_lines.length(); let mut found_traceback_header = false; let mut caller_inserted = false; for index in 0..traceback_line_count { - let line = py::list::borrow_item(traceback_lines, index); + let line = traceback_lines.get_borrowed_unchecked(index); if !found_traceback_header && line.is_unicode() { if let Some(line_text) = unicode_to_string(line) { if line_text.starts_with("Traceback") { found_traceback_header = true; - if py::list::append(parts, line) < 0 { + if parts.append(line) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -473,7 +498,7 @@ unsafe fn format_combined_traceback( ); } if !caller_string.is_null() { - if py::list::append(parts, caller_string) < 0 { + if parts.append(caller_string) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -490,7 +515,7 @@ unsafe fn format_combined_traceback( } } - if py::list::append(parts, line) < 0 { + if parts.append(line) < 0 { cleanup_traceback_build!( parts, traceback_module, @@ -503,18 +528,16 @@ unsafe fn format_combined_traceback( } if !found_traceback_header && !caller_string.is_null() && !caller_inserted { - let header = - py::unicode::from_cstr(crate::cstr!("Traceback (most recent call last):\n")) - .as_object(); + let header = py::unicode::from_cstr(crate::cstr!("Traceback (most recent call last):\n")); if !header.is_null() { - if py::list::insert(parts, 0, header) == 0 { - let _ = py::list::insert(parts, 1, caller_string); + if parts.insert(0, header) == 0 { + let _ = parts.insert(1, caller_string); } header.decref(); } } - let result = py::unicode::join(empty_string, parts).as_object(); + let result = empty_string.join(parts); parts.decref_nullable(); traceback_module.decref_nullable(); @@ -529,24 +552,23 @@ unsafe fn format_combined_traceback( unsafe fn emit_fallback_warning( exception_value: *mut PyObject, object: *mut PyObject, - error_identifier: *mut PyObject, + error_identifier: *mut PyUnicodeObject, ) -> i32 { unsafe { let mut status = 0; let caller_info = get_caller_frame_info(); let mut traceback_string = format_combined_traceback(caller_info, exception_value); - let mut full_message: *mut PyObject = ptr::null_mut(); - let type_object = object.class() as *mut PyObject; - let mut module_name = type_object.getattr_cstr(crate::cstr!("__module__")); - let mut type_name = type_object.getattr_cstr(crate::cstr!("__name__")); - let mut deepcopy_qualified_name: *mut PyObject = ptr::null_mut(); - let mut deepcopy_expression: *mut PyObject = ptr::null_mut(); - let mut deepcopy_expression_with_memo: *mut PyObject = ptr::null_mut(); + let mut full_message: *mut PyUnicodeObject = ptr::null_mut(); + let type_object = object.class(); + let mut module_name = getattr_unicode_or_null(type_object, crate::cstr!("__module__")); + let mut type_name = getattr_unicode_or_null(type_object, crate::cstr!("__name__")); + let mut deepcopy_qualified_name: *mut PyUnicodeObject = ptr::null_mut(); + let mut deepcopy_expression: *mut PyUnicodeObject = ptr::null_mut(); + let mut deepcopy_expression_with_memo: *mut PyUnicodeObject = ptr::null_mut(); if traceback_string.is_null() { py::err::clear(); - traceback_string = - py::unicode::from_cstr(crate::cstr!("[traceback unavailable]\n")).as_object(); + traceback_string = py::unicode::from_cstr(crate::cstr!("[traceback unavailable]\n")); if traceback_string.is_null() { status = -1; finish_warning_emit!( @@ -565,7 +587,7 @@ unsafe fn emit_fallback_warning( if module_name.is_null() { py::err::clear(); - module_name = py::unicode::from_cstr(crate::cstr!("__main__")).as_object(); + module_name = py::unicode::from_cstr(crate::cstr!("__main__")); if module_name.is_null() { status = -1; finish_warning_emit!( @@ -584,7 +606,7 @@ unsafe fn emit_fallback_warning( if type_name.is_null() { py::err::clear(); - type_name = py::unicode::from_cstr(crate::cstr!("?")).as_object(); + type_name = py::unicode::from_cstr(crate::cstr!("?")); if type_name.is_null() { status = -1; finish_warning_emit!( @@ -602,8 +624,7 @@ unsafe fn emit_fallback_warning( } deepcopy_qualified_name = - py::unicode::from_format!(crate::cstr!("%U.%U.__deepcopy__"), module_name, type_name) - .as_object(); + py::unicode::from_format!(crate::cstr!("%U.%U.__deepcopy__"), module_name, type_name); if deepcopy_qualified_name.is_null() { status = -1; finish_warning_emit!( @@ -619,11 +640,8 @@ unsafe fn emit_fallback_warning( ); } - if !caller_info.is_null() - && py::tuple::check(caller_info) - && py::tuple::size(caller_info) == 4 - { - let line = py::tuple::get_item(caller_info, 3); + if !caller_info.is_null() && caller_info.length() == 4 { + let line = caller_info.get_borrowed_unchecked(3); deepcopy_expression = extract_deepcopy_expression(line); } @@ -635,8 +653,7 @@ unsafe fn emit_fallback_warning( } if deepcopy_expression.is_null() { - deepcopy_expression = - py::unicode::from_format!(crate::cstr!("deepcopy(%U())"), type_name).as_object(); + deepcopy_expression = py::unicode::from_format!(crate::cstr!("deepcopy(%U())"), type_name); if deepcopy_expression.is_null() { status = -1; finish_warning_emit!( @@ -658,8 +675,7 @@ unsafe fn emit_fallback_warning( if deepcopy_expression_with_memo.is_null() { py::err::clear(); deepcopy_expression_with_memo = - py::unicode::from_format!(crate::cstr!("deepcopy(%U(), memo={})"), type_name) - .as_object(); + py::unicode::from_format!(crate::cstr!("deepcopy(%U(), memo={})"), type_name); if deepcopy_expression_with_memo.is_null() { status = -1; finish_warning_emit!( @@ -690,8 +706,7 @@ unsafe fn emit_fallback_warning( error_identifier, error_identifier, deepcopy_expression, - ) - .as_object(); + ); if full_message.is_null() { status = -1; finish_warning_emit!( @@ -707,7 +722,7 @@ unsafe fn emit_fallback_warning( ); } - if py::err::warn(PyExc_UserWarning, py::unicode::as_utf8(full_message), 1) < 0 { + if py::err::warn(PyExc_UserWarning, full_message.as_utf8(), 1) < 0 { status = -1; } else { py::err::clear(); @@ -735,7 +750,7 @@ pub unsafe fn maybe_retry_with_dict_memo( ) -> *mut PyObject { unsafe { let mut result: *mut PyObject = ptr::null_mut(); - let mut error_identifier: *mut PyObject = ptr::null_mut(); + let mut error_identifier: *mut PyUnicodeObject = ptr::null_mut(); if !py::err::matches_current(PyExc_TypeError) && !py::err::matches_current(PyExc_AssertionError) @@ -768,7 +783,7 @@ pub unsafe fn maybe_retry_with_dict_memo( ); } - let dict_size_before = py::dict::size(dict_memo); + let dict_size_before = dict_memo.size(); result = dunder_deepcopy.call_one(dict_memo); if result.is_null() { finish_fallback_retry!( diff --git a/src/lib.rs b/src/lib.rs index 3e54877..856e906 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,6 @@ use core::ffi::{c_void, CStr}; use std::hint::{likely, unlikely}; use std::ptr; -mod py; mod about; #[allow(dead_code)] mod cache; @@ -17,12 +16,13 @@ mod extra; mod fallback; mod memo; mod patch; +mod py; mod recursion; mod reduce; mod state; -use py::*; use crate::memo::PyMemoObject; use memo::{AnyMemo, DictMemo}; +use py::*; use state::{MemoMode, STATE}; // ══════════════════════════════════════════════════════════════ // copy(obj, /) — METH_O @@ -43,14 +43,19 @@ pub(crate) unsafe extern "C" fn py_deepcopy( kwnames: *mut PyObject, ) -> *mut PyObject { unsafe { + let keyword_names = if kwnames.is_null() { + ptr::null_mut() + } else { + PyTupleObject::cast_unchecked(kwnames) + }; let mut obj: *mut PyObject = ptr::null_mut(); let mut memo_arg: *mut PyObject = py::NoneObject; // ── Fast path: no keyword arguments ───────────────── - let kwcount = if kwnames.is_null() { + let kwcount = if keyword_names.is_null() { 0 } else { - py::tuple::size(kwnames) + keyword_names.length() }; if likely(kwcount == 0) { @@ -93,10 +98,10 @@ pub(crate) unsafe extern "C" fn py_deepcopy( let mut seen_memo_kw = false; for i in 0..kwcount { - let name = py::tuple::get_item(kwnames, i); + let name = keyword_names.get_borrowed_unchecked(i); let val = *args.offset(nargs + i); - if py::unicode::compare_ascii(name, cstr!("x")) == 0 { + if name.compare_ascii(cstr!("x")) == 0 { if !obj.is_null() { py::err::set_string( PyExc_TypeError, @@ -105,7 +110,7 @@ pub(crate) unsafe extern "C" fn py_deepcopy( return ptr::null_mut(); } obj = val; - } else if py::unicode::compare_ascii(name, cstr!("memo")) == 0 { + } else if name.compare_ascii(cstr!("memo")) == 0 { if seen_memo_kw || nargs == 2 { py::err::set_string( PyExc_TypeError, @@ -195,6 +200,11 @@ unsafe extern "C" fn py_replace( kwnames: *mut PyObject, ) -> *mut PyObject { unsafe { + let keyword_names = if kwnames.is_null() { + ptr::null_mut() + } else { + PyTupleObject::cast_unchecked(kwnames) + }; if nargs == 0 { py::err::set_string( PyExc_TypeError, @@ -213,8 +223,7 @@ unsafe extern "C" fn py_replace( let obj = *args; let type_pointer = obj.class(); - let class_object = type_pointer as *mut PyObject; - let func = class_object.getattr_cstr(cstr!("__replace__")); + let func = type_pointer.getattr_cstr(cstr!("__replace__")); if func.is_null() { py::err::clear(); py::err::format!( @@ -233,22 +242,23 @@ unsafe extern "C" fn py_replace( posargs.steal_item_unchecked(0, obj.newref()); let mut kwargs: *mut PyObject = ptr::null_mut(); - let kwcount = if kwnames.is_null() { + let kwcount = if keyword_names.is_null() { 0 } else { - py::tuple::size(kwnames) + keyword_names.length() }; if kwcount > 0 { - kwargs = py::dict::new().as_object(); + let keyword_arguments = py::dict::new(); + kwargs = keyword_arguments.cast(); if kwargs.is_null() { func.decref(); posargs.decref(); return ptr::null_mut(); } for i in 0..kwcount { - let key = py::tuple::get_item(kwnames, i); + let key = keyword_names.get_borrowed_unchecked(i); let val = *args.offset(nargs + i); - (kwargs as *mut PyDictObject).set_item(key, val); + keyword_arguments.set_item(key, val); } } @@ -326,12 +336,12 @@ unsafe extern "C" fn orcopium_exec(module: *mut PyObject) -> i32 { return -1; } - if py::module::add_object(module, cstr!("Error"), py_obj!("copy.Error").newref()) < 0 { + if module.add_module_object(cstr!("Error"), py_obj!("copy.Error").newref()) < 0 { return -1; } - let memo_type = ptr::addr_of_mut!(memo::Memo_Type) as *mut PyObject; - if py::module::add_object(module, cstr!("memo"), memo_type.newref()) < 0 { + let memo_type = ptr::addr_of_mut!(memo::Memo_Type); + if module.add_module_object(cstr!("memo"), memo_type.newref()) < 0 { return -1; } @@ -355,7 +365,7 @@ unsafe extern "C" fn orcopium_exec(module: *mut PyObject) -> i32 { config_module.decref(); return -1; } - if py::module::add_object(module, cstr!("configure"), configure) < 0 { + if module.add_module_object(cstr!("configure"), configure) < 0 { config_module.decref(); configure.decref(); return -1; @@ -366,7 +376,7 @@ unsafe extern "C" fn orcopium_exec(module: *mut PyObject) -> i32 { if get_config.is_null() { return -1; } - if py::module::add_object(module, cstr!("get_config"), get_config) < 0 { + if module.add_module_object(cstr!("get_config"), get_config) < 0 { get_config.decref(); return -1; } @@ -429,11 +439,7 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef { }; /// Register a submodule on the parent and in sys.modules. -pub unsafe fn add_submodule( - parent: *mut PyObject, - name: &CStr, - submodule: *mut PyObject, -) -> i32 { +pub unsafe fn add_submodule(parent: *mut PyObject, name: &CStr, submodule: *mut PyObject) -> i32 { unsafe { let canonical = py::unicode::from_format!(cstr!("copium.%s"), name.as_ptr()); if canonical.is_null() { @@ -448,7 +454,7 @@ pub unsafe fn add_submodule( sys_modules.set_item(canonical, submodule); } - let parent_name = py::module::get_name(parent); + let parent_name = parent.module_name(); if !parent_name.is_null() { let full_name = py::unicode::from_format!(cstr!("%U.%s"), parent_name, name.as_ptr()); if !full_name.is_null() { @@ -461,7 +467,7 @@ pub unsafe fn add_submodule( } canonical.decref(); - if py::module::add_object(parent, name, submodule) < 0 { + if parent.add_module_object(name, submodule) < 0 { submodule.decref(); return -1; } diff --git a/src/memo/any.rs b/src/memo/any.rs index 0a38b3c..1e4cdb9 100644 --- a/src/memo/any.rs +++ b/src/memo/any.rs @@ -41,7 +41,17 @@ impl AnyMemo { } if existing != sentinel { - self.keepalive = existing as *mut PyListObject; + if !existing.is_list() { + existing.decref(); + pykey.decref(); + py::err::set_string( + PyExc_TypeError, + crate::cstr!("memo keepalive slot must contain a list"), + ); + return -1; + } + + self.keepalive = PyListObject::cast_unchecked(existing); pykey.decref(); return 0; } @@ -54,7 +64,7 @@ impl AnyMemo { return -1; } - if self.object.setitem(pykey, list.as_object()) < 0 { + if self.object.setitem(pykey, list) < 0 { list.decref(); pykey.decref(); return -1; @@ -111,7 +121,7 @@ impl Memo for AnyMemo { return -1; } - let rc = self.object.setitem(pykey, copy.as_object()); + let rc = self.object.setitem(pykey, copy); pykey.decref(); if rc < 0 { return -1; diff --git a/src/memo/dict.rs b/src/memo/dict.rs index 5258d11..180eac0 100644 --- a/src/memo/dict.rs +++ b/src/memo/dict.rs @@ -30,8 +30,17 @@ impl DictMemo { let existing = self.dict.get_item(pykey); if !existing.is_null() { + if !existing.is_list() { + pykey.decref(); + py::err::set_string( + PyExc_TypeError, + crate::cstr!("memo keepalive slot must contain a list"), + ); + return -1; + } + existing.incref(); - self.keepalive = existing as *mut PyListObject; + self.keepalive = PyListObject::cast_unchecked(existing); pykey.decref(); return 0; } @@ -112,7 +121,7 @@ impl Memo for DictMemo { #[inline(always)] unsafe fn as_call_arg(&mut self) -> *mut PyObject { - self.dict as *mut PyObject + self.dict.cast() } unsafe fn ensure_memo_is_valid(&mut self) -> i32 { diff --git a/src/memo/native.rs b/src/memo/native.rs index 254be7b..eafbbda 100644 --- a/src/memo/native.rs +++ b/src/memo/native.rs @@ -60,7 +60,7 @@ impl PyMemoObject { } #[cold] - pub unsafe fn to_dict(&self) -> *mut PyObject { + pub unsafe fn to_dict(&self) -> *mut PyDictObject { unsafe { let dict = py::dict::new_presized(0); if dict.is_null() { @@ -68,13 +68,13 @@ impl PyMemoObject { } if self.table.slots.is_null() { - return dict.as_object(); + return dict; } for i in 0..self.table.size { let entry = &*self.table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let pykey = py::long::from_ptr(entry.key as *mut c_void).as_object(); + let pykey = py::long::from_ptr(entry.key as *mut c_void); if pykey.is_null() { dict.decref(); return ptr::null_mut(); @@ -88,15 +88,14 @@ impl PyMemoObject { } } - dict.as_object() + dict } } #[cold] - pub unsafe fn sync_from_dict(&mut self, dict: *mut PyObject, orig_size: Py_ssize_t) -> i32 { + pub unsafe fn sync_from_dict(&mut self, dict: *mut PyDictObject, orig_size: Py_ssize_t) -> i32 { unsafe { - let dict_typed = dict as *mut PyDictObject; - let cur_size = dict_typed.len(); + let cur_size = dict.len(); if cur_size <= orig_size { return 0; } @@ -106,15 +105,15 @@ impl PyMemoObject { let mut value: *mut PyObject = ptr::null_mut(); let mut idx: Py_ssize_t = 0; - while dict_typed.dict_next(&mut pos, &mut py_key, &mut value) { + while dict.dict_next(&mut pos, &mut py_key, &mut value) { idx += 1; if idx <= orig_size { continue; } - if !py::long::check(py_key) { + if !py_key.is_long() { continue; } - let key = py::long::as_ptr(py_key) as usize; + let key = py_key.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return -1; } @@ -163,7 +162,7 @@ impl Memo for PyMemoObject { probe: &Self::Probe, ) -> i32 { let key = original as usize; - if unlikely(self.table.insert_h(key, copy.as_object(), *probe) < 0) { + if unlikely(self.table.insert_h(key, copy.cast(), *probe) < 0) { return -1; } self.keepalive.append(original); @@ -176,7 +175,7 @@ impl Memo for PyMemoObject { } unsafe fn as_call_arg(&mut self) -> *mut PyObject { - self as *mut PyMemoObject as *mut PyObject + std::ptr::from_mut(self).cast() } unsafe fn checkpoint(&mut self) -> Option { diff --git a/src/memo/pytype.rs b/src/memo/pytype.rs index d67b2c4..b3160d1 100644 --- a/src/memo/pytype.rs +++ b/src/memo/pytype.rs @@ -33,9 +33,9 @@ unsafe fn keepalive_list_new(owner: *mut PyMemoObject) -> *mut PyObject { return ptr::null_mut(); } (*obj).owner = owner; - (owner as *mut PyObject).incref(); + owner.incref(); py::gc::track(obj as *mut c_void); - obj as *mut PyObject + obj.cast() } } @@ -59,7 +59,7 @@ unsafe extern "C" fn keepalive_list_dealloc(obj: *mut PyObject) { unsafe { let self_ = obj as *mut PyKeepaliveListObject; py::gc::untrack(self_ as *mut c_void); - ((*self_).owner as *mut PyObject).decref_nullable(); + (*self_).owner.decref_nullable(); py::gc::delete(self_ as *mut c_void); } } @@ -72,7 +72,7 @@ unsafe extern "C" fn keepalive_list_traverse( unsafe { let self_ = obj as *mut PyKeepaliveListObject; if !(*self_).owner.is_null() { - return visit((*self_).owner as *mut PyObject, arg); + return visit((*self_).owner.cast(), arg); } 0 } @@ -139,7 +139,7 @@ unsafe extern "C" fn keepalive_list_iter(obj: *mut PyObject) -> *mut PyObject { for (i, &item) in items.iter().enumerate() { item.incref(); - py::list::set_item(list, i as Py_ssize_t, item); + list.steal_item_unchecked(i as Py_ssize_t, item); } let it = list.get_iter(); @@ -150,18 +150,18 @@ unsafe extern "C" fn keepalive_list_iter(obj: *mut PyObject) -> *mut PyObject { unsafe extern "C" fn keepalive_list_repr(obj: *mut PyObject) -> *mut PyObject { unsafe { - let list = py::seq::to_list(obj).as_object(); + let list = obj.sequence_to_list(); if list.is_null() { return ptr::null_mut(); } - let inner = list.repr().as_object(); + let inner = list.repr(); list.decref(); if inner.is_null() { return ptr::null_mut(); } - let wrapped = py::unicode::from_format!(cstr!("keepalive(%U)"), inner).as_object(); + let wrapped = py::unicode::from_format!(cstr!("keepalive(%U)"), inner); inner.decref(); - wrapped + wrapped.cast() } } @@ -347,7 +347,7 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { } if !(*self_).keepalive.items.is_empty() { - let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); + let py_key = py::long::from_ptr(self_ as *mut c_void); if py_key.is_null() { dict.decref(); return ptr::null_mut(); @@ -360,7 +360,7 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { return ptr::null_mut(); } - if (dict as *mut PyDictObject).set_item(py_key, proxy) < 0 { + if dict.set_item(py_key, proxy) < 0 { proxy.decref(); py_key.decref(); dict.decref(); @@ -371,15 +371,15 @@ unsafe extern "C" fn memo_repr(obj: *mut PyObject) -> *mut PyObject { py_key.decref(); } - let inner = dict.repr().as_object(); + let inner = dict.repr(); dict.decref(); if inner.is_null() { return ptr::null_mut(); } - let wrapped = py::unicode::from_format!(cstr!("memo(%U)"), inner).as_object(); + let wrapped = py::unicode::from_format!(cstr!("memo(%U)"), inner); inner.decref(); - wrapped + wrapped.cast() } } @@ -388,7 +388,7 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { let self_ = obj as *mut PyMemoObject; let table = &(*self_).table; - let list = py::list::new(0).as_object(); + let list = py::list::new(0); if list.is_null() { return ptr::null_mut(); } @@ -397,8 +397,8 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { for i in 0..table.size { let entry = &*table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); - if py_key.is_null() || py::list::append(list, py_key) < 0 { + let py_key = py::long::from_ptr(entry.key as *mut c_void); + if py_key.is_null() || list.append(py_key) < 0 { py_key.decref_nullable(); list.decref(); return ptr::null_mut(); @@ -409,8 +409,8 @@ unsafe extern "C" fn memo_iter(obj: *mut PyObject) -> *mut PyObject { } if !(*self_).keepalive.items.is_empty() { - let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); - if py_key.is_null() || py::list::append(list, py_key) < 0 { + let py_key = py::long::from_ptr(self_ as *mut c_void); + if py_key.is_null() || list.append(py_key) < 0 { py_key.decref_nullable(); list.decref(); return ptr::null_mut(); @@ -443,12 +443,12 @@ unsafe extern "C" fn memo_mp_subscript(obj: *mut PyObject, pykey: *mut PyObject) unsafe { let self_ = obj as *mut PyMemoObject; - if !py::long::check(pykey) { + if !pykey.is_long() { py::err::set(PyExc_KeyError, pykey); return ptr::null_mut(); } - let key = py::long::as_ptr(pykey) as usize; + let key = pykey.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -479,12 +479,12 @@ unsafe extern "C" fn memo_mp_ass_subscript( unsafe { let self_ = obj as *mut PyMemoObject; - if !py::long::check(pykey) { + if !pykey.is_long() { py::err::set_string(PyExc_KeyError, cstr!("keys must be integers")); return -1; } - let key = py::long::as_ptr(pykey) as usize; + let key = pykey.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return -1; } @@ -545,12 +545,12 @@ unsafe extern "C" fn memo_sq_contains(obj: *mut PyObject, pykey: *mut PyObject) unsafe { let self_ = obj as *mut PyMemoObject; - if !py::long::check(pykey) { + if !pykey.is_long() { py::err::set_string(PyExc_TypeError, cstr!("keys must be integers")); return -1; } - let key = py::long::as_ptr(pykey) as usize; + let key = pykey.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return -1; } @@ -599,12 +599,12 @@ unsafe extern "C" fn memo_py_get( let self_ = obj as *mut PyMemoObject; let pykey = *args; - if !py::long::check(pykey) { + if !pykey.is_long() { py::err::set_string(PyExc_TypeError, cstr!("keys must be integers")); return ptr::null_mut(); } - let key = py::long::as_ptr(pykey) as usize; + let key = pykey.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -640,7 +640,7 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu n += 1; } - let list = py::list::new(n).as_object(); + let list = py::list::new(n); if list.is_null() { return ptr::null_mut(); } @@ -651,7 +651,7 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { entry.value.incref(); - py::list::set_item(list, idx, entry.value); + list.steal_item_unchecked(idx, entry.value); idx += 1; } } @@ -663,10 +663,10 @@ unsafe extern "C" fn memo_py_values(obj: *mut PyObject, _: *mut PyObject) -> *mu list.decref(); return ptr::null_mut(); } - py::list::set_item(list, idx, proxy); + list.steal_item_unchecked(idx, proxy); } - list + list.cast() } } @@ -678,7 +678,7 @@ unsafe extern "C" fn memo_py_keys(obj: *mut PyObject, _: *mut PyObject) -> *mut n += 1; } - let list = py::list::new(n).as_object(); + let list = py::list::new(n); if list.is_null() { return ptr::null_mut(); } @@ -688,27 +688,27 @@ unsafe extern "C" fn memo_py_keys(obj: *mut PyObject, _: *mut PyObject) -> *mut for i in 0..(*self_).table.size { let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); + let py_key = py::long::from_ptr(entry.key as *mut c_void); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - py::list::set_item(list, idx, py_key); + list.steal_item_unchecked(idx, py_key); idx += 1; } } } if !(*self_).keepalive.items.is_empty() { - let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); + let py_key = py::long::from_ptr(self_ as *mut c_void); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - py::list::set_item(list, idx, py_key); + list.steal_item_unchecked(idx, py_key); } - list + list.cast() } } @@ -720,7 +720,7 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut n += 1; } - let list = py::list::new(n).as_object(); + let list = py::list::new(n); if list.is_null() { return ptr::null_mut(); } @@ -730,28 +730,28 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut for i in 0..(*self_).table.size { let entry = &*(*self_).table.slots.add(i); if entry.key != 0 && entry.key != TOMBSTONE { - let py_key = py::long::from_ptr(entry.key as *mut c_void).as_object(); + let py_key = py::long::from_ptr(entry.key as *mut c_void); if py_key.is_null() { list.decref(); return ptr::null_mut(); } - let pair = py::tuple::new(2).as_object(); + let pair = py::tuple::new(2); if pair.is_null() { py_key.decref(); list.decref(); return ptr::null_mut(); } - py::tuple::set_item(pair, 0, py_key); + pair.steal_item_unchecked(0, py_key); entry.value.incref(); - py::tuple::set_item(pair, 1, entry.value); - py::list::set_item(list, idx, pair); + pair.steal_item_unchecked(1, entry.value); + list.steal_item_unchecked(idx, pair); idx += 1; } } } if !(*self_).keepalive.items.is_empty() { - let py_key = py::long::from_ptr(self_ as *mut c_void).as_object(); + let py_key = py::long::from_ptr(self_ as *mut c_void); if py_key.is_null() { list.decref(); return ptr::null_mut(); @@ -762,19 +762,19 @@ unsafe extern "C" fn memo_py_items(obj: *mut PyObject, _: *mut PyObject) -> *mut list.decref(); return ptr::null_mut(); } - let pair = py::tuple::new(2).as_object(); + let pair = py::tuple::new(2); if pair.is_null() { py_key.decref(); proxy.decref(); list.decref(); return ptr::null_mut(); } - py::tuple::set_item(pair, 0, py_key); - py::tuple::set_item(pair, 1, proxy); - py::list::set_item(list, idx, pair); + pair.steal_item_unchecked(0, py_key); + pair.steal_item_unchecked(1, proxy); + list.steal_item_unchecked(idx, pair); } - list + list.cast() } } @@ -795,12 +795,12 @@ unsafe extern "C" fn memo_py_setdefault( let self_ = obj as *mut PyMemoObject; let pykey = *args; - if !py::long::check(pykey) { + if !pykey.is_long() { py::err::set_string(PyExc_KeyError, cstr!("keys must be integers")); return ptr::null_mut(); } - let key = py::long::as_ptr(pykey) as usize; + let key = pykey.as_void_ptr() as usize; if key == 0 && !py::err::occurred().is_null() { return ptr::null_mut(); } @@ -814,7 +814,11 @@ unsafe extern "C" fn memo_py_setdefault( return found.newref(); } - let default_value = if nargs == 2 { *args.add(1) } else { py::NoneObject }; + let default_value = if nargs == 2 { + *args.add(1) + } else { + py::NoneObject + }; if (*self_).insert_logged(key, default_value, hash_pointer(key)) < 0 { return ptr::null_mut(); } @@ -900,7 +904,7 @@ unsafe fn init_memo_methods() { pub unsafe fn memo_ready_type() -> i32 { unsafe { init_keepalive_methods(); - if py::type_object::ready(ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE)) < 0 { + if ptr::addr_of_mut!(KEEPALIVE_LIST_TYPE).ready() < 0 { return -1; } @@ -938,6 +942,6 @@ pub unsafe fn memo_ready_type() -> i32 { (*tp).tp_methods = ptr::addr_of_mut!(MEMO_METHODS_TABLE).cast::(); (*tp).tp_finalize = Some(memo_finalize); - py::type_object::ready(tp) + tp.ready() } } diff --git a/src/patch.rs b/src/patch.rs index a7b3ea9..2239d66 100644 --- a/src/patch.rs +++ b/src/patch.rs @@ -77,7 +77,7 @@ unsafe fn apply_patch(_py: Python<'_>, fn_ptr: *mut PyObject, target: *mut PyObj return -1; } - py::vectorcall::set_function_vectorcall(fn_ptr, copium_deepcopy_vectorcall); + fn_ptr.set_vectorcall_function(copium_deepcopy_vectorcall); 1 } } @@ -95,17 +95,14 @@ unsafe fn unapply_patch(_py: Python<'_>, fn_ptr: *mut PyObject) -> i32 { return -1; } - let raw = py::capsule::PyCapsulePtr::get_pointer( - capsule, - capsule_name(), - ); + let raw = capsule.get_pointer(capsule_name()); capsule.decref(); if raw.is_null() { return -1; } let original_vc = std::mem::transmute::<*mut std::ffi::c_void, vectorcallfunc>(raw); - py::vectorcall::set_function_vectorcall(fn_ptr, original_vc); + fn_ptr.set_vectorcall_function(original_vc); fn_ptr.del_attr_cstr(crate::cstr!("__copium_original__")); py::err::clear(); @@ -131,7 +128,7 @@ unsafe fn template_code() -> *mut PyObject { use crate::{py_cache, py_exec, py_obj, py_str}; py_cache!({ let filters = py_obj!("warnings.filters"); - let saved_warnings = py::seq::to_list(filters).as_object(); + let saved_warnings = filters.sequence_to_list(); let warnings_set = py_obj!("warnings.simplefilter").call_one(py_str!("ignore")); if !warnings_set.is_null() { @@ -170,12 +167,13 @@ unsafe fn template_code() -> *mut PyObject { unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { unsafe { let tc = template_code(); - let template_consts = tc.getattr_cstr(crate::cstr!("co_consts")); - if template_consts.is_null() { + let template_consts_object = tc.getattr_cstr(crate::cstr!("co_consts")); + if template_consts_object.is_null() { return ptr::null_mut(); } + let template_consts = PyTupleObject::cast_unchecked(template_consts_object); - let n = py::tuple::size(template_consts); + let n = template_consts.length(); if n < 0 { template_consts.decref(); return ptr::null_mut(); @@ -183,10 +181,10 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { let mut sentinel_idx: Py_ssize_t = -1; for i in 0..n { - let item = py::tuple::get_item(template_consts, i); + let item = template_consts.get_borrowed_unchecked(i); if !item.is_null() && item.is_unicode() - && py::unicode::compare_ascii(item, crate::cstr!("copium.deepcopy")) == 0 + && item.compare_ascii(crate::cstr!("copium.deepcopy")) == 0 { sentinel_idx = i; break; @@ -202,7 +200,7 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { return ptr::null_mut(); } - let new_consts = py::list::new(n).as_object(); + let new_consts = py::list::new(n); if new_consts.is_null() { template_consts.decref(); return ptr::null_mut(); @@ -212,14 +210,14 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { let item = if j == sentinel_idx { target } else { - py::tuple::get_item(template_consts, j) + template_consts.get_borrowed_unchecked(j) }; if item.is_null() { new_consts.decref(); template_consts.decref(); return ptr::null_mut(); } - if py::list::set_item(new_consts, j, item.newref()) < 0 { + if new_consts.steal_item(j, item.newref()) < 0 { new_consts.decref(); template_consts.decref(); return ptr::null_mut(); @@ -227,7 +225,7 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { } template_consts.decref(); - let consts_tuple = py::list::as_tuple(new_consts).as_object(); + let consts_tuple = new_consts.as_tuple(); new_consts.decref(); if consts_tuple.is_null() { return ptr::null_mut(); @@ -239,14 +237,13 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { return ptr::null_mut(); } - let kwargs = py::dict::new().as_object(); + let kwargs = py::dict::new(); if kwargs.is_null() { replace.decref(); consts_tuple.decref(); return ptr::null_mut(); } - if (kwargs as *mut PyDictObject).set_item_cstr(crate::cstr!("co_consts"), consts_tuple) < 0 - { + if kwargs.set_item_cstr(crate::cstr!("co_consts"), consts_tuple) < 0 { kwargs.decref(); replace.decref(); consts_tuple.decref(); @@ -254,7 +251,7 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { } consts_tuple.decref(); - let empty = py::tuple::new(0).as_object(); + let empty = py::tuple::new(0); if empty.is_null() { kwargs.decref(); replace.decref(); diff --git a/src/py/ffi.rs b/src/py/ffi.rs index f456db8..39816cc 100644 --- a/src/py/ffi.rs +++ b/src/py/ffi.rs @@ -17,13 +17,13 @@ pub use pyo3_ffi::{ PyDict_Type, PyEllipsis_Type, PyExc_AssertionError, PyExc_AttributeError, PyExc_IndexError, PyExc_KeyError, PyExc_RecursionError, PyExc_RuntimeError, PyExc_SystemError, PyExc_TypeError, PyExc_UserWarning, PyExc_ValueError, PyFloat_Type, - PyFrozenSet_Type, PyFunction_Type, PyListObject, PyList_Type, PyLongObject, PyLong_Type, - PyMappingMethods, PyMethodDef, PyMethodDefPointer, PyModuleDef, PyModuleDef_HEAD_INIT, - PyModuleDef_Slot, PyObject, PySequenceMethods, PySetObject, PySet_Type, PySliceObject, - PySlice_Type, PyTupleObject, PyTuple_Type, PyTypeObject, PyUnicodeObject, - PyUnicode_Type, Py_eval_input, Py_file_input, Py_hash_t, Py_mod_exec, Py_ssize_t, - Py_TPFLAGS_DEFAULT, Py_TPFLAGS_HAVE_GC, Py_None, PyProperty_Type, PyRange_Type, - _PyWeakref_RefType, + PyFrameObject, PyFrame_Type, PyFrozenSet_Type, PyFunction_Type, PyListObject, PyList_Type, + PyLongObject, PyLong_Type, PyMappingMethods, PyMethodDef, PyMethodDefPointer, PyModuleDef, + PyModuleDef_HEAD_INIT, PyModuleDef_Slot, PyObject, PySequenceMethods, PySetObject, + PySet_Type, PySliceObject, PySlice_Type, PyTupleObject, PyTuple_Type, PyTypeObject, + PyType_Type, PyUnicodeObject, PyUnicode_Type, Py_eval_input, Py_file_input, Py_hash_t, + Py_mod_exec, Py_ssize_t, Py_TPFLAGS_DEFAULT, Py_TPFLAGS_HAVE_GC, Py_None, + PyProperty_Type, PyRange_Type, _PyWeakref_RefType, }; use pyo3_ffi::{Py_DECREF, PyDict_SetItem, Py_SIZE, Py_TPFLAGS_LIST_SUBCLASS}; diff --git a/src/py/long.rs b/src/py/long.rs index 1c4959b..bcfcb15 100644 --- a/src/py/long.rs +++ b/src/py/long.rs @@ -5,13 +5,19 @@ use super::PyTypeInfo; pub unsafe trait PyLongPtr { unsafe fn as_i64(self) -> i64; + unsafe fn as_void_ptr(self) -> *mut c_void; } -unsafe impl PyLongPtr for *mut PyLongObject { +unsafe impl PyLongPtr for *mut T { #[inline(always)] unsafe fn as_i64(self) -> i64 { pyo3_ffi::PyLong_AsLong(self as *mut PyObject) as i64 } + + #[inline(always)] + unsafe fn as_void_ptr(self) -> *mut c_void { + pyo3_ffi::PyLong_AsVoidPtr(self as *mut PyObject) + } } #[inline(always)] @@ -21,7 +27,7 @@ pub unsafe fn from_i64(value: i64) -> *mut PyLongObject { #[inline(always)] pub unsafe fn as_i64(value: *mut T) -> i64 { - pyo3_ffi::PyLong_AsLong(value as *mut PyObject) as i64 + value.as_i64() } #[inline(always)] @@ -31,7 +37,7 @@ pub unsafe fn from_ptr(pointer: *mut T) -> *mut PyLongObject { #[inline(always)] pub unsafe fn as_ptr(value: *mut T) -> *mut c_void { - pyo3_ffi::PyLong_AsVoidPtr(value as *mut PyObject) + value.as_void_ptr() } #[inline(always)] diff --git a/src/py/mod.rs b/src/py/mod.rs index c61bc8e..bb8b4e1 100644 --- a/src/py/mod.rs +++ b/src/py/mod.rs @@ -29,13 +29,15 @@ pub mod type_object; pub mod unicode; pub mod vectorcall; -pub use ffi::*; pub use bytearray::PyBufPtr; pub use capsule::PyCapsulePtr; pub use dict::PyMapPtr; +pub use ffi::*; pub use frame::PyFramePtr; pub use list::PyMutSeqPtr; +pub use long::PyLongPtr; pub use method::PyBoundMethodPtr; +pub use module::PyModulePtr; pub use object::{PyObjectPtr, PyObjectSlotPtr}; pub use seq::PySeqPtr; pub use set::{PyMutSetPtr, PySetPtr}; @@ -93,9 +95,12 @@ pytype! { PySliceObject => PySlice_Type, PyLongObject => PyLong_Type, PyCodeObject => PyCode_Type, + PyFrameObject => PyFrame_Type, + PyTypeObject => PyType_Type, } #[allow(non_upper_case_globals)] pub const NoneObject: *mut PyObject = unsafe { &raw const _Py_NoneStruct as *const _ as *mut _ }; #[allow(non_upper_case_globals)] -pub const EllipsisObject: *mut PyObject = unsafe { &raw const _Py_EllipsisObject as *const _ as *mut _ }; +pub const EllipsisObject: *mut PyObject = + unsafe { &raw const _Py_EllipsisObject as *const _ as *mut _ }; diff --git a/src/py/module.rs b/src/py/module.rs index ab81edf..b42f4ef 100644 --- a/src/py/module.rs +++ b/src/py/module.rs @@ -4,6 +4,33 @@ use std::os::raw::c_int; use super::PyTypeInfo; +pub unsafe trait PyModulePtr { + unsafe fn add_module_object(self, name: &CStr, object: *mut V) -> c_int; + unsafe fn add_module_string_constant(self, name: &CStr, value: &CStr) -> c_int; + unsafe fn module_name(self) -> *mut PyUnicodeObject; +} + +unsafe impl PyModulePtr for *mut T { + #[inline(always)] + unsafe fn add_module_object(self, name: &CStr, object: *mut V) -> c_int { + pyo3_ffi::PyModule_AddObject( + self as *mut PyObject, + name.as_ptr(), + object as *mut PyObject, + ) + } + + #[inline(always)] + unsafe fn add_module_string_constant(self, name: &CStr, value: &CStr) -> c_int { + pyo3_ffi::PyModule_AddStringConstant(self as *mut PyObject, name.as_ptr(), value.as_ptr()) + } + + #[inline(always)] + unsafe fn module_name(self) -> *mut PyUnicodeObject { + pyo3_ffi::PyModule_GetNameObject(self as *mut PyObject) as *mut PyUnicodeObject + } +} + #[inline(always)] pub unsafe fn create(definition: *mut PyModuleDef) -> *mut PyObject { pyo3_ffi::PyModule_Create(definition) @@ -20,7 +47,7 @@ pub unsafe fn add_object( name: &CStr, object: *mut V, ) -> c_int { - pyo3_ffi::PyModule_AddObject(module as *mut PyObject, name.as_ptr(), object as *mut PyObject) + module.add_module_object(name, object) } #[inline(always)] @@ -29,12 +56,12 @@ pub unsafe fn add_string_constant( name: &CStr, value: &CStr, ) -> c_int { - pyo3_ffi::PyModule_AddStringConstant(module as *mut PyObject, name.as_ptr(), value.as_ptr()) + module.add_module_string_constant(name, value) } #[inline(always)] pub unsafe fn get_name(module: *mut M) -> *mut PyUnicodeObject { - pyo3_ffi::PyModule_GetNameObject(module as *mut PyObject) as *mut PyUnicodeObject + module.module_name() } #[inline(always)] diff --git a/src/py/object.rs b/src/py/object.rs index 60a7793..10c5e5e 100644 --- a/src/py/object.rs +++ b/src/py/object.rs @@ -1,10 +1,10 @@ +use super::{ffi, PyTypeInfo}; +use crate::py; use libc::c_ulong; use pyo3_ffi::*; use std::ffi::{c_void, CStr}; use std::os::raw::c_int; use std::ptr; -use crate::py; -use super::{ffi, PyTypeInfo}; pub unsafe trait PyObjectPtr: Sized { unsafe fn id(self) -> *mut PyLongObject; @@ -18,10 +18,8 @@ pub unsafe trait PyObjectPtr: Sized { unsafe fn getattr(self, name: *mut N) -> *mut PyObject; unsafe fn getattr_cstr(self, name: &CStr) -> *mut PyObject; - unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) - -> c_int; - unsafe fn set_attr(self, name: *mut N, value: *mut V) - -> c_int; + unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) -> c_int; + unsafe fn set_attr(self, name: *mut N, value: *mut V) -> c_int; unsafe fn set_attr_cstr(self, name: &CStr, value: *mut V) -> c_int; unsafe fn del_attr(self, name: *mut N) -> c_int; unsafe fn del_attr_cstr(self, name: &CStr) -> c_int; @@ -40,6 +38,13 @@ pub unsafe trait PyObjectPtr: Sized { unsafe fn getitem(self, key: *mut K) -> *mut PyObject; unsafe fn get_iter(self) -> *mut PyObject; unsafe fn iter_next(self) -> *mut PyObject; + unsafe fn fast_sequence(self, message: &CStr) -> *mut PyObject; + unsafe fn fast_sequence_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject; + unsafe fn fast_sequence_length(self) -> Py_ssize_t; + unsafe fn sequence_to_tuple(self) -> *mut PyTupleObject; + unsafe fn sequence_to_list(self) -> *mut PyListObject; + #[cfg(all(Py_3_14, Py_GIL_DISABLED))] + unsafe fn iter_next_item(self, item: &mut *mut PyObject) -> i32; unsafe fn repr(self) -> *mut PyUnicodeObject; unsafe fn str_(self) -> *mut PyUnicodeObject; @@ -47,7 +52,9 @@ pub unsafe trait PyObjectPtr: Sized { unsafe fn is_type(self) -> bool; unsafe fn is_tuple(self) -> bool; + unsafe fn is_list(self) -> bool; unsafe fn is_dict(self) -> bool; + unsafe fn is_long(self) -> bool; unsafe fn is_unicode(self) -> bool; unsafe fn is_bytes(self) -> bool; unsafe fn is_none(self) -> bool; @@ -65,7 +72,6 @@ pub unsafe trait PyObjectPtr: Sized { ) -> c_int { self.getattr_opt(name, result) } - } unsafe impl PyObjectPtr for *mut T { @@ -122,20 +128,12 @@ unsafe impl PyObjectPtr for *mut T { } #[inline(always)] - unsafe fn getattr_opt( - self, - name: *mut N, - result: &mut *mut PyObject, - ) -> c_int { + unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) -> c_int { ffi::PyObject_GetOptionalAttr(self as *mut PyObject, name as *mut PyObject, result) } #[inline(always)] - unsafe fn set_attr( - self, - name: *mut N, - value: *mut V, - ) -> c_int { + unsafe fn set_attr(self, name: *mut N, value: *mut V) -> c_int { pyo3_ffi::PyObject_SetAttr( self as *mut PyObject, name as *mut PyObject, @@ -223,6 +221,37 @@ unsafe impl PyObjectPtr for *mut T { pyo3_ffi::PyIter_Next(self as *mut PyObject) } + #[inline(always)] + unsafe fn fast_sequence(self, message: &CStr) -> *mut PyObject { + ffi::PySequence_Fast(self as *mut PyObject, message.as_ptr()) + } + + #[inline(always)] + unsafe fn fast_sequence_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + ffi::PySequence_Fast_GET_ITEM(self as *mut PyObject, index) + } + + #[inline(always)] + unsafe fn fast_sequence_length(self) -> Py_ssize_t { + ffi::PySequence_Fast_GET_SIZE(self as *mut PyObject) + } + + #[inline(always)] + unsafe fn sequence_to_tuple(self) -> *mut PyTupleObject { + pyo3_ffi::PySequence_Tuple(self as *mut PyObject) as *mut PyTupleObject + } + + #[inline(always)] + unsafe fn sequence_to_list(self) -> *mut PyListObject { + pyo3_ffi::PySequence_List(self as *mut PyObject) as *mut PyListObject + } + + #[cfg(all(Py_3_14, Py_GIL_DISABLED))] + #[inline(always)] + unsafe fn iter_next_item(self, item: &mut *mut PyObject) -> i32 { + ffi::PyIter_NextItem(self as *mut PyObject, item) + } + #[inline(always)] unsafe fn repr(self) -> *mut PyUnicodeObject { pyo3_ffi::PyObject_Repr(self as *mut PyObject) as *mut PyUnicodeObject @@ -248,11 +277,21 @@ unsafe impl PyObjectPtr for *mut T { (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_TUPLE_SUBCLASS as c_ulong)) != 0 } + #[inline(always)] + unsafe fn is_list(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_LIST_SUBCLASS as c_ulong)) != 0 + } + #[inline(always)] unsafe fn is_dict(self) -> bool { (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_DICT_SUBCLASS as c_ulong)) != 0 } + #[inline(always)] + unsafe fn is_long(self) -> bool { + (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_LONG_SUBCLASS as c_ulong)) != 0 + } + #[inline(always)] unsafe fn is_unicode(self) -> bool { (ffi::tp_flags_of(self.class()) & (Py_TPFLAGS_UNICODE_SUBCLASS as c_ulong)) != 0 @@ -307,7 +346,10 @@ pub unsafe fn call_one_arg( } #[inline(always)] -pub unsafe fn call_with(object: *mut O, args: *mut A) -> *mut PyObject { +pub unsafe fn call_with( + object: *mut O, + args: *mut A, +) -> *mut PyObject { object.call_with(args) } @@ -327,11 +369,8 @@ pub unsafe fn get_iter(object: *mut O) -> *mut PyObject { #[cfg(all(Py_3_14, Py_GIL_DISABLED))] #[inline(always)] -pub unsafe fn iter_next_item( - iterator: *mut I, - item: &mut *mut PyObject, -) -> i32 { - ffi::PyIter_NextItem(iterator as *mut PyObject, item) +pub unsafe fn iter_next_item(iterator: *mut I, item: &mut *mut PyObject) -> i32 { + iterator.iter_next_item(item) } #[inline(always)] diff --git a/src/py/seq.rs b/src/py/seq.rs index 4c5a932..241025a 100644 --- a/src/py/seq.rs +++ b/src/py/seq.rs @@ -14,7 +14,7 @@ unsafe fn is_valid_index(index: Py_ssize_t, limit: Py_ssize_t) -> bool { pub unsafe trait PySeqPtr: Sized { unsafe fn length(&self) -> Py_ssize_t; unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject; - unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject); + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut T); #[inline(always)] unsafe fn borrow_item(self, index: Py_ssize_t) -> *mut PyObject { @@ -25,7 +25,7 @@ pub unsafe trait PySeqPtr: Sized { } #[inline(always)] - unsafe fn steal_item(self, index: Py_ssize_t, value: *mut PyObject) -> c_int { + unsafe fn steal_item(self, index: Py_ssize_t, value: *mut T) -> c_int { if unlikely(!is_valid_index(index, self.length())) { return -1; } @@ -47,7 +47,7 @@ pub unsafe trait PySeqPtr: Sized { } #[inline(always)] - unsafe fn set_slot_steal_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { + unsafe fn set_slot_steal_unchecked(self, index: Py_ssize_t, value: *mut T) { self.steal_item_unchecked(index, value) } @@ -69,8 +69,8 @@ unsafe impl PySeqPtr for *mut PyListObject { } #[inline(always)] - unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { - pyo3_ffi::PyList_SET_ITEM(self as *mut PyObject, index, value) + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut T) { + pyo3_ffi::PyList_SET_ITEM(self as *mut PyObject, index, value as *mut PyObject) } } @@ -86,8 +86,8 @@ unsafe impl PySeqPtr for *mut PyTupleObject { } #[inline(always)] - unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut PyObject) { - pyo3_ffi::PyTuple_SET_ITEM(self as *mut PyObject, index, value) + unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut T) { + pyo3_ffi::PyTuple_SET_ITEM(self as *mut PyObject, index, value as *mut PyObject) } } diff --git a/src/py/type_object.rs b/src/py/type_object.rs index 6b90ce8..9514c57 100644 --- a/src/py/type_object.rs +++ b/src/py/type_object.rs @@ -11,6 +11,7 @@ pub unsafe trait PyTypeObjectPtr { unsafe fn is_type_subclass(self) -> bool; unsafe fn is_atomic_immutable(self) -> bool; unsafe fn is_immutable_collection(self) -> bool; + unsafe fn ready(self) -> c_int; } unsafe impl PyTypeObjectPtr for *mut PyTypeObject { @@ -63,9 +64,14 @@ unsafe impl PyTypeObjectPtr for *mut PyTypeObject { | (self == std::ptr::addr_of_mut!(PyFrozenSet_Type)) | (self == std::ptr::addr_of_mut!(PySlice_Type)) } + + #[inline(always)] + unsafe fn ready(self) -> c_int { + pyo3_ffi::PyType_Ready(self) + } } #[inline(always)] pub unsafe fn ready(type_object: *mut PyTypeObject) -> c_int { - pyo3_ffi::PyType_Ready(type_object) + type_object.ready() } diff --git a/src/py/unicode.rs b/src/py/unicode.rs index cb631d8..18a6232 100644 --- a/src/py/unicode.rs +++ b/src/py/unicode.rs @@ -8,17 +8,17 @@ pub unsafe trait PyUnicodePtr { unsafe fn as_utf8<'a>(self) -> &'a CStr; unsafe fn byte_length(self) -> Py_ssize_t; unsafe fn compare_ascii(self, string: &CStr) -> i32; - unsafe fn join(self, sequence: *mut T) -> *mut PyUnicodeObject; - unsafe fn tailmatch( + unsafe fn join(self, sequence: *mut Sequence) -> *mut PyUnicodeObject; + unsafe fn tailmatch( self, - substring: *mut PyUnicodeObject, + substring: *mut S, start: Py_ssize_t, end: Py_ssize_t, direction: i32, ) -> bool; } -unsafe impl PyUnicodePtr for *mut PyUnicodeObject { +unsafe impl PyUnicodePtr for *mut T { #[inline(always)] unsafe fn as_utf8<'a>(self) -> &'a CStr { CStr::from_ptr(pyo3_ffi::PyUnicode_AsUTF8(self as *mut PyObject)) @@ -35,15 +35,15 @@ unsafe impl PyUnicodePtr for *mut PyUnicodeObject { } #[inline(always)] - unsafe fn join(self, sequence: *mut T) -> *mut PyUnicodeObject { + unsafe fn join(self, sequence: *mut Sequence) -> *mut PyUnicodeObject { pyo3_ffi::PyUnicode_Join(self as *mut PyObject, sequence as *mut PyObject) as *mut PyUnicodeObject } #[inline(always)] - unsafe fn tailmatch( + unsafe fn tailmatch( self, - substring: *mut PyUnicodeObject, + substring: *mut S, start: Py_ssize_t, end: Py_ssize_t, direction: i32, @@ -76,17 +76,17 @@ pub unsafe fn from_str_and_size(value: &str) -> *mut PyUnicodeObject { #[inline(always)] pub unsafe fn as_utf8<'a, T: PyTypeInfo>(value: *mut T) -> &'a CStr { - CStr::from_ptr(pyo3_ffi::PyUnicode_AsUTF8(value as *mut PyObject)) + value.as_utf8() } #[inline(always)] pub unsafe fn byte_length(value: *mut T) -> Py_ssize_t { - pyo3_ffi::PyUnicode_GET_LENGTH(value as *mut PyObject) + value.byte_length() } #[inline(always)] pub unsafe fn compare_ascii(value: *mut T, string: &CStr) -> i32 { - pyo3_ffi::PyUnicode_CompareWithASCIIString(value as *mut PyObject, string.as_ptr()) + value.compare_ascii(string) } #[inline(always)] @@ -94,8 +94,7 @@ pub unsafe fn join( separator: *mut S, sequence: *mut Q, ) -> *mut PyUnicodeObject { - pyo3_ffi::PyUnicode_Join(separator as *mut PyObject, sequence as *mut PyObject) - as *mut PyUnicodeObject + separator.join(sequence) } #[inline(always)] @@ -106,13 +105,7 @@ pub unsafe fn tailmatch( end: Py_ssize_t, direction: i32, ) -> bool { - pyo3_ffi::PyUnicode_Tailmatch( - value as *mut PyObject, - substring as *mut PyObject, - start, - end, - direction as c_int, - ) != 0 + value.tailmatch(substring, start, end, direction) } macro_rules! from_format { diff --git a/src/py/vectorcall.rs b/src/py/vectorcall.rs index 23b6872..f014f97 100644 --- a/src/py/vectorcall.rs +++ b/src/py/vectorcall.rs @@ -4,6 +4,7 @@ use super::{ffi, PyTypeInfo}; pub unsafe trait PyVectorcallPtr { unsafe fn vectorcall_function(self) -> Option; + unsafe fn set_vectorcall_function(self, vectorcall: vectorcallfunc); } unsafe impl PyVectorcallPtr for *mut T { @@ -11,26 +12,23 @@ unsafe impl PyVectorcallPtr for *mut T { unsafe fn vectorcall_function(self) -> Option { ffi::PyVectorcall_Function(self as *mut PyObject) } + + #[inline(always)] + unsafe fn set_vectorcall_function(self, vectorcall: vectorcallfunc) { + #[cfg(Py_3_12)] + unsafe { + ffi::PyFunction_SetVectorcall(self as *mut PyObject, vectorcall); + } + + #[cfg(not(Py_3_12))] + { + let _ = self; + let _ = vectorcall; + } + } } #[inline(always)] pub fn nargs(nargsf: usize) -> Py_ssize_t { ffi::PyVectorcall_NARGS(nargsf) } - -#[inline(always)] -pub unsafe fn set_function_vectorcall( - function: *mut F, - vectorcall: vectorcallfunc, -) { - #[cfg(Py_3_12)] - unsafe { - ffi::PyFunction_SetVectorcall(function as *mut PyObject, vectorcall); - } - - #[cfg(not(Py_3_12))] - { - let _ = function; - let _ = vectorcall; - } -} diff --git a/src/reduce.rs b/src/reduce.rs index e1f2ab8..caabea4 100644 --- a/src/reduce.rs +++ b/src/reduce.rs @@ -49,6 +49,10 @@ pub(crate) unsafe fn chain_type_error(msg: *mut PyObject) { } } +pub(crate) unsafe fn chain_typed_type_error(message: *mut T) { + unsafe { chain_type_error(message.cast()) } +} + // ── Registry & reduce dispatch ───────────────────────────── pub(crate) unsafe fn try_reduce_via_registry( @@ -56,7 +60,7 @@ pub(crate) unsafe fn try_reduce_via_registry( tp: *mut PyTypeObject, ) -> *mut PyObject { unsafe { - let reducer = py_obj!(PyDictObject, "copyreg.dispatch_table").get_item(tp as *mut PyObject); + let reducer = py_obj!(PyDictObject, "copyreg.dispatch_table").get_item(tp); if reducer.is_null() { return ptr::null_mut(); } @@ -76,7 +80,7 @@ pub(crate) unsafe fn call_reduce_method_preferring_ex(obj: *mut PyObject) -> *mu let mut reduce_ex: *mut PyObject = ptr::null_mut(); let has = obj.get_optional_attr(py_str!("__reduce_ex__"), &mut reduce_ex); if has > 0 { - let four = py::long::from_i64(4).as_object(); + let four = py::long::from_i64(4); let res = reduce_ex.call_one(four); four.decref(); reduce_ex.decref(); @@ -107,7 +111,7 @@ pub(crate) unsafe fn call_reduce_method_preferring_ex(obj: *mut PyObject) -> *mu pub(crate) struct ReduceParts { pub(crate) callable: *mut PyObject, - pub(crate) argtup: *mut PyObject, + pub(crate) argument_tuple: *mut PyTupleObject, pub(crate) state: *mut PyObject, pub(crate) listitems: *mut PyObject, pub(crate) dictitems: *mut PyObject, @@ -126,7 +130,7 @@ pub(crate) unsafe fn validate_reduce_tuple( unsafe { let empty = ReduceParts { callable: ptr::null_mut(), - argtup: ptr::null_mut(), + argument_tuple: ptr::null_mut(), state: ptr::null_mut(), listitems: ptr::null_mut(), dictitems: ptr::null_mut(), @@ -143,8 +147,8 @@ pub(crate) unsafe fn validate_reduce_tuple( return (ReduceKind::Error, empty); } - let tup = reduce_result as *mut PyTupleObject; - let size = tup.length(); + let reduce_tuple = PyTupleObject::cast_unchecked(reduce_result); + let size = reduce_tuple.length(); if size < 2 || size > 5 { py::err::set_string( PyExc_TypeError, @@ -153,51 +157,53 @@ pub(crate) unsafe fn validate_reduce_tuple( return (ReduceKind::Error, empty); } - let callable = tup.get_borrowed_unchecked(0); - let mut argtup = tup.get_borrowed_unchecked(1); + let callable = reduce_tuple.get_borrowed_unchecked(0); + let argument_tuple_object = reduce_tuple.get_borrowed_unchecked(1); let none = py::NoneObject; let state_raw = if size >= 3 { - tup.get_borrowed_unchecked(2) + reduce_tuple.get_borrowed_unchecked(2) } else { none }; let list_raw = if size >= 4 { - tup.get_borrowed_unchecked(3) + reduce_tuple.get_borrowed_unchecked(3) } else { none }; let dict_raw = if size == 5 { - tup.get_borrowed_unchecked(4) + reduce_tuple.get_borrowed_unchecked(4) } else { none }; - if !argtup.is_tuple() { - let coerced = py::seq::to_tuple(argtup).as_object(); + let argument_tuple = if argument_tuple_object.is_tuple() { + argument_tuple_object.cast::() + } else { + let coerced = argument_tuple_object.sequence_to_tuple(); if coerced.is_null() { let msg = py::unicode::from_format!( crate::cstr!( "second element of the tuple returned by %s.__reduce__ must be a tuple, not %.200s" ), (*reducing_type).tp_name, - (*argtup.class()).tp_name, + (*argument_tuple_object.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg.as_object()); + chain_typed_type_error(msg); } return (ReduceKind::Error, empty); } - let old = argtup; - tup.set_slot_steal_unchecked(1, coerced); + let old = argument_tuple_object; + reduce_tuple.set_slot_steal_unchecked(1, coerced); old.decref(); - argtup = coerced; - } + coerced + }; ( ReduceKind::Tuple, ReduceParts { callable, - argtup, + argument_tuple, state: if state_raw == none { ptr::null_mut() } else { @@ -240,11 +246,14 @@ unsafe fn call_tp_new( } } -unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *mut PyObject { +unsafe fn reconstruct_newobj( + argument_tuple: *mut PyTupleObject, + memo: &mut M, +) -> *mut PyObject { unsafe { - let tup = argtup as *mut PyTupleObject; - let nargs = tup.length(); - if nargs < 1 { + let tuple = argument_tuple; + let argument_count = tuple.length(); + if argument_count < 1 { py::err::set_string( PyExc_TypeError, crate::cstr!("__newobj__ requires at least 1 argument"), @@ -252,116 +261,117 @@ unsafe fn reconstruct_newobj(argtup: *mut PyObject, memo: &mut M) -> *m return ptr::null_mut(); } - let cls = tup.get_borrowed_unchecked(0); - if !cls.is_type() { + let class_object = tuple.get_borrowed_unchecked(0); + if !class_object.is_type() { py::err::format!( PyExc_TypeError, crate::cstr!("__newobj__ arg 1 must be a type, not %.200s"), - (*cls.class()).tp_name, + (*class_object.class()).tp_name, ); return ptr::null_mut(); } + let class_pointer = class_object.cast::(); - let args = bail!(py::tuple::new(nargs - 1)); + let arguments = bail!(py::tuple::new(argument_count - 1)); - for i in 1..nargs { - let arg = tup.get_borrowed_unchecked(i); - let copied = deepcopy::deepcopy(arg, memo); + for argument_index in 1..argument_count { + let argument = tuple.get_borrowed_unchecked(argument_index); + let copied = deepcopy::deepcopy(argument, memo); if copied.is_error() { - args.decref(); + arguments.decref(); return ptr::null_mut(); } - args.set_slot_steal_unchecked(i - 1, copied.into_raw()); + arguments.set_slot_steal_unchecked(argument_index - 1, copied.into_raw()); } - let instance = call_tp_new(cls as *mut PyTypeObject, args.as_object(), ptr::null_mut()); - args.decref(); + let instance = call_tp_new(class_pointer, arguments.cast(), ptr::null_mut()); + arguments.decref(); instance } } unsafe fn reconstruct_newobj_ex( - argtup: *mut PyObject, + argument_tuple: *mut PyTupleObject, memo: &mut M, reducing_type: *mut PyTypeObject, ) -> *mut PyObject { unsafe { - let tup = argtup as *mut PyTupleObject; - if tup.length() != 3 { + if argument_tuple.length() != 3 { py::err::format!( PyExc_TypeError, crate::cstr!("__newobj_ex__ requires 3 arguments, got %zd"), - tup.length(), + argument_tuple.length(), ); return ptr::null_mut(); } - let cls = tup.get_borrowed_unchecked(0); - let mut args = tup.get_borrowed_unchecked(1); - let mut kwargs = tup.get_borrowed_unchecked(2); + let class_object = argument_tuple.get_borrowed_unchecked(0); + let mut arguments_object = argument_tuple.get_borrowed_unchecked(1); + let mut keyword_arguments_object = argument_tuple.get_borrowed_unchecked(2); - if !cls.is_type() { + if !class_object.is_type() { py::err::format!( PyExc_TypeError, crate::cstr!("__newobj_ex__ arg 1 must be a type, not %.200s"), - (*cls.class()).tp_name, + (*class_object.class()).tp_name, ); return ptr::null_mut(); } + let class_pointer = class_object.cast::(); - let mut coerced_args: *mut PyObject = ptr::null_mut(); + let mut coerced_arguments: *mut PyTupleObject = ptr::null_mut(); let mut coerced_kwargs: *mut PyDictObject = ptr::null_mut(); - if !args.is_tuple() { - coerced_args = py::seq::to_tuple(args).as_object(); - if coerced_args.is_null() { + if !arguments_object.is_tuple() { + coerced_arguments = arguments_object.sequence_to_tuple(); + if coerced_arguments.is_null() { let msg = py::unicode::from_format!( crate::cstr!( "__newobj_ex__ args in %s.__reduce__ result must be a tuple, not %.200s" ), (*reducing_type).tp_name, - (*args.class()).tp_name, + (*arguments_object.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg.as_object()); + chain_typed_type_error(msg); } return ptr::null_mut(); } - args = coerced_args; + arguments_object = coerced_arguments.cast(); } - if !kwargs.is_dict() { + if !keyword_arguments_object.is_dict() { coerced_kwargs = py::dict::new_presized(0); if coerced_kwargs.is_null() { - coerced_args.decref_nullable(); + coerced_arguments.decref_nullable(); return ptr::null_mut(); } - if coerced_kwargs.merge(kwargs, true) < 0 { + if coerced_kwargs.merge(keyword_arguments_object, true) < 0 { let msg = py::unicode::from_format!( crate::cstr!( "__newobj_ex__ kwargs in %s.__reduce__ result must be a dict, not %.200s" ), (*reducing_type).tp_name, - (*kwargs.class()).tp_name, + (*keyword_arguments_object.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg.as_object()); + chain_typed_type_error(msg); } - coerced_args.decref_nullable(); + coerced_arguments.decref_nullable(); coerced_kwargs.decref(); return ptr::null_mut(); } - kwargs = coerced_kwargs.as_object(); + keyword_arguments_object = coerced_kwargs.cast(); } - let copied_args = deepcopy::deepcopy(args, memo); - coerced_args.decref_nullable(); + let copied_args = deepcopy::deepcopy(arguments_object, memo); + coerced_arguments.decref_nullable(); if copied_args.is_error() { coerced_kwargs.decref_nullable(); return ptr::null_mut(); } - let copied_kwargs = deepcopy::deepcopy(kwargs, memo); + let copied_kwargs = deepcopy::deepcopy(keyword_arguments_object, memo); coerced_kwargs.decref_nullable(); if copied_kwargs.is_error() { copied_args.into_raw().decref(); @@ -370,7 +380,7 @@ unsafe fn reconstruct_newobj_ex( let ca = copied_args.into_raw(); let ck = copied_kwargs.into_raw(); - let instance = call_tp_new(cls as *mut PyTypeObject, ca, ck); + let instance = call_tp_new(class_pointer, ca, ck); ca.decref(); ck.decref(); instance @@ -379,30 +389,29 @@ unsafe fn reconstruct_newobj_ex( unsafe fn reconstruct_callable( callable: *mut PyObject, - argtup: *mut PyObject, + argument_tuple: *mut PyTupleObject, memo: &mut M, ) -> *mut PyObject { unsafe { - let tup = argtup as *mut PyTupleObject; - let nargs = tup.length(); + let argument_count = argument_tuple.length(); - if nargs == 0 { + if argument_count == 0 { return callable.call(); } - let copied_args = bail!(py::tuple::new(nargs)); + let copied_args = bail!(py::tuple::new(argument_count)); - for i in 0..nargs { - let arg = tup.get_borrowed_unchecked(i); - let copied = deepcopy::deepcopy(arg, memo); + for argument_index in 0..argument_count { + let argument = argument_tuple.get_borrowed_unchecked(argument_index); + let copied = deepcopy::deepcopy(argument, memo); if copied.is_error() { copied_args.decref(); return ptr::null_mut(); } - copied_args.set_slot_steal_unchecked(i, copied.into_raw()); + copied_args.set_slot_steal_unchecked(argument_index, copied.into_raw()); } - let instance = callable.call_with(copied_args.as_object()); + let instance = callable.call_with(copied_args); copied_args.decref(); instance } @@ -465,7 +474,8 @@ unsafe fn apply_dict_state( copied.decref(); return -1; } - let ret = (instance_dict as *mut PyDictObject).merge(copied, true); + let instance_dictionary = PyDictObject::cast_unchecked(instance_dict); + let ret = instance_dictionary.merge(copied, true); instance_dict.decref(); if ret < 0 { let msg = py::unicode::from_format!( @@ -476,13 +486,14 @@ unsafe fn apply_dict_state( (*copied.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg.as_object()); + chain_typed_type_error(msg); } } copied.decref(); return ret; } + let copied_dictionary = PyDictObject::cast_unchecked(copied); let instance_dict = instance.getattr(py_str!("__dict__")); if instance_dict.is_null() { copied.decref(); @@ -494,7 +505,7 @@ unsafe fn apply_dict_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) { + while copied_dictionary.dict_next(&mut pos, &mut key, &mut value) { if instance_dict.setitem(key, value) < 0 { ret = -1; break; @@ -534,7 +545,7 @@ unsafe fn apply_slot_state( (*copied.class()).tp_name, ); if !msg.is_null() { - chain_type_error(msg.as_object()); + chain_typed_type_error(msg); } copied.decref(); return -1; @@ -558,13 +569,13 @@ unsafe fn apply_slot_state( if pair.is_null() { break; } - let seq = py::seq::fast(pair, crate::cstr!("items() must return pairs")); + let seq = pair.fast_sequence(crate::cstr!("items() must return pairs")); pair.decref(); if seq.is_null() { ret = -1; break; } - if py::seq::fast_length(seq) != 2 { + if seq.fast_sequence_length() != 2 { seq.decref(); if py::err::occurred().is_null() { py::err::set_string( @@ -575,8 +586,8 @@ unsafe fn apply_slot_state( ret = -1; break; } - let k = py::seq::fast_borrow_item_unchecked(seq, 0); - let v = py::seq::fast_borrow_item_unchecked(seq, 1); + let k = seq.fast_sequence_item_unchecked(0); + let v = seq.fast_sequence_item_unchecked(1); let rc = instance.set_attr(k, v); seq.decref(); if rc < 0 { @@ -596,7 +607,9 @@ unsafe fn apply_slot_state( let mut pos: Py_ssize_t = 0; let mut ret: c_int = 0; - while (copied as *mut PyDictObject).dict_next(&mut pos, &mut key, &mut value) { + let copied_dictionary = PyDictObject::cast_unchecked(copied); + + while copied_dictionary.dict_next(&mut pos, &mut key, &mut value) { if instance.set_attr(key, value) < 0 { ret = -1; break; @@ -617,10 +630,12 @@ unsafe fn apply_state_tuple( let mut dict_state = state; let mut slotstate: *mut PyObject = ptr::null_mut(); - if state.is_tuple() && (state as *mut PyTupleObject).length() == 2 { - let tup = state as *mut PyTupleObject; - dict_state = tup.get_borrowed_unchecked(0); - slotstate = tup.get_borrowed_unchecked(1); + if state.is_tuple() { + let tuple_state = PyTupleObject::cast_unchecked(state); + if tuple_state.length() == 2 { + dict_state = tuple_state.get_borrowed_unchecked(0); + slotstate = tuple_state.get_borrowed_unchecked(1); + } } if apply_dict_state(instance, dict_state, memo) < 0 { @@ -708,18 +723,50 @@ unsafe fn apply_dictitems( } let (mut key, mut value); - if pair.is_tuple() && (pair as *mut PyTupleObject).length() == 2 { - let ptup = pair as *mut PyTupleObject; - key = ptup.get_borrowed_unchecked(0).newref(); - value = ptup.get_borrowed_unchecked(1).newref(); + if pair.is_tuple() { + let pair_tuple = PyTupleObject::cast_unchecked(pair); + if pair_tuple.length() == 2 { + key = pair_tuple.get_borrowed_unchecked(0).newref(); + value = pair_tuple.get_borrowed_unchecked(1).newref(); + } else { + let seq = pair.fast_sequence(crate::cstr!("cannot unpack non-sequence")); + if seq.is_null() { + pair.decref(); + ret = -1; + break; + } + let n = seq.fast_sequence_length(); + if n != 2 { + seq.decref(); + pair.decref(); + if n < 2 { + py::err::format!( + PyExc_ValueError, + crate::cstr!("not enough values to unpack (expected 2, got %zd)"), + n, + ); + } else { + py::err::format!( + PyExc_ValueError, + crate::cstr!("too many values to unpack (expected 2, got %zd)"), + n, + ); + } + ret = -1; + break; + } + key = seq.fast_sequence_item_unchecked(0).newref(); + value = seq.fast_sequence_item_unchecked(1).newref(); + seq.decref(); + } } else { - let seq = py::seq::fast(pair, crate::cstr!("cannot unpack non-sequence")); + let seq = pair.fast_sequence(crate::cstr!("cannot unpack non-sequence")); if seq.is_null() { pair.decref(); ret = -1; break; } - let n = py::seq::fast_length(seq); + let n = seq.fast_sequence_length(); if n != 2 { seq.decref(); pair.decref(); @@ -739,8 +786,8 @@ unsafe fn apply_dictitems( ret = -1; break; } - key = py::seq::fast_borrow_item_unchecked(seq, 0).newref(); - value = py::seq::fast_borrow_item_unchecked(seq, 1).newref(); + key = seq.fast_sequence_item_unchecked(0).newref(); + value = seq.fast_sequence_item_unchecked(1).newref(); seq.decref(); } pair.decref(); @@ -815,11 +862,11 @@ pub unsafe fn reconstruct( } let instance = if parts.callable == py_obj!("copyreg.__newobj__") { - reconstruct_newobj(parts.argtup, memo) + reconstruct_newobj(parts.argument_tuple, memo) } else if parts.callable == py_obj!("copyreg.__newobj_ex__") { - reconstruct_newobj_ex(parts.argtup, memo, tp) + reconstruct_newobj_ex(parts.argument_tuple, memo, tp) } else { - reconstruct_callable(parts.callable, parts.argtup, memo) + reconstruct_callable(parts.callable, parts.argument_tuple, memo) }; if instance.is_null() { diff --git a/src/state.rs b/src/state.rs index b8d7159..5e7d082 100644 --- a/src/state.rs +++ b/src/state.rs @@ -19,12 +19,12 @@ pub enum OnIncompatible { } pub struct ModuleState { - pub sentinel: *mut PyObject, + pub sentinel: *mut PyListObject, pub memo_mode: MemoMode, pub on_incompatible: OnIncompatible, - pub ignored_errors: *mut PyObject, - pub ignored_errors_joined: *mut PyObject, + pub ignored_errors: *mut PyTupleObject, + pub ignored_errors_joined: *mut PyUnicodeObject, } unsafe impl Sync for ModuleState {} @@ -42,7 +42,7 @@ pub unsafe fn init() -> i32 { unsafe { let s = std::ptr::addr_of_mut!(STATE); - (*s).sentinel = py::list::new(0).as_object(); + (*s).sentinel = py::list::new(0); if (*s).sentinel.is_null() { return -1; } @@ -53,11 +53,11 @@ pub unsafe fn init() -> i32 { pub unsafe fn cleanup() {} -unsafe fn parse_ignored_errors_from_environment() -> *mut PyObject { +unsafe fn parse_ignored_errors_from_environment() -> *mut PyTupleObject { unsafe { let environment_value = match std::env::var("COPIUM_NO_MEMO_FALLBACK_WARNING") { Ok(value) if !value.is_empty() => value, - _ => return py::tuple::new(0).as_object(), + _ => return py::tuple::new(0), }; let ignored_error_parts: Vec<&str> = environment_value @@ -77,14 +77,14 @@ unsafe fn parse_ignored_errors_from_environment() -> *mut PyObject { return ptr::null_mut(); } - tuple.steal_item_unchecked(index as isize, item.as_object()); + tuple.steal_item_unchecked(index as isize, item); } - tuple.as_object() + tuple } } -pub unsafe fn update_suppress_warnings(new_tuple: *mut PyObject) -> i32 { +pub unsafe fn update_suppress_warnings(new_tuple: *mut PyTupleObject) -> i32 { unsafe { let s = std::ptr::addr_of_mut!(STATE); @@ -95,13 +95,13 @@ pub unsafe fn update_suppress_warnings(new_tuple: *mut PyObject) -> i32 { (*s).ignored_errors_joined.decref_nullable(); (*s).ignored_errors_joined = ptr::null_mut(); - if (new_tuple as *mut PyTupleObject).length() > 0 { + if new_tuple.length() > 0 { let separator = py::unicode::from_cstr(cstr!("::")); if separator.is_null() { return -1; } - (*s).ignored_errors_joined = separator.join(new_tuple).as_object(); + (*s).ignored_errors_joined = separator.join(new_tuple); separator.decref(); if (*s).ignored_errors_joined.is_null() { return -1; From d5ca0f076c866d18c7fde2942da74e840a1cc96f Mon Sep 17 00:00:00 2001 From: Bobronium Date: Sat, 21 Mar 2026 04:02:27 +0400 Subject: [PATCH 10/10] Cleanup API --- src/copy.rs | 6 +-- src/deepcopy.rs | 14 +++--- src/memo/native.rs | 2 +- src/patch.rs | 2 +- src/py/dict.rs | 34 ++----------- src/py/ffi.rs | 5 -- src/py/list.rs | 40 --------------- src/py/long.rs | 15 ------ src/py/module.rs | 23 --------- src/py/object.rs | 112 +++++------------------------------------- src/py/seq.rs | 61 ++--------------------- src/py/set.rs | 19 +++---- src/py/tuple.rs | 23 --------- src/py/type_object.rs | 5 -- src/py/unicode.rs | 39 --------------- src/reduce.rs | 6 +-- 16 files changed, 45 insertions(+), 361 deletions(-) diff --git a/src/copy.rs b/src/copy.rs index 10f4a9c..82c67ca 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -53,7 +53,7 @@ impl PyCopy for *mut PyListObject { for index in 0..size { let item = self.get_borrowed_unchecked(index); item.incref(); - copied.set_slot_steal_unchecked(index, item); + copied.steal_item_unchecked(index, item); } PyResult::ok(copied) @@ -76,10 +76,10 @@ impl PyCopy for *mut PySetObject { impl PyCopy for *mut PyByteArrayObject { unsafe fn copy(self) -> PyResult { unsafe { - let size = PyBufPtr::len(self); + let size = self.length(); let copied = check!(py::bytearray::new(size)); if size > 0 { - ptr::copy_nonoverlapping(self.as_ptr(), copied.as_ptr(), size as usize); + ptr::copy_nonoverlapping(self.as_mut_ptr(), copied.as_mut_ptr(), size as usize); } PyResult::ok(copied) } diff --git a/src/deepcopy.rs b/src/deepcopy.rs index 56fa1e9..c38f91a 100644 --- a/src/deepcopy.rs +++ b/src/deepcopy.rs @@ -153,7 +153,7 @@ impl PyDeepCopy for *mut PyListObject { for i in 0..sz { #[cfg(not(Py_3_12))] ellipsis.incref(); - copied.set_slot_steal_unchecked(i, ellipsis); + copied.steal_item_unchecked(i, ellipsis); } if memo.memoize(self, copied, &probe) < 0 { @@ -190,7 +190,7 @@ impl PyDeepCopy for *mut PyListObject { } else { #[cfg(not(Py_3_12))] let old_item = copied.get_borrowed_unchecked(i); - copied.set_slot_steal_unchecked(i, raw); + copied.steal_item_unchecked(i, raw); #[cfg(not(Py_3_12))] old_item.decref(); } @@ -230,7 +230,7 @@ impl PyDeepCopy for *mut PyTupleObject { if raw != item { all_same = false; } - copied.set_slot_steal_unchecked(i, raw); + copied.steal_item_unchecked(i, raw); } if all_same { @@ -298,7 +298,7 @@ impl PyDeepCopy for *mut PyDictObject { return PyResult::error(); } - let rc = copied.set_item_steal_two(key_copy.into_raw(), val_copy.into_raw()); + let rc = copied.steal_item(key_copy.into_raw(), val_copy.into_raw()); if unlikely(rc < 0) { memo.forget(self, &probe); @@ -328,7 +328,7 @@ impl PyDeepCopy for *mut PySetObject { let mut hash: Py_hash_t = 0; while self.next_entry(&mut pos, &mut item, &mut hash) { item.incref(); - snapshot.set_slot_steal_unchecked(i, item); + snapshot.steal_item_unchecked(i, item); i += 1; } }); @@ -394,7 +394,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { let mut i: Py_ssize_t = 0; while self.next_entry(&mut pos, &mut item, &mut hash) { item.incref(); - snapshot.set_slot_steal_unchecked(i, item); + snapshot.steal_item_unchecked(i, item); i += 1; } @@ -412,7 +412,7 @@ impl PyDeepCopy for *mut PyFrozensetObject { items.decref(); return PyResult::error(); } - items.set_slot_steal_unchecked(j, item_copy.into_raw()); + items.steal_item_unchecked(j, item_copy.into_raw()); } snapshot.decref(); diff --git a/src/memo/native.rs b/src/memo/native.rs index eafbbda..3f09ede 100644 --- a/src/memo/native.rs +++ b/src/memo/native.rs @@ -95,7 +95,7 @@ impl PyMemoObject { #[cold] pub unsafe fn sync_from_dict(&mut self, dict: *mut PyDictObject, orig_size: Py_ssize_t) -> i32 { unsafe { - let cur_size = dict.len(); + let cur_size = dict.size(); if cur_size <= orig_size { return 0; } diff --git a/src/patch.rs b/src/patch.rs index 2239d66..1dfe7fe 100644 --- a/src/patch.rs +++ b/src/patch.rs @@ -225,7 +225,7 @@ unsafe fn build_patched_code(target: *mut PyObject) -> *mut PyObject { } template_consts.decref(); - let consts_tuple = new_consts.as_tuple(); + let consts_tuple = new_consts.to_tuple(); new_consts.decref(); if consts_tuple.is_null() { return ptr::null_mut(); diff --git a/src/py/dict.rs b/src/py/dict.rs index d9737a7..1ea3a57 100644 --- a/src/py/dict.rs +++ b/src/py/dict.rs @@ -16,6 +16,11 @@ pub type PyDictWatchCallback = Option< pub unsafe trait PyMapPtr: Sized { unsafe fn size(self) -> Py_ssize_t; + #[inline(always)] + unsafe fn len(self) -> Py_ssize_t { + self.size() + } + unsafe fn set_item(self, key: *mut K, value: *mut V) -> c_int; unsafe fn steal_item(self, key: *mut K, value: *mut V) -> c_int; @@ -32,20 +37,6 @@ pub unsafe trait PyMapPtr: Sized { unsafe fn set_item_cstr(self, key: &CStr, value: *mut V) -> c_int; unsafe fn watch(self, watcher_id: i32) -> c_int; unsafe fn unwatch(self, watcher_id: i32) -> c_int; - - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - self.size() - } - - #[inline(always)] - unsafe fn set_item_steal_two( - self, - key: *mut K, - value: *mut V, - ) -> c_int { - self.steal_item(key, value) - } } unsafe impl PyMapPtr for *mut PyDictObject { @@ -158,21 +149,6 @@ pub unsafe fn new_presized(length: Py_ssize_t) -> *mut PyDictObject { ffi::_PyDict_NewPresized(length) as *mut PyDictObject } -#[inline(always)] -pub unsafe fn size(dictionary: *mut D) -> Py_ssize_t { - pyo3_ffi::PyDict_Size(dictionary as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn next( - dictionary: *mut D, - position: &mut Py_ssize_t, - key: &mut *mut PyObject, - value: &mut *mut PyObject, -) -> bool { - pyo3_ffi::PyDict_Next(dictionary as *mut PyObject, position, key, value) != 0 -} - #[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] #[inline(always)] pub unsafe fn add_watcher(callback: PyDictWatchCallback) -> i32 { diff --git a/src/py/ffi.rs b/src/py/ffi.rs index 39816cc..0b568d6 100644 --- a/src/py/ffi.rs +++ b/src/py/ffi.rs @@ -219,8 +219,3 @@ macro_rules! cstr { } }}; } - -#[inline(always)] -pub fn ptr_from_cstr(value: &CStr) -> *const c_char { - value.as_ptr() -} diff --git a/src/py/list.rs b/src/py/list.rs index c2ed5e0..f5b3c59 100644 --- a/src/py/list.rs +++ b/src/py/list.rs @@ -7,11 +7,6 @@ pub unsafe trait PyMutSeqPtr: Sized { unsafe fn append(self, item: *mut T) -> c_int; unsafe fn insert(self, index: Py_ssize_t, item: *mut T) -> c_int; unsafe fn to_tuple(self) -> *mut PyTupleObject; - - #[inline(always)] - unsafe fn as_tuple(self) -> *mut PyTupleObject { - self.to_tuple() - } } unsafe impl PyMutSeqPtr for *mut PyListObject { @@ -35,38 +30,3 @@ unsafe impl PyMutSeqPtr for *mut PyListObject { pub unsafe fn new(length: Py_ssize_t) -> *mut PyListObject { pyo3_ffi::PyList_New(length) as *mut PyListObject } - -#[inline(always)] -pub unsafe fn append(list: *mut L, item: *mut V) -> c_int { - pyo3_ffi::PyList_Append(list as *mut PyObject, item as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn set_item(list: *mut L, index: Py_ssize_t, item: *mut PyObject) -> c_int { - pyo3_ffi::PyList_SetItem(list as *mut PyObject, index, item) -} - -#[inline(always)] -pub unsafe fn borrow_item(list: *mut L, index: Py_ssize_t) -> *mut PyObject { - pyo3_ffi::PyList_GET_ITEM(list as *mut PyObject, index) -} - -#[inline(always)] -pub unsafe fn size(list: *mut L) -> Py_ssize_t { - pyo3_ffi::PyList_GET_SIZE(list as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn insert(list: *mut L, index: Py_ssize_t, item: *mut V) -> c_int { - pyo3_ffi::PyList_Insert(list as *mut PyObject, index, item as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn check(list: *mut L) -> bool { - pyo3_ffi::PyList_Check(list as *mut PyObject) != 0 -} - -#[inline(always)] -pub unsafe fn as_tuple(list: *mut L) -> *mut PyTupleObject { - pyo3_ffi::PyList_AsTuple(list as *mut PyObject) as *mut PyTupleObject -} diff --git a/src/py/long.rs b/src/py/long.rs index bcfcb15..25a57b7 100644 --- a/src/py/long.rs +++ b/src/py/long.rs @@ -25,22 +25,7 @@ pub unsafe fn from_i64(value: i64) -> *mut PyLongObject { pyo3_ffi::PyLong_FromLong(value as libc::c_long) as *mut PyLongObject } -#[inline(always)] -pub unsafe fn as_i64(value: *mut T) -> i64 { - value.as_i64() -} - #[inline(always)] pub unsafe fn from_ptr(pointer: *mut T) -> *mut PyLongObject { pyo3_ffi::PyLong_FromVoidPtr(pointer as *mut c_void) as *mut PyLongObject } - -#[inline(always)] -pub unsafe fn as_ptr(value: *mut T) -> *mut c_void { - value.as_void_ptr() -} - -#[inline(always)] -pub unsafe fn check(value: *mut T) -> bool { - pyo3_ffi::PyLong_Check(value as *mut PyObject) != 0 -} diff --git a/src/py/module.rs b/src/py/module.rs index b42f4ef..9e11097 100644 --- a/src/py/module.rs +++ b/src/py/module.rs @@ -41,29 +41,6 @@ pub unsafe fn def_init(definition: *mut PyModuleDef) -> *mut PyObject { pyo3_ffi::PyModuleDef_Init(definition) } -#[inline(always)] -pub unsafe fn add_object( - module: *mut M, - name: &CStr, - object: *mut V, -) -> c_int { - module.add_module_object(name, object) -} - -#[inline(always)] -pub unsafe fn add_string_constant( - module: *mut M, - name: &CStr, - value: &CStr, -) -> c_int { - module.add_module_string_constant(name, value) -} - -#[inline(always)] -pub unsafe fn get_name(module: *mut M) -> *mut PyUnicodeObject { - module.module_name() -} - #[inline(always)] pub unsafe fn import(name: &CStr) -> *mut PyObject { pyo3_ffi::PyImport_ImportModule(name.as_ptr()) diff --git a/src/py/object.rs b/src/py/object.rs index 10c5e5e..6e4e48e 100644 --- a/src/py/object.rs +++ b/src/py/object.rs @@ -8,17 +8,20 @@ use std::ptr; pub unsafe trait PyObjectPtr: Sized { unsafe fn id(self) -> *mut PyLongObject; - unsafe fn as_object(self) -> *mut PyObject; unsafe fn class(self) -> *mut PyTypeObject; unsafe fn refcount(self) -> Py_ssize_t; unsafe fn incref(self); unsafe fn decref(self); - unsafe fn decref_if_nonnull(self); + unsafe fn decref_nullable(self); unsafe fn newref(self) -> *mut PyObject; unsafe fn getattr(self, name: *mut N) -> *mut PyObject; unsafe fn getattr_cstr(self, name: &CStr) -> *mut PyObject; - unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) -> c_int; + unsafe fn get_optional_attr( + self, + name: *mut N, + result: &mut *mut PyObject, + ) -> c_int; unsafe fn set_attr(self, name: *mut N, value: *mut V) -> c_int; unsafe fn set_attr_cstr(self, name: &CStr, value: *mut V) -> c_int; unsafe fn del_attr(self, name: *mut N) -> c_int; @@ -59,19 +62,6 @@ pub unsafe trait PyObjectPtr: Sized { unsafe fn is_bytes(self) -> bool; unsafe fn is_none(self) -> bool; - #[inline(always)] - unsafe fn decref_nullable(self) { - self.decref_if_nonnull() - } - - #[inline(always)] - unsafe fn get_optional_attr( - self, - name: *mut N, - result: &mut *mut PyObject, - ) -> c_int { - self.getattr_opt(name, result) - } } unsafe impl PyObjectPtr for *mut T { @@ -80,11 +70,6 @@ unsafe impl PyObjectPtr for *mut T { pyo3_ffi::PyLong_FromVoidPtr(self as *mut c_void) as *mut PyLongObject } - #[inline(always)] - unsafe fn as_object(self) -> *mut PyObject { - self as *mut PyObject - } - #[inline(always)] unsafe fn class(self) -> *mut PyTypeObject { (*(self as *mut PyObject)).ob_type @@ -106,7 +91,7 @@ unsafe impl PyObjectPtr for *mut T { } #[inline(always)] - unsafe fn decref_if_nonnull(self) { + unsafe fn decref_nullable(self) { if !self.is_null() { self.decref(); } @@ -128,7 +113,11 @@ unsafe impl PyObjectPtr for *mut T { } #[inline(always)] - unsafe fn getattr_opt(self, name: *mut N, result: &mut *mut PyObject) -> c_int { + unsafe fn get_optional_attr( + self, + name: *mut N, + result: &mut *mut PyObject, + ) -> c_int { ffi::PyObject_GetOptionalAttr(self as *mut PyObject, name as *mut PyObject, result) } @@ -308,81 +297,6 @@ unsafe impl PyObjectPtr for *mut T { } } -#[inline(always)] -pub unsafe fn getattr_cstr(object: *mut O, name: &CStr) -> *mut PyObject { - object.getattr_cstr(name) -} - -#[inline(always)] -pub unsafe fn set_attr_cstr( - object: *mut O, - name: &CStr, - value: *mut V, -) -> c_int { - object.set_attr_cstr(name, value) -} - -#[inline(always)] -pub unsafe fn del_attr_cstr(object: *mut O, name: &CStr) -> c_int { - object.del_attr_cstr(name) -} - -#[inline(always)] -pub unsafe fn has_attr_cstr(object: *mut O, name: &CStr) -> bool { - object.has_attr_cstr(name) -} - -#[inline(always)] -pub unsafe fn call_no_args(object: *mut O) -> *mut PyObject { - object.call() -} - -#[inline(always)] -pub unsafe fn call_one_arg( - object: *mut O, - argument: *mut A, -) -> *mut PyObject { - object.call_one(argument) -} - -#[inline(always)] -pub unsafe fn call_with( - object: *mut O, - args: *mut A, -) -> *mut PyObject { - object.call_with(args) -} - -#[inline(always)] -pub unsafe fn call_with_kwargs( - object: *mut O, - args: *mut A, - kwargs: *mut K, -) -> *mut PyObject { - object.call_with_kwargs(args, kwargs) -} - -#[inline(always)] -pub unsafe fn get_iter(object: *mut O) -> *mut PyObject { - object.get_iter() -} - -#[cfg(all(Py_3_14, Py_GIL_DISABLED))] -#[inline(always)] -pub unsafe fn iter_next_item(iterator: *mut I, item: &mut *mut PyObject) -> i32 { - iterator.iter_next_item(item) -} - -#[inline(always)] -pub unsafe fn repr(object: *mut O) -> *mut PyUnicodeObject { - object.repr() -} - -#[inline(always)] -pub unsafe fn str_(object: *mut O) -> *mut PyUnicodeObject { - object.str_() -} - pub unsafe trait PyObjectSlotPtr { unsafe fn clear(self); } @@ -396,6 +310,6 @@ unsafe impl PyObjectSlotPtr for *mut *mut T { let old_value = *self; *self = ptr::null_mut(); - old_value.decref_if_nonnull(); + old_value.decref_nullable(); } } diff --git a/src/py/seq.rs b/src/py/seq.rs index 241025a..e9f6624 100644 --- a/src/py/seq.rs +++ b/src/py/seq.rs @@ -13,17 +13,9 @@ unsafe fn is_valid_index(index: Py_ssize_t, limit: Py_ssize_t) -> bool { pub unsafe trait PySeqPtr: Sized { unsafe fn length(&self) -> Py_ssize_t; - unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject; + unsafe fn get_borrowed_unchecked(self, index: Py_ssize_t) -> *mut PyObject; unsafe fn steal_item_unchecked(self, index: Py_ssize_t, value: *mut T); - #[inline(always)] - unsafe fn borrow_item(self, index: Py_ssize_t) -> *mut PyObject { - if unlikely(!is_valid_index(index, self.length())) { - return ptr::null_mut(); - } - self.borrow_item_unchecked(index) - } - #[inline(always)] unsafe fn steal_item(self, index: Py_ssize_t, value: *mut T) -> c_int { if unlikely(!is_valid_index(index, self.length())) { @@ -34,26 +26,11 @@ pub unsafe trait PySeqPtr: Sized { } #[inline(always)] - unsafe fn own_item(self, index: Py_ssize_t) -> *mut PyObject { + unsafe fn get_owned_check_bounds(self, index: Py_ssize_t) -> *mut PyObject { if unlikely(!is_valid_index(index, self.length())) { return ptr::null_mut(); } - self.borrow_item_unchecked(index).newref() - } - - #[inline(always)] - unsafe fn get_borrowed_unchecked(self, index: Py_ssize_t) -> *mut PyObject { - self.borrow_item_unchecked(index) - } - - #[inline(always)] - unsafe fn set_slot_steal_unchecked(self, index: Py_ssize_t, value: *mut T) { - self.steal_item_unchecked(index, value) - } - - #[inline(always)] - unsafe fn get_owned_check_bounds(self, index: Py_ssize_t) -> *mut PyObject { - self.own_item(index) + self.get_borrowed_unchecked(index).newref() } } @@ -64,7 +41,7 @@ unsafe impl PySeqPtr for *mut PyListObject { } #[inline(always)] - unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + unsafe fn get_borrowed_unchecked(self, index: Py_ssize_t) -> *mut PyObject { pyo3_ffi::PyList_GET_ITEM(self as *mut PyObject, index) } @@ -81,7 +58,7 @@ unsafe impl PySeqPtr for *mut PyTupleObject { } #[inline(always)] - unsafe fn borrow_item_unchecked(self, index: Py_ssize_t) -> *mut PyObject { + unsafe fn get_borrowed_unchecked(self, index: Py_ssize_t) -> *mut PyObject { pyo3_ffi::PyTuple_GET_ITEM(self as *mut PyObject, index) } @@ -90,31 +67,3 @@ unsafe impl PySeqPtr for *mut PyTupleObject { pyo3_ffi::PyTuple_SET_ITEM(self as *mut PyObject, index, value as *mut PyObject) } } - -#[inline(always)] -pub unsafe fn fast(object: *mut T, message: &CStr) -> *mut PyObject { - ffi::PySequence_Fast(object as *mut PyObject, message.as_ptr()) -} - -#[inline(always)] -pub unsafe fn fast_borrow_item_unchecked( - fast_sequence: *mut T, - index: Py_ssize_t, -) -> *mut PyObject { - ffi::PySequence_Fast_GET_ITEM(fast_sequence as *mut PyObject, index) -} - -#[inline(always)] -pub unsafe fn fast_length(fast_sequence: *mut T) -> Py_ssize_t { - ffi::PySequence_Fast_GET_SIZE(fast_sequence as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn to_tuple(object: *mut T) -> *mut PyTupleObject { - pyo3_ffi::PySequence_Tuple(object as *mut PyObject) as *mut PyTupleObject -} - -#[inline(always)] -pub unsafe fn to_list(object: *mut T) -> *mut PyListObject { - pyo3_ffi::PySequence_List(object as *mut PyObject) as *mut PyListObject -} diff --git a/src/py/set.rs b/src/py/set.rs index 6bc423f..607250f 100644 --- a/src/py/set.rs +++ b/src/py/set.rs @@ -6,26 +6,21 @@ use super::{ffi, PyFrozensetObject, PyTypeInfo}; pub unsafe trait PySetPtr: Sized { unsafe fn size(self) -> Py_ssize_t; + #[inline(always)] + unsafe fn len(self) -> Py_ssize_t { + self.size() + } + unsafe fn next_entry( self, position: &mut Py_ssize_t, key: &mut *mut PyObject, hash: &mut Py_hash_t, ) -> bool; - - #[inline(always)] - unsafe fn len(self) -> Py_ssize_t { - self.size() - } } pub unsafe trait PyMutSetPtr: Sized { - unsafe fn add(self, item: *mut T) -> c_int; - - #[inline(always)] - unsafe fn add_item(self, item: *mut T) -> c_int { - self.add(item) - } + unsafe fn add_item(self, item: *mut T) -> c_int; } unsafe impl PySetPtr for *mut PySetObject { @@ -64,7 +59,7 @@ unsafe impl PySetPtr for *mut PyFrozensetObject { unsafe impl PyMutSetPtr for *mut PySetObject { #[inline(always)] - unsafe fn add(self, item: *mut T) -> c_int { + unsafe fn add_item(self, item: *mut T) -> c_int { pyo3_ffi::PySet_Add(self as *mut PyObject, item as *mut PyObject) } } diff --git a/src/py/tuple.rs b/src/py/tuple.rs index 73476b9..b855036 100644 --- a/src/py/tuple.rs +++ b/src/py/tuple.rs @@ -1,29 +1,6 @@ use pyo3_ffi::*; -use std::os::raw::c_int; - -use super::PyTypeInfo; #[inline(always)] pub unsafe fn new(length: Py_ssize_t) -> *mut PyTupleObject { pyo3_ffi::PyTuple_New(length) as *mut PyTupleObject } - -#[inline(always)] -pub unsafe fn size(tuple: *mut T) -> Py_ssize_t { - pyo3_ffi::PyTuple_GET_SIZE(tuple as *mut PyObject) -} - -#[inline(always)] -pub unsafe fn get_item(tuple: *mut T, index: Py_ssize_t) -> *mut PyObject { - pyo3_ffi::PyTuple_GET_ITEM(tuple as *mut PyObject, index) -} - -#[inline(always)] -pub unsafe fn set_item(tuple: *mut T, index: Py_ssize_t, item: *mut PyObject) -> c_int { - pyo3_ffi::PyTuple_SetItem(tuple as *mut PyObject, index, item) -} - -#[inline(always)] -pub unsafe fn check(tuple: *mut T) -> bool { - pyo3_ffi::PyTuple_Check(tuple as *mut PyObject) != 0 -} diff --git a/src/py/type_object.rs b/src/py/type_object.rs index 9514c57..8f1a6e3 100644 --- a/src/py/type_object.rs +++ b/src/py/type_object.rs @@ -70,8 +70,3 @@ unsafe impl PyTypeObjectPtr for *mut PyTypeObject { pyo3_ffi::PyType_Ready(self) } } - -#[inline(always)] -pub unsafe fn ready(type_object: *mut PyTypeObject) -> c_int { - type_object.ready() -} diff --git a/src/py/unicode.rs b/src/py/unicode.rs index 18a6232..2e0cc0b 100644 --- a/src/py/unicode.rs +++ b/src/py/unicode.rs @@ -63,51 +63,12 @@ pub unsafe fn from_cstr(value: &CStr) -> *mut PyUnicodeObject { pyo3_ffi::PyUnicode_FromString(value.as_ptr()) as *mut PyUnicodeObject } -#[inline(always)] -pub unsafe fn from_cstr_and_size(value: &CStr, size: Py_ssize_t) -> *mut PyUnicodeObject { - pyo3_ffi::PyUnicode_FromStringAndSize(value.as_ptr(), size) as *mut PyUnicodeObject -} - #[inline(always)] pub unsafe fn from_str_and_size(value: &str) -> *mut PyUnicodeObject { pyo3_ffi::PyUnicode_FromStringAndSize(value.as_ptr().cast(), value.len() as Py_ssize_t) as *mut PyUnicodeObject } -#[inline(always)] -pub unsafe fn as_utf8<'a, T: PyTypeInfo>(value: *mut T) -> &'a CStr { - value.as_utf8() -} - -#[inline(always)] -pub unsafe fn byte_length(value: *mut T) -> Py_ssize_t { - value.byte_length() -} - -#[inline(always)] -pub unsafe fn compare_ascii(value: *mut T, string: &CStr) -> i32 { - value.compare_ascii(string) -} - -#[inline(always)] -pub unsafe fn join( - separator: *mut S, - sequence: *mut Q, -) -> *mut PyUnicodeObject { - separator.join(sequence) -} - -#[inline(always)] -pub unsafe fn tailmatch( - value: *mut T, - substring: *mut S, - start: Py_ssize_t, - end: Py_ssize_t, - direction: i32, -) -> bool { - value.tailmatch(substring, start, end, direction) -} - macro_rules! from_format { ($format_string:expr $(, $argument:expr )* $(,)?) => {{ #[allow(unused_unsafe)] diff --git a/src/reduce.rs b/src/reduce.rs index caabea4..cc72ba6 100644 --- a/src/reduce.rs +++ b/src/reduce.rs @@ -194,7 +194,7 @@ pub(crate) unsafe fn validate_reduce_tuple( return (ReduceKind::Error, empty); } let old = argument_tuple_object; - reduce_tuple.set_slot_steal_unchecked(1, coerced); + reduce_tuple.steal_item_unchecked(1, coerced); old.decref(); coerced }; @@ -281,7 +281,7 @@ unsafe fn reconstruct_newobj( arguments.decref(); return ptr::null_mut(); } - arguments.set_slot_steal_unchecked(argument_index - 1, copied.into_raw()); + arguments.steal_item_unchecked(argument_index - 1, copied.into_raw()); } let instance = call_tp_new(class_pointer, arguments.cast(), ptr::null_mut()); @@ -408,7 +408,7 @@ unsafe fn reconstruct_callable( copied_args.decref(); return ptr::null_mut(); } - copied_args.set_slot_steal_unchecked(argument_index, copied.into_raw()); + copied_args.steal_item_unchecked(argument_index, copied.into_raw()); } let instance = callable.call_with(copied_args);