mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Expose more attributes as member descriptors (#8857)
* Read type.__module__ from the dict only for heap types A static type's `__module__` is the text before the last `.` of its name, or `builtins`. A heap type reads its dict and raises `AttributeError` when the entry is missing; `type` and `object` reprs ignore that error. `type()` no longer stores `__module__ = None` when the caller's globals have no `__name__`. `#[pyexception(module = ...)]` now reaches the type name, and static types that relied on a dict `__module__` (ast nodes, pyexpat, ssl, _hashlib and _interpchannels errors, ExceptionGroup) carry their module. Assisted-by: Grok:4.7 Assisted-by: Claude:claude-opus-5-5 * Expose more builtin attributes as member descriptors `code.co_flags`, `co_nlocals`, `co_filename`, `super.__self__`, `__self_class__`, `__thisclass__`, `traceback.tb_frame`, `tb_lasti`, `type.__itemsize__` and `builtin_function_or_method.__module__` are now `#[pymember]` fields instead of getsets. `super` keeps its three references in atomic cells instead of a lock, `tb_lasti` is an `i32`, `co_nlocals` is stored at construction, and the builtin function `__module__` is a writable object cell. Assisted-by: Grok:4.7 Assisted-by: Claude:claude-opus-5-5 * Expose stdlib attributes as member descriptors Decompressor `eof`/`needs_input`/`unused_data` (bz2, lzma, zlib, plus lzma `check`), `ContextVar.name`, `functools.partial` `func`/`args`/ `keywords`, socket `family`/`type`/`proto`, `select.kevent` fields, `SemLock` `handle`/`kind`/`maxvalue`/`name`, `Pickler.bin`/`fast`, `xmlparser.intern`, ElementTree `XMLParser.entity`/`target`, `UCD.unidata_version`, `_ExceptHookArgs` fields, multibytecodec `stream`, and `_io` `Buffered*.raw` and `TextIOWrapper` `buffer`/`encoding`/`line_buffering`/`write_through` are now `#[pymember]` fields instead of getsets. Values that changed under a lock move to atomic cells on the payload. New member kinds `Short`, `Long`, `String`, `UShort`, `ULong`, `LongLong` and `ULongLong` access exactly the width of their C type. Assisted-by: Grok:4.7 Assisted-by: Claude:claude-opus-5-5 * Match bare super and static type itemsize `super.__new__(super)` leaves `__thisclass__`, `__self__` and `__self_class__` empty, and a super with no type takes the zero-argument path when bound. Static types carry their `tp_itemsize` (`type`, `memoryview`, `code`, `frame`, generators, `_sre` objects, `_ctypes` thunks), inherit it from their direct base when they leave it at 0, and a variable-size base rejects any nonempty `__slots__` and a weakref slot. `_thread._ExceptHookArgs` is a struct sequence. Assisted-by: Grok:4.7 Assisted-by: Claude:claude-opus-5-5 * Expose SimpleNamespace.__dict__, __basicsize__ and __flags__ as members `SimpleNamespace.__dict__` addresses the instance dict cell the same way `module.__dict__` does. `slots.basicsize` now holds the full `tp_basicsize` (header plus payload), so `__basicsize__` reads it directly. Type flags are one atomic word: `__abstractmethods__` and `_abc` set their bits in it in place, the separate `abc_tpflags` word is gone, and `__flags__` reads the word as an unsigned long member. Assisted-by: Grok:4.7 Assisted-by: Claude:claude-opus-5-5
This commit is contained in:
1 parent
f87170772e
commit
31963fa767
58 files changed
+2010
-1251
No files matched your search
@@ -130,7 +130,8 @@ impl PyMemberDef {
|
||||
let mut flags = self.flags;
|
||||
if flags & PY_RELATIVE_OFFSET != 0 {
|
||||
// type creation adds tp_basicsize and clears the flag before GetOne.
|
||||
offset += (rustpython_vm::object::SIZEOF_PYOBJECT_HEAD + ty.slots.basicsize) as isize;
|
||||
// `slots.basicsize` is already the full tp_basicsize.
|
||||
offset += ty.slots.basicsize as isize;
|
||||
flags &= !PY_RELATIVE_OFFSET;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ pub unsafe extern "C" fn PyType_GetFullyQualifiedName(ptr: *mut PyTypeObject) ->
|
||||
with_vm(|vm| {
|
||||
let ty = unsafe { ptr.assume_borrowed() };
|
||||
let qualname = ty.__qualname__(vm).try_downcast::<PyStr>(vm)?;
|
||||
let module = ty.__module__(vm);
|
||||
let module = ty.__module__(vm)?;
|
||||
|
||||
if let Some(module) = module.downcast_ref::<PyStr>()
|
||||
&& module.as_wtf8() != "builtins"
|
||||
|
||||
@@ -520,6 +520,11 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(core::mem::size_of::<CodeFlags>() == core::mem::size_of::<i32>());
|
||||
assert!(core::mem::align_of::<CodeFlags>() == core::mem::align_of::<i32>());
|
||||
};
|
||||
|
||||
impl CodeFlags {
|
||||
/// The `__future__` flags that `compile()` accepts and that a compiled code
|
||||
/// object inherits from its caller. Mirrors `PyCF_MASK`.
|
||||
|
||||
@@ -551,12 +551,14 @@ fn generate_class_def(
|
||||
}
|
||||
});
|
||||
// If repr(transparent) with a base, the type has the same memory layout as base,
|
||||
// so basicsize should be 0 (no additional space beyond the base type)
|
||||
// Otherwise, basicsize = sizeof(payload). The header size is added in __basicsize__ getter.
|
||||
// so basicsize stays 0 and type creation copies the base's full tp_basicsize.
|
||||
// Otherwise, basicsize is the object header plus the payload.
|
||||
let basicsize = if is_repr_transparent && base.is_some() {
|
||||
quote!(0)
|
||||
} else {
|
||||
quote!(std::mem::size_of::<#ident>())
|
||||
quote!(
|
||||
::rustpython_vm::object::SIZEOF_PYOBJECT_HEAD + ::core::mem::size_of::<#ident>()
|
||||
)
|
||||
};
|
||||
if base.is_some() && is_pystruct {
|
||||
bail_span!(ident, "PyStructSequence cannot have `base` class attr",);
|
||||
@@ -914,8 +916,13 @@ pub(crate) fn impl_pyexception(attr: PunctuatedNestedMeta, item: &Item) -> Resul
|
||||
None => quote! {},
|
||||
};
|
||||
|
||||
let module_attr = match class_meta.optional_module()? {
|
||||
Some(module) => quote! { module = #module, },
|
||||
None => quote! { module = false, },
|
||||
};
|
||||
|
||||
let ret = quote! {
|
||||
#[pyclass(module = false, name = #class_name, base = #base_class_name #traverse_attr #payload_attr)]
|
||||
#[pyclass(#module_attr name = #class_name, base = #base_class_name #traverse_attr #payload_attr)]
|
||||
#item
|
||||
#impl_pyclass
|
||||
};
|
||||
|
||||
@@ -534,6 +534,15 @@ impl ExceptionItemMeta {
|
||||
pub(crate) fn has_impl(&self) -> Result<bool> {
|
||||
self.inner()._bool("impl")
|
||||
}
|
||||
|
||||
/// `module = "..."` when present. Omitted means a builtin exception.
|
||||
pub(crate) fn optional_module(&self) -> Result<Option<String>> {
|
||||
if self.inner().meta_map.contains_key("module") {
|
||||
self.module()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for ExceptionItemMeta {
|
||||
|
||||
@@ -18,6 +18,7 @@ use libc::sem_t;
|
||||
use nix::errno::Errno;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct SemHandle {
|
||||
raw: *mut sem_t,
|
||||
@@ -118,6 +119,7 @@ pub type RawSocket = SOCKET;
|
||||
pub const INFINITE_TIMEOUT: u32 = INFINITE;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct SemHandle {
|
||||
raw: HANDLE,
|
||||
|
||||
+27
-29
@@ -6,14 +6,15 @@ pub(crate) use _bz2::module_def;
|
||||
mod _bz2 {
|
||||
use crate::compression::DecompressorArgs;
|
||||
use crate::vm::{
|
||||
Py, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytesRef, PyType},
|
||||
Py, PyObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytes, PyType},
|
||||
common::lock::PyMutex,
|
||||
function::ArgBytesLike,
|
||||
object::PyResult,
|
||||
object::{PyAtomicRef, PyResult},
|
||||
types::Constructor,
|
||||
};
|
||||
use alloc::fmt;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use rustpython_common::compression::bz2 as backend;
|
||||
|
||||
fn map_bz2_error(error: backend::Bz2Error, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
@@ -31,15 +32,6 @@ mod _bz2 {
|
||||
|
||||
struct PyBZ2DecompressorInner {
|
||||
decompress: backend::Decompressor,
|
||||
unused_data: PyBytesRef,
|
||||
}
|
||||
|
||||
impl PyBZ2DecompressorInner {
|
||||
fn sync_visible_state(&mut self, vm: &VirtualMachine) {
|
||||
if self.unused_data.as_bytes() != self.decompress.unused_data() {
|
||||
self.unused_data = vm.ctx.new_bytes(self.decompress.unused_data().to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
@@ -48,6 +40,14 @@ mod _bz2 {
|
||||
struct BZ2Decompressor {
|
||||
#[pytraverse(skip)]
|
||||
inner: PyMutex<PyBZ2DecompressorInner>,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
eof: AtomicBool,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
needs_input: AtomicBool,
|
||||
#[pymember(type = "object_ex")]
|
||||
unused_data: PyAtomicRef<Option<PyObject>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for BZ2Decompressor {
|
||||
@@ -63,8 +63,10 @@ mod _bz2 {
|
||||
Ok(Self {
|
||||
inner: PyMutex::new(PyBZ2DecompressorInner {
|
||||
decompress: backend::Decompressor::new(),
|
||||
unused_data: vm.ctx.empty_bytes.clone(),
|
||||
}),
|
||||
eof: AtomicBool::new(false),
|
||||
needs_input: AtomicBool::new(true),
|
||||
unused_data: PyAtomicRef::from(Some(vm.ctx.empty_bytes.clone().into())),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -84,25 +86,21 @@ mod _bz2 {
|
||||
return Err(vm.new_value_error("Decompressor is unusable after a previous error"));
|
||||
}
|
||||
let result = inner.decompress.decompress(data, max_length);
|
||||
inner.sync_visible_state(vm);
|
||||
self.eof.store(inner.decompress.eof(), Ordering::Relaxed);
|
||||
self.needs_input
|
||||
.store(inner.decompress.needs_input(), Ordering::Relaxed);
|
||||
let stale = self.unused_data.deref().is_none_or(|obj| {
|
||||
obj.downcast_ref::<PyBytes>()
|
||||
.is_none_or(|bytes| bytes.as_bytes() != inner.decompress.unused_data())
|
||||
});
|
||||
if stale {
|
||||
let bytes = vm.ctx.new_bytes(inner.decompress.unused_data().to_vec());
|
||||
// The previous bytes object is dropped after the slot is replaced.
|
||||
let _previous = unsafe { self.unused_data.swap(Some(bytes.into())) };
|
||||
}
|
||||
result.map_err(|error| map_bz2_error(error, vm))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn eof(&self) -> bool {
|
||||
self.inner.lock().decompress.eof()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn unused_data(&self) -> PyBytesRef {
|
||||
self.inner.lock().unused_data.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn needs_input(&self) -> bool {
|
||||
self.inner.lock().decompress.needs_input()
|
||||
}
|
||||
|
||||
#[pymethod(name = "__reduce__")]
|
||||
fn reduce(&self, vm: &VirtualMachine) -> PyResult<()> {
|
||||
Err(vm.new_type_error("cannot pickle '_bz2.BZ2Decompressor' object"))
|
||||
|
||||
@@ -974,6 +974,7 @@ mod _multibytecodec {
|
||||
struct MultibyteStreamReader {
|
||||
#[pytraverse(skip)]
|
||||
codec: CodecRef,
|
||||
#[pymember]
|
||||
stream: PyObjectRef,
|
||||
#[pytraverse(skip)]
|
||||
inner: PyMutex<DecoderState>,
|
||||
@@ -1006,11 +1007,6 @@ mod _multibytecodec {
|
||||
|
||||
#[pyclass(with(Constructor, Initializer), flags(BASETYPE))]
|
||||
impl MultibyteStreamReader {
|
||||
#[pygetset]
|
||||
fn stream(&self) -> PyObjectRef {
|
||||
self.stream.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn errors(&self, vm: &VirtualMachine) -> PyStrRef {
|
||||
self.inner.lock().errors.name(vm)
|
||||
@@ -1134,6 +1130,7 @@ mod _multibytecodec {
|
||||
struct MultibyteStreamWriter {
|
||||
#[pytraverse(skip)]
|
||||
codec: CodecRef,
|
||||
#[pymember]
|
||||
stream: PyObjectRef,
|
||||
#[pytraverse(skip)]
|
||||
inner: PyMutex<EncoderState>,
|
||||
@@ -1166,11 +1163,6 @@ mod _multibytecodec {
|
||||
|
||||
#[pyclass(with(Constructor, Initializer), flags(BASETYPE))]
|
||||
impl MultibyteStreamWriter {
|
||||
#[pygetset]
|
||||
fn stream(&self) -> PyObjectRef {
|
||||
self.stream.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn errors(&self, vm: &VirtualMachine) -> PyStrRef {
|
||||
self.inner.lock().errors.name(vm)
|
||||
|
||||
@@ -359,6 +359,7 @@ mod _contextvars {
|
||||
#[pyclass(name, traverse)]
|
||||
#[derive(PyPayload)]
|
||||
struct ContextVar {
|
||||
#[pymember]
|
||||
name: PyStrRef,
|
||||
default: Option<PyObjectRef>,
|
||||
#[pytraverse(skip)]
|
||||
@@ -436,11 +437,6 @@ mod _contextvars {
|
||||
|
||||
#[pyclass(with(Constructor, Hashable, Representable))]
|
||||
impl ContextVar {
|
||||
#[pygetset]
|
||||
fn name(&self) -> PyStrRef {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn get(
|
||||
zelf: &Py<Self>,
|
||||
|
||||
@@ -1829,11 +1829,9 @@ pub(crate) mod _elementtree {
|
||||
/// The `pyexpat.xmlparser` doing the actual scanning, or `None`
|
||||
/// before `__init__` (and after `close()` dropped it).
|
||||
parser: Option<PyObjectRef>,
|
||||
target: Option<PyObjectRef>,
|
||||
/// The target again, when it is exactly our own `TreeBuilder`, so a
|
||||
/// per-event dispatch is one clone rather than a type check.
|
||||
native_target: Option<PyRef<PyTreeBuilder>>,
|
||||
entity: Option<PyDictRef>,
|
||||
/// Cache of raw expat names to their `{uri}local` form.
|
||||
names: Option<PyDictRef>,
|
||||
handle_start: Option<PyObjectRef>,
|
||||
@@ -1855,6 +1853,10 @@ pub(crate) mod _elementtree {
|
||||
)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct PyXMLParser {
|
||||
#[pymember]
|
||||
entity: crate::vm::object::PyAtomicRef<Option<PyObject>>,
|
||||
#[pymember]
|
||||
target: crate::vm::object::PyAtomicRef<Option<PyObject>>,
|
||||
state: PyRwLock<XMLParserState>,
|
||||
}
|
||||
|
||||
@@ -1865,9 +1867,9 @@ pub(crate) mod _elementtree {
|
||||
return;
|
||||
};
|
||||
st.parser.traverse(traverse_fn);
|
||||
st.target.traverse(traverse_fn);
|
||||
st.native_target.traverse(traverse_fn);
|
||||
st.entity.traverse(traverse_fn);
|
||||
self.entity.traverse(traverse_fn);
|
||||
self.target.traverse(traverse_fn);
|
||||
st.names.traverse(traverse_fn);
|
||||
st.handle_start.traverse(traverse_fn);
|
||||
st.handle_end.traverse(traverse_fn);
|
||||
@@ -1887,9 +1889,9 @@ pub(crate) mod _elementtree {
|
||||
out.extend(
|
||||
[
|
||||
st.parser.take(),
|
||||
st.target.take(),
|
||||
unsafe { self.entity.swap(None) },
|
||||
unsafe { self.target.swap(None) },
|
||||
st.native_target.take().map(Into::into),
|
||||
st.entity.take().map(Into::into),
|
||||
st.names.take().map(Into::into),
|
||||
st.handle_start.take(),
|
||||
st.handle_end.take(),
|
||||
@@ -1920,6 +1922,8 @@ pub(crate) mod _elementtree {
|
||||
impl Default for PyXMLParser {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entity: crate::vm::object::PyAtomicRef::from(None),
|
||||
target: crate::vm::object::PyAtomicRef::from(None),
|
||||
state: PyRwLock::new(XMLParserState::default()),
|
||||
}
|
||||
}
|
||||
@@ -1963,9 +1967,11 @@ pub(crate) mod _elementtree {
|
||||
_ => PyTreeBuilder::default().into_ref(&vm.ctx).into(),
|
||||
};
|
||||
|
||||
let entity = vm.ctx.new_dict();
|
||||
let _previous = unsafe { zelf.entity.swap(Some(entity.into())) };
|
||||
let _previous = unsafe { zelf.target.swap(Some(target.clone())) };
|
||||
let handlers = XMLParserState {
|
||||
parser: Some(parser.clone()),
|
||||
entity: Some(vm.ctx.new_dict()),
|
||||
names: Some(vm.ctx.new_dict()),
|
||||
handle_start_ns: optional_handler(&target, "start_ns", vm)?,
|
||||
handle_end_ns: optional_handler(&target, "end_ns", vm)?,
|
||||
@@ -1981,7 +1987,6 @@ pub(crate) mod _elementtree {
|
||||
.is(PyTreeBuilder::class(&vm.ctx))
|
||||
.then(|| target.clone().downcast::<PyTreeBuilder>().ok())
|
||||
.flatten(),
|
||||
target: Some(target),
|
||||
};
|
||||
let has_comment = handlers.handle_comment.is_some();
|
||||
let has_pi = handlers.handle_pi.is_some();
|
||||
@@ -2044,7 +2049,7 @@ pub(crate) mod _elementtree {
|
||||
impl PyXMLParser {
|
||||
fn check(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
let st = self.state.read();
|
||||
if st.target.is_none() {
|
||||
if self.target.deref().is_none() {
|
||||
return Err(vm.new_value_error("XMLParser.__init__() wasn't called"));
|
||||
}
|
||||
st.parser
|
||||
@@ -2125,24 +2130,6 @@ pub(crate) mod _elementtree {
|
||||
|
||||
#[pyclass(with(Constructor, Initializer), flags(BASETYPE, HAS_WEAKREF))]
|
||||
impl PyXMLParser {
|
||||
#[pygetset]
|
||||
fn entity(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.state
|
||||
.read()
|
||||
.entity
|
||||
.clone()
|
||||
.map_or_else(|| vm.ctx.none(), Into::into)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn target(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.state
|
||||
.read()
|
||||
.target
|
||||
.clone()
|
||||
.unwrap_or_else(|| vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn version(&self, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let expat = vm.import("pyexpat", 0)?;
|
||||
|
||||
+34
-36
@@ -6,9 +6,11 @@ pub(crate) use _lzma::module_def;
|
||||
mod _lzma {
|
||||
use crate::compression::DecompressorArgs;
|
||||
use alloc::fmt;
|
||||
use core::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
use rustpython_common::{compression::lzma as backend, lock::PyMutex};
|
||||
use rustpython_vm::builtins::{PyBaseExceptionRef, PyBytesRef, PyDict, PyType, PyTypeRef};
|
||||
use rustpython_vm::builtins::{PyBaseExceptionRef, PyBytes, PyDict, PyType, PyTypeRef};
|
||||
use rustpython_vm::function::ArgBytesLike;
|
||||
use rustpython_vm::object::PyAtomicRef;
|
||||
use rustpython_vm::types::Constructor;
|
||||
use rustpython_vm::{Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine};
|
||||
|
||||
@@ -264,22 +266,25 @@ mod _lzma {
|
||||
|
||||
struct DecompressorInner {
|
||||
backend: backend::Decompressor,
|
||||
unused_data: PyBytesRef,
|
||||
}
|
||||
|
||||
impl DecompressorInner {
|
||||
fn sync_visible_state(&mut self, vm: &VirtualMachine) {
|
||||
if self.unused_data.as_bytes() != self.backend.unused_data() {
|
||||
self.unused_data = vm.ctx.new_bytes(self.backend.unused_data().to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "LZMADecompressor")]
|
||||
#[pyclass(name = "LZMADecompressor", traverse)]
|
||||
#[derive(PyPayload)]
|
||||
struct LZMADecompressor {
|
||||
#[pytraverse(skip)]
|
||||
state: PyMutex<DecompressorInner>,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
check: AtomicI32,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
eof: AtomicBool,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
needs_input: AtomicBool,
|
||||
#[pymember(type = "object_ex")]
|
||||
unused_data: PyAtomicRef<Option<PyObject>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for LZMADecompressor {
|
||||
@@ -314,11 +319,13 @@ mod _lzma {
|
||||
let filters = filters_to_backend(args.filters, vm)?;
|
||||
let backend = backend::Decompressor::new(args.format, args.memlimit, filters)
|
||||
.map_err(|error| map_backend_error(error, vm))?;
|
||||
let check = backend.check();
|
||||
Ok(Self {
|
||||
state: PyMutex::new(DecompressorInner {
|
||||
backend,
|
||||
unused_data: vm.ctx.empty_bytes.clone(),
|
||||
}),
|
||||
state: PyMutex::new(DecompressorInner { backend }),
|
||||
check: AtomicI32::new(check),
|
||||
eof: AtomicBool::new(false),
|
||||
needs_input: AtomicBool::new(true),
|
||||
unused_data: PyAtomicRef::from(Some(vm.ctx.empty_bytes.clone().into())),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -331,29 +338,20 @@ mod _lzma {
|
||||
let data = &*args.data();
|
||||
let mut state = self.state.lock();
|
||||
let result = state.backend.decompress(data, max_length);
|
||||
state.sync_visible_state(vm);
|
||||
self.check.store(state.backend.check(), Ordering::Relaxed);
|
||||
self.eof.store(state.backend.eof(), Ordering::Relaxed);
|
||||
self.needs_input
|
||||
.store(state.backend.needs_input(), Ordering::Relaxed);
|
||||
let stale = self.unused_data.deref().is_none_or(|obj| {
|
||||
obj.downcast_ref::<PyBytes>()
|
||||
.is_none_or(|bytes| bytes.as_bytes() != state.backend.unused_data())
|
||||
});
|
||||
if stale {
|
||||
let bytes = vm.ctx.new_bytes(state.backend.unused_data().to_vec());
|
||||
let _previous = unsafe { self.unused_data.swap(Some(bytes.into())) };
|
||||
}
|
||||
result.map_err(|error| map_backend_error(error, vm))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn check(&self) -> i32 {
|
||||
self.state.lock().backend.check()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn eof(&self) -> bool {
|
||||
self.state.lock().backend.eof()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn unused_data(&self) -> PyBytesRef {
|
||||
self.state.lock().unused_data.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn needs_input(&self) -> bool {
|
||||
self.state.lock().backend.needs_input()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
|
||||
@@ -1,5 +1,73 @@
|
||||
pub(crate) use _multiprocessing::module_def;
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
const _: () = assert!(
|
||||
core::mem::size_of::<rustpython_host_env::multiprocessing::SemHandle>()
|
||||
== core::mem::size_of::<usize>()
|
||||
&& core::mem::align_of::<rustpython_host_env::multiprocessing::SemHandle>()
|
||||
== core::mem::align_of::<usize>()
|
||||
);
|
||||
|
||||
/// `SemLock.handle` is one pointer-sized word (`sem_t *` or `HANDLE`).
|
||||
#[cfg(any(unix, windows))]
|
||||
#[repr(transparent)]
|
||||
struct SemHandleWord(rustpython_host_env::multiprocessing::SemHandle);
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
impl core::ops::Deref for SemHandleWord {
|
||||
type Target = rustpython_host_env::multiprocessing::SemHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
impl core::fmt::Debug for SemHandleWord {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
impl crate::vm::builtins::descriptor::MemberLayout for SemHandleWord {
|
||||
const KIND: crate::vm::builtins::descriptor::MemberKind = {
|
||||
if cfg!(all(windows, target_pointer_width = "64")) {
|
||||
crate::vm::builtins::descriptor::MemberKind::ULongLong
|
||||
} else {
|
||||
crate::vm::builtins::descriptor::MemberKind::ULong
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
fn semlock_name(
|
||||
name: Option<String>,
|
||||
) -> (
|
||||
Option<alloc::ffi::CString>,
|
||||
crate::vm::builtins::descriptor::CStrMember,
|
||||
) {
|
||||
let Some(name) = name else {
|
||||
return (
|
||||
None,
|
||||
crate::vm::builtins::descriptor::CStrMember::new(core::ptr::null()),
|
||||
);
|
||||
};
|
||||
let owner = match alloc::ffi::CString::new(name) {
|
||||
Ok(owner) => owner,
|
||||
Err(err) => {
|
||||
let bytes: Vec<u8> = err
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
.filter(|byte| *byte != 0)
|
||||
.collect();
|
||||
alloc::ffi::CString::new(bytes).unwrap_or_default()
|
||||
}
|
||||
};
|
||||
let member = crate::vm::builtins::descriptor::CStrMember::new(owner.as_ptr());
|
||||
(Some(owner), member)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[pymodule]
|
||||
mod _multiprocessing {
|
||||
@@ -63,10 +131,17 @@ mod _multiprocessing {
|
||||
#[pyclass(name = "SemLock", module = "_multiprocessing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct SemLock {
|
||||
handle: SemHandle,
|
||||
#[pymember]
|
||||
handle: super::SemHandleWord,
|
||||
#[pymember]
|
||||
kind: i32,
|
||||
#[pymember]
|
||||
maxvalue: i32,
|
||||
name: Option<String>,
|
||||
/// Owns the bytes `name` points at.
|
||||
#[expect(dead_code, reason = "keeps the semaphore name allocation alive")]
|
||||
name_owner: Option<alloc::ffi::CString>,
|
||||
#[pymember]
|
||||
name: crate::vm::builtins::descriptor::CStrMember,
|
||||
last_tid: AtomicU32,
|
||||
count: AtomicI32,
|
||||
/// Serializes owner bookkeeping. Dropped around blocking waits.
|
||||
@@ -77,26 +152,6 @@ mod _multiprocessing {
|
||||
|
||||
#[pyclass(with(Constructor), flags(BASETYPE))]
|
||||
impl SemLock {
|
||||
#[pygetset]
|
||||
fn handle(&self) -> isize {
|
||||
self.handle.as_handle_int()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn kind(&self) -> i32 {
|
||||
self.kind
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn maxvalue(&self) -> i32 {
|
||||
self.maxvalue
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn name(&self) -> Option<String> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
let blocking = args.block;
|
||||
@@ -237,11 +292,15 @@ mod _multiprocessing {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
// On Windows, _rebuild receives the handle directly (no sem_open)
|
||||
let (name_owner, name_member) = super::semlock_name(name);
|
||||
let zelf = Self {
|
||||
handle: SemHandle::from_raw(handle as host_multiprocessing::RawHandle),
|
||||
handle: super::SemHandleWord(SemHandle::from_raw(
|
||||
handle as host_multiprocessing::RawHandle,
|
||||
)),
|
||||
kind,
|
||||
maxvalue,
|
||||
name,
|
||||
name_owner,
|
||||
name: name_member,
|
||||
last_tid: AtomicU32::new(0),
|
||||
count: AtomicI32::new(0),
|
||||
crit: PyMutex::new(()),
|
||||
@@ -311,14 +370,17 @@ mod _multiprocessing {
|
||||
return Err(vm.new_value_error("invalid value"));
|
||||
}
|
||||
|
||||
let handle =
|
||||
SemHandle::create(args.value, args.maxvalue).map_err(|e| e.to_pyexception(vm))?;
|
||||
let name = if args.unlink { None } else { Some(args.name) };
|
||||
let handle = super::SemHandleWord(
|
||||
SemHandle::create(args.value, args.maxvalue).map_err(|e| e.to_pyexception(vm))?,
|
||||
);
|
||||
let (name_owner, name) =
|
||||
super::semlock_name(if args.unlink { None } else { Some(args.name) });
|
||||
|
||||
Ok(Self {
|
||||
handle,
|
||||
kind: args.kind,
|
||||
maxvalue: args.maxvalue,
|
||||
name_owner,
|
||||
name,
|
||||
last_tid: AtomicU32::new(0),
|
||||
count: AtomicI32::new(0),
|
||||
@@ -462,10 +524,17 @@ mod _multiprocessing {
|
||||
#[pyclass(name = "SemLock", module = "_multiprocessing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct SemLock {
|
||||
handle: SemHandle,
|
||||
#[pymember]
|
||||
handle: super::SemHandleWord,
|
||||
#[pymember]
|
||||
kind: i32,
|
||||
#[pymember]
|
||||
maxvalue: i32,
|
||||
name: Option<String>,
|
||||
/// Owns the bytes `name` points at.
|
||||
#[expect(dead_code, reason = "keeps the semaphore name allocation alive")]
|
||||
name_owner: Option<alloc::ffi::CString>,
|
||||
#[pymember]
|
||||
name: crate::vm::builtins::descriptor::CStrMember,
|
||||
last_tid: AtomicU64, // unsigned long
|
||||
count: AtomicI32, // int
|
||||
/// Serializes owner bookkeeping. Dropped around blocking waits.
|
||||
@@ -476,26 +545,6 @@ mod _multiprocessing {
|
||||
|
||||
#[pyclass(with(Constructor), flags(BASETYPE))]
|
||||
impl SemLock {
|
||||
#[pygetset]
|
||||
fn handle(&self) -> isize {
|
||||
self.handle.as_handle_int()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn kind(&self) -> i32 {
|
||||
self.kind
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn maxvalue(&self) -> i32 {
|
||||
self.maxvalue
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn name(&self) -> Option<String> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
// _multiprocessing_SemLock_acquire_impl
|
||||
#[pymethod]
|
||||
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
@@ -701,12 +750,16 @@ mod _multiprocessing {
|
||||
let Some(ref name_str) = name else {
|
||||
return Err(vm.new_value_error("cannot rebuild SemLock without name"));
|
||||
};
|
||||
let handle = SemHandle::open_existing(name_str).map_err(|err| os_error(vm, err))?;
|
||||
let handle = super::SemHandleWord(
|
||||
SemHandle::open_existing(name_str).map_err(|err| os_error(vm, err))?,
|
||||
);
|
||||
// return newsemlockobject(type, handle, kind, maxvalue, name_copy);
|
||||
let (name_owner, name) = super::semlock_name(name);
|
||||
let zelf = Self {
|
||||
handle,
|
||||
kind,
|
||||
maxvalue,
|
||||
name_owner,
|
||||
name,
|
||||
last_tid: AtomicU64::new(0),
|
||||
count: AtomicI32::new(0),
|
||||
@@ -828,10 +881,12 @@ mod _multiprocessing {
|
||||
})?;
|
||||
|
||||
// return newsemlockobject(type, handle, kind, maxvalue, name_copy);
|
||||
let (name_owner, name) = super::semlock_name(name);
|
||||
Ok(Self {
|
||||
handle,
|
||||
handle: super::SemHandleWord(handle),
|
||||
kind: args.kind,
|
||||
maxvalue: args.maxvalue,
|
||||
name_owner,
|
||||
name,
|
||||
last_tid: AtomicU64::new(0),
|
||||
count: AtomicI32::new(0),
|
||||
|
||||
+17
-45
@@ -18,6 +18,7 @@ mod _pickle {
|
||||
protocol::{PyBuffer, PyIter, PyIterReturn},
|
||||
types::{AsBuffer, Constructor, Initializer, Representable},
|
||||
};
|
||||
use core::sync::atomic::{AtomicI32, Ordering};
|
||||
use malachite_bigint::BigInt;
|
||||
use num_traits::{ToPrimitive, Zero};
|
||||
use std::collections::HashMap;
|
||||
@@ -1872,8 +1873,6 @@ mod _pickle {
|
||||
pub(super) struct PicklerConfig {
|
||||
initialized: bool,
|
||||
proto: u8,
|
||||
bin: bool,
|
||||
fast: bool,
|
||||
fix_imports: bool,
|
||||
buffer_callback: Option<PyObjectRef>,
|
||||
}
|
||||
@@ -1883,8 +1882,6 @@ mod _pickle {
|
||||
Self {
|
||||
initialized: false,
|
||||
proto: DEFAULT_PROTOCOL,
|
||||
bin: true,
|
||||
fast: false,
|
||||
fix_imports: false,
|
||||
buffer_callback: None,
|
||||
}
|
||||
@@ -1943,6 +1940,10 @@ mod _pickle {
|
||||
out: PyMutex<Output>,
|
||||
memo: PyRwLock<MemoTable>,
|
||||
config: PyRwLock<PicklerConfig>,
|
||||
#[pymember(writable)]
|
||||
bin: AtomicI32,
|
||||
#[pymember(writable)]
|
||||
fast: AtomicI32,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
@@ -1989,8 +1990,6 @@ mod _pickle {
|
||||
Ok(PicklerConfig {
|
||||
initialized: true,
|
||||
proto,
|
||||
bin: proto >= 1,
|
||||
fast: false,
|
||||
fix_imports: args.fix_imports && proto < 3,
|
||||
buffer_callback,
|
||||
})
|
||||
@@ -2004,6 +2003,8 @@ mod _pickle {
|
||||
out: PyMutex::new(Output::default()),
|
||||
memo: PyRwLock::new(MemoTable::default()),
|
||||
config: PyRwLock::new(PicklerConfig::default()),
|
||||
bin: AtomicI32::new(1),
|
||||
fast: AtomicI32::new(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2028,6 +2029,9 @@ mod _pickle {
|
||||
..Output::default()
|
||||
};
|
||||
zelf.memo.write().clear();
|
||||
zelf.bin
|
||||
.store(i32::from(config.proto >= 1), Ordering::Relaxed);
|
||||
zelf.fast.store(0, Ordering::Relaxed);
|
||||
*zelf.config.write() = config;
|
||||
Ok(())
|
||||
}
|
||||
@@ -2095,42 +2099,6 @@ mod _pickle {
|
||||
*zelf.memo.write() = new_memo;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn fast(&self) -> bool {
|
||||
self.config.read().fast
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_fast(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
|
||||
match value {
|
||||
PySetterValue::Assign(v) => {
|
||||
self.config.write().fast = v.try_to_bool(vm)?;
|
||||
Ok(())
|
||||
}
|
||||
PySetterValue::Delete => {
|
||||
Err(vm.new_type_error("attribute deletion is not supported"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn bin(&self) -> bool {
|
||||
self.config.read().bin
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_bin(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
|
||||
match value {
|
||||
PySetterValue::Assign(v) => {
|
||||
self.config.write().bin = v.try_to_bool(vm)?;
|
||||
Ok(())
|
||||
}
|
||||
PySetterValue::Delete => {
|
||||
Err(vm.new_type_error("attribute deletion is not supported"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// saving
|
||||
@@ -3709,17 +3677,17 @@ mod _pickle {
|
||||
let Some(mut out) = zelf.out.try_lock() else {
|
||||
return Err(vm.new_runtime_error("Pickler.dump() called recursively"));
|
||||
};
|
||||
let (initialized, proto, bin, fast, fix_imports, buffer_callback) = {
|
||||
let (initialized, proto, fix_imports, buffer_callback) = {
|
||||
let cfg = zelf.config.read();
|
||||
(
|
||||
cfg.initialized,
|
||||
cfg.proto,
|
||||
cfg.bin,
|
||||
cfg.fast,
|
||||
cfg.fix_imports,
|
||||
cfg.buffer_callback.clone(),
|
||||
)
|
||||
};
|
||||
let bin = zelf.bin.load(Ordering::Relaxed) != 0;
|
||||
let fast = zelf.fast.load(Ordering::Relaxed) != 0;
|
||||
if !initialized {
|
||||
return Err(new_pickling_error(
|
||||
vm,
|
||||
@@ -3803,6 +3771,8 @@ mod _pickle {
|
||||
..Output::default()
|
||||
}),
|
||||
memo: PyRwLock::new(MemoTable::default()),
|
||||
bin: AtomicI32::new(i32::from(config.proto >= 1)),
|
||||
fast: AtomicI32::new(0),
|
||||
config: PyRwLock::new(config),
|
||||
}
|
||||
.into_ref(&vm.ctx);
|
||||
@@ -3833,6 +3803,8 @@ mod _pickle {
|
||||
let pickler = PyPickler {
|
||||
out: PyMutex::new(Output::default()),
|
||||
memo: PyRwLock::new(MemoTable::default()),
|
||||
bin: AtomicI32::new(i32::from(config.proto >= 1)),
|
||||
fast: AtomicI32::new(0),
|
||||
config: PyRwLock::new(config),
|
||||
}
|
||||
.into_ref(&vm.ctx);
|
||||
|
||||
@@ -211,7 +211,7 @@ mod _pyexpat {
|
||||
|
||||
#[pyattr]
|
||||
#[pyattr(name = "XMLParserType")]
|
||||
#[pyclass(name = "xmlparser", module = false, traverse)]
|
||||
#[pyclass(name = "xmlparser", traverse)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(super) struct PyExpatLikeXmlParser {
|
||||
#[pytraverse(skip)]
|
||||
@@ -255,7 +255,8 @@ mod _pyexpat {
|
||||
namespace_prefixes: MutableObject,
|
||||
ordered_attributes: MutableObject,
|
||||
specified_attributes: MutableObject,
|
||||
intern: MutableObject,
|
||||
#[pymember]
|
||||
intern: PyObjectRef,
|
||||
// Additional handlers (stubs for compatibility)
|
||||
processing_instruction: MutableObject,
|
||||
unparsed_entity_decl: MutableObject,
|
||||
@@ -830,7 +831,7 @@ mod _pyexpat {
|
||||
namespace_prefixes: MutableObject::new(vm.ctx.new_bool(false).into()),
|
||||
ordered_attributes: MutableObject::new(vm.ctx.new_bool(false).into()),
|
||||
specified_attributes: MutableObject::new(vm.ctx.new_bool(false).into()),
|
||||
intern: MutableObject::new(intern_dict),
|
||||
intern: intern_dict,
|
||||
// Additional handlers (stubs for compatibility)
|
||||
processing_instruction: MutableObject::new(vm.ctx.none()),
|
||||
unparsed_entity_decl: MutableObject::new(vm.ctx.none()),
|
||||
@@ -890,7 +891,6 @@ mod _pyexpat {
|
||||
class,
|
||||
specified_attributes
|
||||
);
|
||||
create_property!(ctx, attributes, "intern", class, intern);
|
||||
create_readonly_int_property!(
|
||||
ctx,
|
||||
attributes,
|
||||
@@ -1066,7 +1066,7 @@ mod _pyexpat {
|
||||
/// Attribute *values* are intentionally left out of this cache, same
|
||||
/// as libexpat: they vary far more than names and rarely repeat.
|
||||
fn intern_name(&self, vm: &VirtualMachine, name: String) -> PyStrRef {
|
||||
let intern_obj = self.intern.read().clone();
|
||||
let intern_obj = self.intern.clone();
|
||||
if let Ok(dict) = intern_obj.downcast::<crate::vm::builtins::PyDict>() {
|
||||
if let Ok(Some(existing)) = dict.get_item_opt(name.as_str(), vm)
|
||||
&& let Ok(existing) = existing.downcast::<PyStr>()
|
||||
@@ -1460,7 +1460,7 @@ mod _pyexpat {
|
||||
// TODO: Tie this exception to the module's state.
|
||||
#[pyattr]
|
||||
#[pyattr(name = "error")]
|
||||
#[pyexception(name = "ExpatError", base = PyException)]
|
||||
#[pyexception(name = "ExpatError", module = "xml.parsers.expat", base = PyException)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(super) struct PyExpatError(PyException);
|
||||
|
||||
+43
-74
@@ -692,22 +692,42 @@ mod decl {
|
||||
Py, PyObject, PyPayload, PyRef,
|
||||
builtins::{PyFloat, PyType},
|
||||
class_or_notimplemented,
|
||||
common::lock::{PyMutex, PyRwLock},
|
||||
common::lock::PyRwLock,
|
||||
convert::{IntoPyException, ToPyObject},
|
||||
function::PyComparisonValue,
|
||||
types::{Comparable, Constructor, Destructor, PyComparisonOp, Representable},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use core::sync::atomic::AtomicI32;
|
||||
use core::sync::atomic::{
|
||||
AtomicI16, AtomicI32, AtomicI64, AtomicU16, AtomicU32, AtomicU64, Ordering,
|
||||
};
|
||||
use num_traits::ToPrimitive;
|
||||
use std::time::Instant;
|
||||
|
||||
#[pyclass(module = "select", name = "kevent")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct PyKevent {
|
||||
ev: PyMutex<host_select::kqueue::Event>,
|
||||
#[pymember(writable)]
|
||||
ident: AtomicU64,
|
||||
#[pymember(writable)]
|
||||
filter: AtomicI16,
|
||||
#[pymember(writable)]
|
||||
flags: AtomicU16,
|
||||
#[pymember(writable)]
|
||||
fflags: AtomicU32,
|
||||
#[pymember(writable)]
|
||||
data: AtomicI64,
|
||||
#[pymember(writable)]
|
||||
udata: AtomicU64,
|
||||
}
|
||||
|
||||
const _: () = assert!(
|
||||
core::mem::size_of::<usize>() == core::mem::size_of::<u64>()
|
||||
&& core::mem::size_of::<isize>() == core::mem::size_of::<i64>()
|
||||
&& core::mem::size_of::<std::os::raw::c_ulong>() == core::mem::size_of::<u64>()
|
||||
&& core::mem::size_of::<std::os::raw::c_long>() == core::mem::size_of::<i64>()
|
||||
);
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub(crate) struct KeventNewArgs {
|
||||
#[pyarg(any)]
|
||||
@@ -741,14 +761,12 @@ mod decl {
|
||||
fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let ident = ident_from_object(&args.ident, vm)?;
|
||||
Ok(Self {
|
||||
ev: PyMutex::new(host_select::kqueue::Event {
|
||||
ident,
|
||||
filter: args.filter,
|
||||
flags: args.flags,
|
||||
fflags: args.fflags,
|
||||
data: args.data,
|
||||
udata: args.udata,
|
||||
}),
|
||||
ident: AtomicU64::new(ident as u64),
|
||||
filter: AtomicI16::new(args.filter),
|
||||
flags: AtomicU16::new(args.flags),
|
||||
fflags: AtomicU32::new(args.fflags),
|
||||
data: AtomicI64::new(args.data as i64),
|
||||
udata: AtomicU64::new(args.udata as u64),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -756,75 +774,26 @@ mod decl {
|
||||
#[pyclass(with(Constructor, Comparable, Representable))]
|
||||
impl PyKevent {
|
||||
pub(super) fn event(&self) -> host_select::kqueue::Event {
|
||||
*self.ev.lock()
|
||||
host_select::kqueue::Event {
|
||||
ident: self.ident.load(Ordering::Relaxed) as usize,
|
||||
filter: self.filter.load(Ordering::Relaxed),
|
||||
flags: self.flags.load(Ordering::Relaxed),
|
||||
fflags: self.fflags.load(Ordering::Relaxed),
|
||||
data: self.data.load(Ordering::Relaxed) as isize,
|
||||
udata: self.udata.load(Ordering::Relaxed) as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn from_event(ev: host_select::kqueue::Event) -> Self {
|
||||
Self {
|
||||
ev: PyMutex::new(ev),
|
||||
ident: AtomicU64::new(ev.ident as u64),
|
||||
filter: AtomicI16::new(ev.filter),
|
||||
flags: AtomicU16::new(ev.flags),
|
||||
fflags: AtomicU32::new(ev.fflags),
|
||||
data: AtomicI64::new(ev.data as i64),
|
||||
udata: AtomicU64::new(ev.udata as u64),
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn ident(&self) -> usize {
|
||||
self.ev.lock().ident
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_ident(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
self.ev.lock().ident = ident_from_object(&value, vm)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn filter(&self) -> i16 {
|
||||
self.ev.lock().filter
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_filter(&self, value: i16) {
|
||||
self.ev.lock().filter = value;
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn flags(&self) -> u16 {
|
||||
self.ev.lock().flags
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_flags(&self, value: u16) {
|
||||
self.ev.lock().flags = value;
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn fflags(&self) -> u32 {
|
||||
self.ev.lock().fflags
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_fflags(&self, value: u32) {
|
||||
self.ev.lock().fflags = value;
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn data(&self) -> isize {
|
||||
self.ev.lock().data
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_data(&self, value: isize) {
|
||||
self.ev.lock().data = value;
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn udata(&self) -> usize {
|
||||
self.ev.lock().udata
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set_udata(&self, value: usize) {
|
||||
self.ev.lock().udata = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl Comparable for PyKevent {
|
||||
|
||||
+44
-38
@@ -44,6 +44,7 @@ mod _socket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use core::sync::atomic::{AtomicI32, Ordering};
|
||||
use core::{
|
||||
mem::ManuallyDrop,
|
||||
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
@@ -1062,9 +1063,12 @@ mod _socket {
|
||||
#[pyclass(name = "socket")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub struct PySocket {
|
||||
kind: AtomicCell<i32>,
|
||||
family: AtomicCell<i32>,
|
||||
proto: AtomicCell<i32>,
|
||||
#[pymember(name = "type")]
|
||||
kind: AtomicI32,
|
||||
#[pymember]
|
||||
family: AtomicI32,
|
||||
#[pymember]
|
||||
proto: AtomicI32,
|
||||
pub(crate) timeout: AtomicCell<f64>,
|
||||
sock: PyRwLock<Option<Socket>>,
|
||||
}
|
||||
@@ -1074,9 +1078,9 @@ mod _socket {
|
||||
impl Default for PySocket {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
kind: AtomicCell::default(),
|
||||
family: AtomicCell::default(),
|
||||
proto: AtomicCell::default(),
|
||||
kind: AtomicI32::new(0),
|
||||
family: AtomicI32::new(0),
|
||||
proto: AtomicI32::new(0),
|
||||
timeout: AtomicCell::new(-1.0),
|
||||
sock: PyRwLock::new(None),
|
||||
}
|
||||
@@ -1137,7 +1141,7 @@ mod _socket {
|
||||
proto: i32,
|
||||
sock: Socket,
|
||||
) -> io::Result<()> {
|
||||
self.family.store(family);
|
||||
self.family.store(family, Ordering::Relaxed);
|
||||
// Mask out SOCK_NONBLOCK and SOCK_CLOEXEC flags from stored type
|
||||
// to ensure consistent cross-platform behavior
|
||||
#[cfg(any(
|
||||
@@ -1164,8 +1168,8 @@ mod _socket {
|
||||
target_os = "redox"
|
||||
)))]
|
||||
let masked_kind = socket_kind;
|
||||
self.kind.store(masked_kind);
|
||||
self.proto.store(proto);
|
||||
self.kind.store(masked_kind, Ordering::Relaxed);
|
||||
self.proto.store(proto, Ordering::Relaxed);
|
||||
let mut s = self.sock.write();
|
||||
let sock = s.insert(sock);
|
||||
// If SOCK_NONBLOCK is set, use timeout 0 (non-blocking)
|
||||
@@ -1272,7 +1276,7 @@ mod _socket {
|
||||
caller: &str,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<host_socket::raw::SockAddr, IoOrPyException> {
|
||||
let family = self.family.load();
|
||||
let family = self.family.load(Ordering::Relaxed);
|
||||
match family {
|
||||
#[cfg(unix)]
|
||||
c::AF_UNIX => {
|
||||
@@ -1338,11 +1342,11 @@ mod _socket {
|
||||
}
|
||||
#[cfg(windows)]
|
||||
family if family == c::AF_HYPERV => {
|
||||
if self.proto.load() != host_socket::HV_PROTOCOL_RAW {
|
||||
if self.proto.load(Ordering::Relaxed) != host_socket::HV_PROTOCOL_RAW {
|
||||
return Err(vm
|
||||
.new_os_error(format!(
|
||||
"{caller}(): unsupported AF_HYPERV protocol: {}",
|
||||
self.proto.load()
|
||||
self.proto.load(Ordering::Relaxed)
|
||||
))
|
||||
.into());
|
||||
}
|
||||
@@ -1403,7 +1407,7 @@ mod _socket {
|
||||
}
|
||||
#[cfg(windows)]
|
||||
family if family == c::AF_BLUETOOTH => {
|
||||
if self.proto.load() != host_socket::BTHPROTO_RFCOMM {
|
||||
if self.proto.load(Ordering::Relaxed) != host_socket::BTHPROTO_RFCOMM {
|
||||
return Err(vm
|
||||
.new_os_error(format!("{caller}(): unknown Bluetooth protocol"))
|
||||
.into());
|
||||
@@ -1448,7 +1452,7 @@ mod _socket {
|
||||
obj.class().name()
|
||||
))
|
||||
})?;
|
||||
let proto = self.proto.load();
|
||||
let proto = self.proto.load(Ordering::Relaxed);
|
||||
let interface: PyStrRef = tuple
|
||||
.as_slice()
|
||||
.first()
|
||||
@@ -1697,9 +1701,9 @@ mod _socket {
|
||||
Ok(format!(
|
||||
"<socket object, fd={}, family={}, type={}, proto={}>",
|
||||
zelf.fileno(),
|
||||
zelf.family.load(),
|
||||
zelf.kind.load(),
|
||||
zelf.proto.load(),
|
||||
zelf.family.load(Ordering::Relaxed),
|
||||
zelf.kind.load(Ordering::Relaxed),
|
||||
zelf.proto.load(Ordering::Relaxed),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1896,7 +1900,10 @@ mod _socket {
|
||||
self.sock_snapshot()?.accept_raw()
|
||||
})?;
|
||||
let fd = into_sock_fileno(sock);
|
||||
Ok((fd, get_addr_tuple(&addr, self.proto.load(), vm)))
|
||||
Ok((
|
||||
fd,
|
||||
get_addr_tuple(&addr, self.proto.load(Ordering::Relaxed), vm),
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -1969,7 +1976,10 @@ mod _socket {
|
||||
.recv_from_with_flags(buffer.spare_capacity_mut(), flags)
|
||||
})?;
|
||||
unsafe { buffer.set_len(n) };
|
||||
Ok((buffer, get_addr_tuple(&addr, self.proto.load(), vm)))
|
||||
Ok((
|
||||
buffer,
|
||||
get_addr_tuple(&addr, self.proto.load(Ordering::Relaxed), vm),
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -2002,7 +2012,10 @@ mod _socket {
|
||||
})?;
|
||||
unsafe { scratch.set_len(n) };
|
||||
buf.borrow_buf_mut()[..n].copy_from_slice(&scratch);
|
||||
Ok((n, get_addr_tuple(&addr, self.proto.load(), vm)))
|
||||
Ok((
|
||||
n,
|
||||
get_addr_tuple(&addr, self.proto.load(Ordering::Relaxed), vm),
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -2226,7 +2239,7 @@ mod _socket {
|
||||
let storage: host_socket::raw::SockAddrStorage =
|
||||
unsafe { core::mem::transmute(address.storage) };
|
||||
let addr = unsafe { host_socket::raw::SockAddr::new(storage, address.len as _) };
|
||||
get_addr_tuple(&addr, self.proto.load(), vm)
|
||||
get_addr_tuple(&addr, self.proto.load(Ordering::Relaxed), vm)
|
||||
} else {
|
||||
vm.ctx.none()
|
||||
};
|
||||
@@ -2310,14 +2323,22 @@ mod _socket {
|
||||
fn getsockname(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
|
||||
let addr = self.sock()?.local_addr()?;
|
||||
|
||||
Ok(get_addr_tuple(&addr, self.proto.load(), vm))
|
||||
Ok(get_addr_tuple(
|
||||
&addr,
|
||||
self.proto.load(Ordering::Relaxed),
|
||||
vm,
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getpeername(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
|
||||
let addr = self.sock()?.peer_addr()?;
|
||||
|
||||
Ok(get_addr_tuple(&addr, self.proto.load(), vm))
|
||||
Ok(get_addr_tuple(
|
||||
&addr,
|
||||
self.proto.load(Ordering::Relaxed),
|
||||
vm,
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -2507,21 +2528,6 @@ mod _socket {
|
||||
let fd = sock_fileno(&sock);
|
||||
host_socket::share_socket(fd as _, process_id).map_err(Into::into)
|
||||
}
|
||||
|
||||
#[pygetset(name = "type")]
|
||||
fn kind(&self) -> i32 {
|
||||
self.kind.load()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn family(&self) -> i32 {
|
||||
self.family.load()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn proto(&self) -> i32 {
|
||||
self.proto.load()
|
||||
}
|
||||
}
|
||||
|
||||
struct Address {
|
||||
|
||||
@@ -207,8 +207,11 @@ mod _socket {
|
||||
#[pyclass(name = "socket")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PySocket {
|
||||
#[pymember]
|
||||
family: AtomicI32,
|
||||
#[pymember(name = "type")]
|
||||
kind: AtomicI32,
|
||||
#[pymember]
|
||||
proto: AtomicI32,
|
||||
timeout: PyMutex<Option<f64>>,
|
||||
closed: PyMutex<bool>,
|
||||
@@ -235,21 +238,6 @@ mod _socket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn family(&self) -> i32 {
|
||||
self.family.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn r#type(&self) -> i32 {
|
||||
self.kind.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn proto(&self) -> i32 {
|
||||
self.proto.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn fileno(&self) -> i32 {
|
||||
if *self.closed.lock() { -1 } else { 0 }
|
||||
|
||||
@@ -18,7 +18,7 @@ pub(crate) mod ssl_error {
|
||||
};
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLError", base = PyOSError)]
|
||||
#[pyexception(name = "SSLError", module = "ssl", base = PyOSError)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLError(PyOSError);
|
||||
@@ -47,7 +47,7 @@ pub(crate) mod ssl_error {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLZeroReturnError", base = PySSLError)]
|
||||
#[pyexception(name = "SSLZeroReturnError", module = "ssl", base = PySSLError)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLZeroReturnError(PySSLError);
|
||||
@@ -56,31 +56,31 @@ pub(crate) mod ssl_error {
|
||||
impl PySSLZeroReturnError {}
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLWantReadError", base = PySSLError, impl)]
|
||||
#[pyexception(name = "SSLWantReadError", module = "ssl", base = PySSLError, impl)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLWantReadError(PySSLError);
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLWantWriteError", base = PySSLError, impl)]
|
||||
#[pyexception(name = "SSLWantWriteError", module = "ssl", base = PySSLError, impl)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLWantWriteError(PySSLError);
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLSyscallError", base = PySSLError, impl)]
|
||||
#[pyexception(name = "SSLSyscallError", module = "ssl", base = PySSLError, impl)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLSyscallError(PySSLError);
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLEOFError", base = PySSLError, impl)]
|
||||
#[pyexception(name = "SSLEOFError", module = "ssl", base = PySSLError, impl)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLEOFError(PySSLError);
|
||||
|
||||
#[pyattr]
|
||||
#[pyexception(name = "SSLCertVerificationError", base = PySSLError, impl)]
|
||||
#[pyexception(name = "SSLCertVerificationError", module = "ssl", base = PySSLError, impl)]
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct PySSLCertVerificationError(PySSLError);
|
||||
|
||||
@@ -72,12 +72,27 @@ mod unicodedata {
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(super) struct Ucd {
|
||||
inner: unicode_core::Ucd,
|
||||
/// Owns the bytes `unidata_version` points at.
|
||||
#[expect(dead_code, reason = "keeps the version string alive")]
|
||||
version_owner: alloc::ffi::CString,
|
||||
#[pymember]
|
||||
unidata_version: crate::vm::builtins::descriptor::CStrMember,
|
||||
}
|
||||
|
||||
impl Ucd {
|
||||
pub(super) const fn new(modern: bool) -> Self {
|
||||
pub(super) fn new(modern: bool) -> Self {
|
||||
let text = if modern {
|
||||
unicode_core::unicode_version()
|
||||
} else {
|
||||
"3.2.0".to_owned()
|
||||
};
|
||||
let version_owner = alloc::ffi::CString::new(text).unwrap_or_default();
|
||||
let unidata_version =
|
||||
crate::vm::builtins::descriptor::CStrMember::new(version_owner.as_ptr());
|
||||
Self {
|
||||
inner: unicode_core::Ucd::new(modern),
|
||||
version_owner,
|
||||
unidata_version,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,11 +232,6 @@ mod unicodedata {
|
||||
.map(Option::Some)
|
||||
.ok_or_else(|| vm.new_value_error("not a numeric character"))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn unidata_version(&self) -> String {
|
||||
self.inner.unidata_version()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
|
||||
+25
-23
@@ -7,14 +7,16 @@ mod zlib {
|
||||
use crate::compression::{DecompressArgs, DecompressorArgs};
|
||||
use crate::vm::{
|
||||
Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytesRef, PyType, PyTypeRef},
|
||||
builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyType, PyTypeRef},
|
||||
common::lock::PyMutex,
|
||||
convert::TryFromBorrowedObject,
|
||||
function::{ArgBytesLike, ArgIndex, OptionalArg, PySize, PySsize},
|
||||
object::PyAtomicRef,
|
||||
types::Constructor,
|
||||
};
|
||||
use adler32::RollingAdler32 as Adler32;
|
||||
use alloc::fmt;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use rustpython_common::compression::zlib as backend;
|
||||
|
||||
#[pyattr]
|
||||
@@ -416,7 +418,6 @@ mod zlib {
|
||||
|
||||
struct PyZlibDecompressorInner {
|
||||
decompress: backend::ZlibDecompressor,
|
||||
unused_data: PyBytesRef,
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
@@ -425,6 +426,14 @@ mod zlib {
|
||||
struct ZlibDecompressor {
|
||||
#[pytraverse(skip)]
|
||||
inner: PyMutex<PyZlibDecompressorInner>,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
eof: AtomicBool,
|
||||
#[pymember]
|
||||
#[pytraverse(skip)]
|
||||
needs_input: AtomicBool,
|
||||
#[pymember(type = "object_ex")]
|
||||
unused_data: PyAtomicRef<Option<PyObject>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ZlibDecompressor {
|
||||
@@ -450,31 +459,16 @@ mod zlib {
|
||||
let decompress = backend::ZlibDecompressor::new(args.wbits, owned_dict(args.zdict))
|
||||
.map_err(|err| new_init_or_zlib_error(err, vm))?;
|
||||
Ok(Self {
|
||||
inner: PyMutex::new(PyZlibDecompressorInner {
|
||||
decompress,
|
||||
unused_data: vm.ctx.empty_bytes.clone(),
|
||||
}),
|
||||
inner: PyMutex::new(PyZlibDecompressorInner { decompress }),
|
||||
eof: AtomicBool::new(false),
|
||||
needs_input: AtomicBool::new(true),
|
||||
unused_data: PyAtomicRef::from(Some(vm.ctx.empty_bytes.clone().into())),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(Constructor))]
|
||||
impl ZlibDecompressor {
|
||||
#[pygetset]
|
||||
fn eof(&self) -> bool {
|
||||
self.inner.lock().decompress.eof()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn unused_data(&self) -> PyBytesRef {
|
||||
self.inner.lock().unused_data.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn needs_input(&self) -> bool {
|
||||
self.inner.lock().decompress.needs_input()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn decompress(&self, args: DecompressorArgs, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
let max_length = args.max_length();
|
||||
@@ -482,8 +476,16 @@ mod zlib {
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
let result = inner.decompress.decompress(data, max_length);
|
||||
if inner.unused_data.as_bytes() != inner.decompress.unused_data() {
|
||||
inner.unused_data = vm.ctx.new_bytes(inner.decompress.unused_data().to_vec());
|
||||
self.eof.store(inner.decompress.eof(), Ordering::Relaxed);
|
||||
self.needs_input
|
||||
.store(inner.decompress.needs_input(), Ordering::Relaxed);
|
||||
let stale = self.unused_data.deref().is_none_or(|obj| {
|
||||
obj.downcast_ref::<PyBytes>()
|
||||
.is_none_or(|bytes| bytes.as_bytes() != inner.decompress.unused_data())
|
||||
});
|
||||
if stale {
|
||||
let bytes = vm.ctx.new_bytes(inner.decompress.unused_data().to_vec());
|
||||
let _previous = unsafe { self.unused_data.swap(Some(bytes.into())) };
|
||||
}
|
||||
result.map_err(|err| match err {
|
||||
backend::DecompressError::Zlib(err) => new_zlib_error(err, vm),
|
||||
|
||||
@@ -60,6 +60,7 @@ impl PyPayload for PyAsyncGen {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<crate::PyObjectRef>(),
|
||||
flags(DISALLOW_INSTANTIATION, HAS_WEAKREF),
|
||||
with(PyRef, Representable, Destructor)
|
||||
)]
|
||||
|
||||
@@ -18,8 +18,8 @@ pub struct PyNativeFunction {
|
||||
pub(crate) zelf: Option<PyObjectRef>,
|
||||
// Module that owns this function. Not passed as a call argument.
|
||||
pub(crate) module_object: Option<PyObjectRef>,
|
||||
#[pytraverse(skip)]
|
||||
pub(crate) module: Option<&'static PyStrInterned>, // None for bound method
|
||||
#[pymember(name = "__module__", writable)]
|
||||
pub(crate) module: crate::object::PyAtomicRef<Option<PyObject>>,
|
||||
/// Prevent HeapMethodDef from being freed while this function references it
|
||||
pub(crate) _method_def_owner: Option<PyObjectRef>,
|
||||
}
|
||||
@@ -32,11 +32,18 @@ impl PyPayload for PyNativeFunction {
|
||||
|
||||
impl fmt::Debug for PyNativeFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let module = match self
|
||||
.module
|
||||
.deref()
|
||||
.and_then(|m| m.downcast_ref::<crate::builtins::PyStr>())
|
||||
{
|
||||
Some(module) => module.as_wtf8().to_owned(),
|
||||
None => Wtf8::new("<unknown>").to_owned(),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"builtin function {}.{} ({:?}) self as instance of {:?}",
|
||||
self.module
|
||||
.map_or_else(|| Wtf8::new("<unknown>"), |m| m.as_wtf8()),
|
||||
module,
|
||||
self.value.name,
|
||||
self.value.flags,
|
||||
self.zelf.as_ref().map(|z| z.class().name().to_owned())
|
||||
@@ -45,8 +52,8 @@ impl fmt::Debug for PyNativeFunction {
|
||||
}
|
||||
|
||||
impl PyNativeFunction {
|
||||
pub const fn with_module(mut self, module: &'static PyStrInterned) -> Self {
|
||||
self.module = Some(module);
|
||||
pub fn with_module(self, module: &'static PyStrInterned) -> Self {
|
||||
drop(self.module.store(Some(module.to_owned().into())));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -149,11 +156,6 @@ impl Representable for PyNativeFunction {
|
||||
flags(HAS_WEAKREF, DISALLOW_INSTANTIATION)
|
||||
)]
|
||||
impl PyNativeFunction {
|
||||
#[pygetset]
|
||||
fn __module__(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> {
|
||||
zelf.0.module
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __name__(zelf: NativeFunctionOrMethod) -> &'static str {
|
||||
zelf.0.value.name
|
||||
@@ -203,7 +205,7 @@ impl PyNativeFunction {
|
||||
#[pymethod]
|
||||
fn __reduce__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult {
|
||||
let zelf = zelf.0;
|
||||
if zelf.zelf.is_none() || zelf.module.is_some() {
|
||||
if zelf.zelf.is_none() || zelf.module.deref().is_some() {
|
||||
Ok(vm.ctx.new_str(zelf.value.name).into())
|
||||
} else {
|
||||
let getattr = vm.builtins.get_attr("getattr", vm)?;
|
||||
|
||||
@@ -184,15 +184,13 @@ impl Representable for PyClassMethod {
|
||||
let callable = zelf.callable.load_owned().repr(vm)?;
|
||||
let class = Self::class(&vm.ctx);
|
||||
|
||||
let module = class.__module__(vm)?;
|
||||
let repr = match (
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|m| m.as_wtf8()),
|
||||
module.downcast_ref::<PyStr>().map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => return Err(vm.new_type_error("Unknown qualified name")),
|
||||
(Some(qualname), Some(module)) if module != "builtins" => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Infamous code object. The python class `code`
|
||||
|
||||
use super::descriptor::{MemberKind, MemberLayout};
|
||||
use super::{PyBytesRef, PyStrRef, PyTupleRef, PyType, set::PyFrozenSet};
|
||||
use crate::common::lock::PyMutex;
|
||||
#[cfg(feature = "host_env")]
|
||||
@@ -477,12 +478,16 @@ pub struct PyCode {
|
||||
#[pymember(name = "co_stacksize", path = "max_stackdepth")]
|
||||
#[pymember(name = "co_name", path = "obj_name")]
|
||||
#[pymember(name = "co_qualname", path = "qualname")]
|
||||
#[pymember(name = "co_flags", path = "flags")]
|
||||
pub code: CodeObject,
|
||||
/// Slot-indexed names, equivalent to CPython's `co_localsplusnames`.
|
||||
/// Derived once so frame-local proxy operations do not repeatedly scan
|
||||
/// merged cell variables.
|
||||
localsplus_names: Box<[&'static PyStrInterned]>,
|
||||
#[pymember(name = "co_filename")]
|
||||
source_path: AtomicPtr<PyStrInterned>,
|
||||
#[pymember(name = "co_nlocals")]
|
||||
nlocals: i32,
|
||||
/// Version counter for lazy re-instrumentation.
|
||||
/// Compared against `PyGlobalState::instrumentation_version` at RESUME.
|
||||
pub instrumentation_version: AtomicU64,
|
||||
@@ -582,9 +587,14 @@ fn build_localspluskinds(
|
||||
Ok(kinds.into_boxed_slice())
|
||||
}
|
||||
|
||||
impl MemberLayout for CodeFlags {
|
||||
const KIND: MemberKind = MemberKind::Int;
|
||||
}
|
||||
|
||||
impl PyCode {
|
||||
pub fn new(code: CodeObject) -> Self {
|
||||
let sp = code.source_path as *const PyStrInterned as *mut PyStrInterned;
|
||||
let nlocals = i32::try_from(code.varnames.len()).unwrap_or(i32::MAX);
|
||||
let localsplus_names = {
|
||||
let varname_ids = code
|
||||
.varnames
|
||||
@@ -622,6 +632,7 @@ impl PyCode {
|
||||
code,
|
||||
localsplus_names,
|
||||
source_path: AtomicPtr::new(sp),
|
||||
nlocals,
|
||||
instrumentation_version: AtomicU64::new(0),
|
||||
monitoring_data: PyMutex::new(None),
|
||||
quickened: core::sync::atomic::AtomicBool::new(false),
|
||||
@@ -1030,11 +1041,11 @@ impl Constructor for PyCode {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<u16>(),
|
||||
with(Representable, Constructor, Comparable, Hashable),
|
||||
flags(HAS_WEAKREF)
|
||||
)]
|
||||
impl PyCode {
|
||||
#[pygetset]
|
||||
pub fn co_filename(&self) -> PyStrRef {
|
||||
self.source_path().to_owned()
|
||||
}
|
||||
@@ -1049,11 +1060,6 @@ impl PyCode {
|
||||
vm.ctx.new_tuple(cellvars)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_nlocals(&self) -> usize {
|
||||
self.code.varnames.len()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_firstlineno(&self) -> u32 {
|
||||
self.code.first_line_number.map_or(0, |n| n.get() as _)
|
||||
@@ -1077,11 +1083,6 @@ impl PyCode {
|
||||
vm.ctx.new_tuple(names)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
const fn co_flags(&self) -> u32 {
|
||||
self.code.flags.bits()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_varnames(&self, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let varnames = self.code.varnames.iter().map(|s| s.to_object()).collect();
|
||||
|
||||
@@ -42,6 +42,7 @@ impl PyPayload for PyCoroutine {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<crate::PyObjectRef>(),
|
||||
flags(DISALLOW_INSTANTIATION, HAS_WEAKREF),
|
||||
with(Py, Representable, Destructor)
|
||||
)]
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::{
|
||||
SetattroFunc, StringifyFunc,
|
||||
},
|
||||
};
|
||||
use core::mem::{align_of, size_of};
|
||||
use rustpython_common::lock::PyRwLock;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -313,26 +314,64 @@ impl Representable for PyClassMethodDescriptor {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(i32)]
|
||||
pub enum MemberKind {
|
||||
/// `short`. Writable cells are `AtomicI16`.
|
||||
Short = 0,
|
||||
Int = 1,
|
||||
/// `long` (`c_long`). Writable cells are the atomic of that width.
|
||||
Long = 2,
|
||||
Double = 4,
|
||||
/// `char *`. The slot is a pointer to a NUL-terminated string. Null loads
|
||||
/// as `None`. Assignment is rejected.
|
||||
String = 5,
|
||||
Object = 6,
|
||||
/// `unsigned short`. Writable cells are `AtomicU16`.
|
||||
UShort = 10,
|
||||
Uint = 11,
|
||||
/// `unsigned long` (`c_ulong`). Writable cells are the atomic of that width.
|
||||
ULong = 12,
|
||||
Bool = 14,
|
||||
ObjectEx = 16,
|
||||
/// `long long`. Writable cells are `AtomicI64`.
|
||||
LongLong = 17,
|
||||
/// `unsigned long long`. Writable cells are `AtomicU64`.
|
||||
ULongLong = 18,
|
||||
/// `Py_ssize_t`. Writable cells are `AtomicIsize`.
|
||||
PySsizeT = 19,
|
||||
}
|
||||
|
||||
impl MemberKind {
|
||||
const fn from_i64() -> Self {
|
||||
if size_of::<core::ffi::c_long>() == 8 {
|
||||
Self::Long
|
||||
} else {
|
||||
Self::LongLong
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_u64() -> Self {
|
||||
if size_of::<core::ffi::c_ulong>() == 8 {
|
||||
Self::ULong
|
||||
} else {
|
||||
Self::ULongLong
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_i32(value: i32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Short),
|
||||
1 => Some(Self::Int),
|
||||
2 => Some(Self::Long),
|
||||
4 => Some(Self::Double),
|
||||
5 => Some(Self::String),
|
||||
6 => Some(Self::Object),
|
||||
10 => Some(Self::UShort),
|
||||
11 => Some(Self::Uint),
|
||||
12 => Some(Self::ULong),
|
||||
14 => Some(Self::Bool),
|
||||
16 => Some(Self::ObjectEx),
|
||||
17 => Some(Self::LongLong),
|
||||
18 => Some(Self::ULongLong),
|
||||
19 => Some(Self::PySsizeT),
|
||||
_ => None,
|
||||
}
|
||||
@@ -363,6 +402,75 @@ pub const fn member_kind_of<T: MemberLayout, Owner>(
|
||||
<T as MemberLayout>::KIND
|
||||
}
|
||||
|
||||
impl MemberLayout for i16 {
|
||||
const KIND: MemberKind = MemberKind::Short;
|
||||
}
|
||||
impl MemberLayout for core::sync::atomic::AtomicI16 {
|
||||
const KIND: MemberKind = MemberKind::Short;
|
||||
}
|
||||
impl MemberCell for core::sync::atomic::AtomicI16 {}
|
||||
|
||||
impl MemberLayout for u16 {
|
||||
const KIND: MemberKind = MemberKind::UShort;
|
||||
}
|
||||
impl MemberLayout for core::sync::atomic::AtomicU16 {
|
||||
const KIND: MemberKind = MemberKind::UShort;
|
||||
}
|
||||
impl MemberCell for core::sync::atomic::AtomicU16 {}
|
||||
|
||||
// `i64` is `long` where `c_long` is 8 bytes, and `long long` otherwise.
|
||||
impl MemberLayout for i64 {
|
||||
const KIND: MemberKind = MemberKind::from_i64();
|
||||
}
|
||||
impl MemberLayout for core::sync::atomic::AtomicI64 {
|
||||
const KIND: MemberKind = MemberKind::from_i64();
|
||||
}
|
||||
impl MemberCell for core::sync::atomic::AtomicI64 {}
|
||||
|
||||
impl MemberLayout for u64 {
|
||||
const KIND: MemberKind = MemberKind::from_u64();
|
||||
}
|
||||
impl MemberLayout for core::sync::atomic::AtomicU64 {
|
||||
const KIND: MemberKind = MemberKind::from_u64();
|
||||
}
|
||||
impl MemberCell for core::sync::atomic::AtomicU64 {}
|
||||
|
||||
/// Readonly `char *`. The word is the pointer, not the characters.
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CStrMember(*const core::ffi::c_char);
|
||||
|
||||
impl core::fmt::Debug for CStrMember {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("CStrMember").field("ptr", &self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CStrMember {
|
||||
#[must_use]
|
||||
pub const fn new(ptr: *const core::ffi::c_char) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn as_ptr(self) -> *const core::ffi::c_char {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// The pointer is not dereferenced by moving the wrapper.
|
||||
unsafe impl Send for CStrMember {}
|
||||
unsafe impl Sync for CStrMember {}
|
||||
|
||||
const _: () = assert!(
|
||||
size_of::<CStrMember>() == size_of::<*const core::ffi::c_char>()
|
||||
&& align_of::<CStrMember>() == align_of::<*const core::ffi::c_char>()
|
||||
);
|
||||
|
||||
impl MemberLayout for CStrMember {
|
||||
const KIND: MemberKind = MemberKind::String;
|
||||
}
|
||||
|
||||
impl MemberLayout for bool {
|
||||
const KIND: MemberKind = MemberKind::Bool;
|
||||
}
|
||||
@@ -405,6 +513,13 @@ impl<T> MemberLayout for Option<PyRef<T>> {
|
||||
impl MemberLayout for crate::object::PyAtomicRef<PyObject> {
|
||||
const KIND: MemberKind = MemberKind::Object;
|
||||
}
|
||||
// `PyAtomicRef<T>` is the same pointer-sized cell as `PyAtomicRef<PyObject>`
|
||||
// (`PhantomData<T>` is zero-sized). Readonly object members may use it. The
|
||||
// getter loads the slot as an object pointer. Writable cells stay
|
||||
// `PyAtomicRef<PyObject>` or `PyAtomicRef<Option<PyObject>>` (`MemberCell`).
|
||||
impl<T: PyPayload> MemberLayout for crate::object::PyAtomicRef<T> {
|
||||
const KIND: MemberKind = MemberKind::Object;
|
||||
}
|
||||
impl MemberLayout for crate::object::PyAtomicRef<Option<PyObject>> {
|
||||
const KIND: MemberKind = MemberKind::Object;
|
||||
}
|
||||
@@ -436,6 +551,34 @@ impl MemberLayout for core::sync::atomic::AtomicIsize {
|
||||
}
|
||||
impl MemberCell for core::sync::atomic::AtomicIsize {}
|
||||
|
||||
// `usize` and `isize` are the same width. A readonly `Py_ssize_t` member may
|
||||
// be a `usize` that is not written after publication and whose value fits in
|
||||
// `isize` (the getter reads the bits as `isize`).
|
||||
const _: () =
|
||||
assert!(size_of::<usize>() == size_of::<isize>() && align_of::<usize>() == align_of::<isize>());
|
||||
impl MemberLayout for usize {
|
||||
const KIND: MemberKind = MemberKind::PySsizeT;
|
||||
}
|
||||
|
||||
// Readonly `co_filename`. The field is `AtomicPtr<PyStrInterned>`: the pointer
|
||||
// is never null, and `PyStrInterned` is `repr(transparent)` over `Py<PyStr>`,
|
||||
// so the word is an object pointer. `member_get_one` reads an Object member
|
||||
// through `PyAtomicRef<Option<PyObject>>` (`get_slot`). That cell is
|
||||
// `AtomicPtr<u8>` when `threading` is on and `Cell<*mut u8>` otherwise; both
|
||||
// are one pointer, and `AtomicPtr<PyStrInterned>` is one pointer in either
|
||||
// build (`Atomic<*mut T>` stores `Align8<*mut T>`, same size and alignment as
|
||||
// the pointer). The load copies that word and increfs. Interned strings live
|
||||
// for the process, and the slot does not own the reference.
|
||||
const _: () = assert!(
|
||||
size_of::<core::sync::atomic::AtomicPtr<PyStrInterned>>()
|
||||
== size_of::<crate::object::PyAtomicRef<Option<PyObject>>>()
|
||||
&& align_of::<core::sync::atomic::AtomicPtr<PyStrInterned>>()
|
||||
== align_of::<crate::object::PyAtomicRef<Option<PyObject>>>()
|
||||
);
|
||||
impl MemberLayout for core::sync::atomic::AtomicPtr<PyStrInterned> {
|
||||
const KIND: MemberKind = MemberKind::Object;
|
||||
}
|
||||
|
||||
/// Where `PyMemberDef.offset` points.
|
||||
///
|
||||
/// `Offset` is a byte offset from the object to the field.
|
||||
@@ -447,6 +590,18 @@ pub enum MemberAccess {
|
||||
TupleItem,
|
||||
}
|
||||
|
||||
/// C layout of `PyMemberDef`: name pointer, int type, `Py_ssize_t` offset,
|
||||
/// int flags, doc pointer. `size_of` of this is `type`'s `tp_itemsize`.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PyMemberDefLayout {
|
||||
_name: *const core::ffi::c_char,
|
||||
_type: core::ffi::c_int,
|
||||
_offset: isize,
|
||||
_flags: core::ffi::c_int,
|
||||
_doc: *const core::ffi::c_char,
|
||||
}
|
||||
|
||||
/// Same fields as `PyMemberDef`: name, type, offset, flags, doc.
|
||||
pub struct PyMemberDef {
|
||||
pub name: String,
|
||||
@@ -539,7 +694,14 @@ impl PyMemberDescriptor {
|
||||
MemberKind::Bool
|
||||
| MemberKind::Double
|
||||
| MemberKind::Int
|
||||
| MemberKind::Long
|
||||
| MemberKind::LongLong
|
||||
| MemberKind::Short
|
||||
| MemberKind::String
|
||||
| MemberKind::Uint
|
||||
| MemberKind::ULong
|
||||
| MemberKind::ULongLong
|
||||
| MemberKind::UShort
|
||||
| MemberKind::PySsizeT => None,
|
||||
}
|
||||
}
|
||||
@@ -689,6 +851,168 @@ fn load_i32(obj: &PyObject, offset: isize, readonly: bool) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_i16(obj: &PyObject, offset: isize, readonly: bool) -> i16 {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
// SAFETY: a readonly short member addresses an `i16` that is not written
|
||||
// after publication.
|
||||
unsafe { addr.cast::<i16>().read() }
|
||||
} else {
|
||||
// SAFETY: a writable short member addresses an aligned `AtomicI16`.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI16>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_u16(obj: &PyObject, offset: isize, readonly: bool) -> u16 {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
// SAFETY: a readonly unsigned short member addresses a `u16` that is not
|
||||
// written after publication.
|
||||
unsafe { addr.cast::<u16>().read() }
|
||||
} else {
|
||||
// SAFETY: a writable unsigned short member addresses an aligned `AtomicU16`.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU16>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_i64(obj: &PyObject, offset: isize, readonly: bool) -> i64 {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
// SAFETY: a readonly `long long` member addresses an `i64` that is not
|
||||
// written after publication.
|
||||
unsafe { addr.cast::<i64>().read() }
|
||||
} else {
|
||||
// SAFETY: a writable `long long` member addresses an aligned `AtomicI64`.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_u64(obj: &PyObject, offset: isize, readonly: bool) -> u64 {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
// SAFETY: a readonly `unsigned long long` member is a `u64` or an
|
||||
// `AtomicU64` of the same size and alignment. A relaxed atomic load
|
||||
// reads either; a plain integer is not written after publication, and
|
||||
// an atomic word (type flags) is updated in place.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
} else {
|
||||
// SAFETY: a writable `unsigned long long` member addresses an aligned `AtomicU64`.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)] // `c_long` is `i32` or `i64`
|
||||
fn load_c_long(obj: &PyObject, offset: isize, readonly: bool) -> core::ffi::c_long {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
// SAFETY: a readonly `long` member addresses a `c_long` that is not
|
||||
// written after publication.
|
||||
unsafe { addr.cast::<core::ffi::c_long>().read() }
|
||||
} else if size_of::<core::ffi::c_long>() == 8 {
|
||||
// SAFETY: a writable `long` member addresses an aligned `AtomicI64`.
|
||||
// `c_long` is 8 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_long
|
||||
}
|
||||
} else {
|
||||
// SAFETY: a writable `long` member addresses an aligned `AtomicI32`.
|
||||
// `c_long` is 4 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI32>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_long
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)] // `c_long` is `i32` or `i64`
|
||||
fn store_c_long(obj: &PyObject, offset: isize, value: core::ffi::c_long) {
|
||||
let addr = member_addr(obj, offset);
|
||||
if size_of::<core::ffi::c_long>() == 8 {
|
||||
// SAFETY: a writable `long` member addresses an aligned `AtomicI64`.
|
||||
// `c_long` is 8 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI64>())
|
||||
.store(value as i64, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: a writable `long` member addresses an aligned `AtomicI32`.
|
||||
// `c_long` is 4 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicI32>())
|
||||
.store(value as i32, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)] // `c_ulong` is `u32` or `u64`
|
||||
fn load_c_ulong(obj: &PyObject, offset: isize, readonly: bool) -> core::ffi::c_ulong {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly && size_of::<core::ffi::c_ulong>() == 8 {
|
||||
// SAFETY: a readonly `unsigned long` member is a `c_ulong` or an
|
||||
// `AtomicU64` of the same size and alignment. A relaxed atomic load
|
||||
// reads either; a plain integer is not written after publication, and
|
||||
// an atomic word (type flags) is updated in place.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_ulong
|
||||
}
|
||||
} else if readonly {
|
||||
// SAFETY: a readonly `unsigned long` member addresses a `c_ulong` that
|
||||
// is not written after publication. `c_ulong` is 4 bytes on this target.
|
||||
unsafe { addr.cast::<core::ffi::c_ulong>().read() }
|
||||
} else if size_of::<core::ffi::c_ulong>() == 8 {
|
||||
// SAFETY: a writable `unsigned long` member addresses an aligned `AtomicU64`.
|
||||
// `c_ulong` is 8 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU64>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_ulong
|
||||
}
|
||||
} else {
|
||||
// SAFETY: a writable `unsigned long` member addresses an aligned `AtomicU32`.
|
||||
// `c_ulong` is 4 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU32>())
|
||||
.load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_ulong
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)] // `c_ulong` is `u32` or `u64`
|
||||
fn store_c_ulong(obj: &PyObject, offset: isize, value: core::ffi::c_ulong) {
|
||||
let addr = member_addr(obj, offset);
|
||||
if size_of::<core::ffi::c_ulong>() == 8 {
|
||||
// SAFETY: a writable `unsigned long` member addresses an aligned `AtomicU64`.
|
||||
// `c_ulong` is 8 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU64>())
|
||||
.store(value as u64, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: a writable `unsigned long` member addresses an aligned `AtomicU32`.
|
||||
// `c_ulong` is 4 bytes on this target.
|
||||
unsafe {
|
||||
(*addr.cast::<core::sync::atomic::AtomicU32>())
|
||||
.store(value as u32, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_u32(obj: &PyObject, offset: isize, readonly: bool) -> u32 {
|
||||
let addr = member_addr(obj, offset);
|
||||
if readonly {
|
||||
@@ -740,13 +1064,16 @@ fn member_get_one(
|
||||
) -> PyResult {
|
||||
let value = match member.kind {
|
||||
MemberKind::Object => obj.get_slot(offset).unwrap_or_else(|| vm.ctx.none()),
|
||||
MemberKind::ObjectEx => obj.get_slot(offset).ok_or_else(|| {
|
||||
vm.new_attribute_error(format!(
|
||||
"'{}' object has no attribute '{}'",
|
||||
obj.class().fully_qualified_name(vm),
|
||||
member.name
|
||||
))
|
||||
})?,
|
||||
MemberKind::ObjectEx => match obj.get_slot(offset) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"'{}' object has no attribute '{}'",
|
||||
obj.class().fully_qualified_name(vm)?,
|
||||
member.name
|
||||
)));
|
||||
}
|
||||
},
|
||||
MemberKind::Bool => {
|
||||
// SAFETY: a bool member addresses an `AtomicBool` or a `bool`
|
||||
// (one byte, naturally aligned). The macro rejects any other field
|
||||
@@ -797,6 +1124,56 @@ fn member_get_one(
|
||||
};
|
||||
vm.ctx.new_int(raw).into()
|
||||
}
|
||||
MemberKind::Short => vm
|
||||
.ctx
|
||||
.new_int(load_i16(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::UShort => vm
|
||||
.ctx
|
||||
.new_int(load_u16(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::Long => vm
|
||||
.ctx
|
||||
.new_int(load_c_long(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::LongLong => vm
|
||||
.ctx
|
||||
.new_int(load_i64(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::ULong => vm
|
||||
.ctx
|
||||
.new_int(load_c_ulong(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::ULongLong => vm
|
||||
.ctx
|
||||
.new_int(load_u64(obj, offset, member.readonly()))
|
||||
.into(),
|
||||
MemberKind::String => {
|
||||
// SAFETY: a string member addresses a `*const c_char` that is not
|
||||
// written after publication. Null is `None`.
|
||||
let ptr = unsafe {
|
||||
member_addr(obj, offset)
|
||||
.cast::<*const core::ffi::c_char>()
|
||||
.read()
|
||||
};
|
||||
if ptr.is_null() {
|
||||
vm.ctx.none()
|
||||
} else {
|
||||
// SAFETY: `ptr` is non-null. The slot points at a `CString` the
|
||||
// owner keeps alive, so the bytes stay NUL-terminated for this read.
|
||||
let bytes = unsafe { core::ffi::CStr::from_ptr(ptr) }.to_bytes();
|
||||
let Ok(text) = core::str::from_utf8(bytes) else {
|
||||
return Err(vm.new_unicode_decode_error(
|
||||
vm.ctx.new_str("utf-8"),
|
||||
vm.ctx.new_bytes(bytes.to_vec()),
|
||||
0,
|
||||
bytes.len(),
|
||||
vm.ctx.new_str("invalid UTF-8"),
|
||||
));
|
||||
};
|
||||
vm.ctx.new_str(text).into()
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
@@ -908,10 +1285,131 @@ fn member_set_one(
|
||||
.store(stored, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
MemberKind::Short => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let long_val = member_as_c_long(&value, vm)?;
|
||||
let stored = long_val as i16;
|
||||
// SAFETY: a writable short member addresses an aligned `AtomicI16`.
|
||||
unsafe {
|
||||
(*member_addr(obj, offset).cast::<core::sync::atomic::AtomicI16>())
|
||||
.store(stored, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
if long_val > i16::MAX as core::ffi::c_long || long_val < i16::MIN as core::ffi::c_long
|
||||
{
|
||||
warn_member(vm, "Truncation of value to short")?;
|
||||
}
|
||||
}
|
||||
MemberKind::UShort => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let long_val = member_as_c_long(&value, vm)?;
|
||||
let stored = long_val as u16;
|
||||
// SAFETY: a writable unsigned short member addresses an aligned `AtomicU16`.
|
||||
unsafe {
|
||||
(*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU16>())
|
||||
.store(stored, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
if long_val > u16::MAX as core::ffi::c_long || long_val < 0 {
|
||||
warn_member(vm, "Truncation of value to unsigned short")?;
|
||||
}
|
||||
}
|
||||
MemberKind::Long => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let stored = member_as_c_long(&value, vm)?;
|
||||
store_c_long(obj, offset, stored);
|
||||
}
|
||||
MemberKind::LongLong => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let stored = member_as_c_longlong(&value, vm)?;
|
||||
// SAFETY: a writable `long long` member addresses an aligned `AtomicI64`.
|
||||
unsafe {
|
||||
(*member_addr(obj, offset).cast::<core::sync::atomic::AtomicI64>())
|
||||
.store(stored, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
MemberKind::ULong => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let (stored, warning) = member_ulong_value(&value, vm)?;
|
||||
store_c_ulong(obj, offset, stored);
|
||||
if let Some(warning) = warning {
|
||||
warn_member(vm, warning)?;
|
||||
}
|
||||
}
|
||||
MemberKind::ULongLong => {
|
||||
let PySetterValue::Assign(value) = value else {
|
||||
return Err(vm.new_type_error("can't delete numeric/char attribute"));
|
||||
};
|
||||
let (stored, warning) = member_ulonglong_value(&value, vm)?;
|
||||
// SAFETY: a writable `unsigned long long` member addresses an aligned `AtomicU64`.
|
||||
unsafe {
|
||||
(*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU64>())
|
||||
.store(stored, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
if let Some(warning) = warning {
|
||||
warn_member(vm, warning)?;
|
||||
}
|
||||
}
|
||||
MemberKind::String => {
|
||||
return Err(vm.new_type_error("readonly attribute"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn member_as_c_longlong(value: &PyObject, vm: &VirtualMachine) -> PyResult<i64> {
|
||||
let int_obj = value.try_index(vm)?;
|
||||
i64::try_from(int_obj.as_bigint())
|
||||
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C long long"))
|
||||
}
|
||||
|
||||
fn member_ulong_value(
|
||||
value: &PyObject,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(core::ffi::c_ulong, Option<&'static str>)> {
|
||||
let int_obj = value.try_index(vm)?;
|
||||
let big = int_obj.as_bigint();
|
||||
if big.sign() == malachite_bigint::Sign::Minus {
|
||||
let long_val = core::ffi::c_long::try_from(big)
|
||||
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))?;
|
||||
return Ok((
|
||||
long_val as core::ffi::c_ulong,
|
||||
Some("Writing negative value into unsigned field"),
|
||||
));
|
||||
}
|
||||
let stored = core::ffi::c_ulong::try_from(big)
|
||||
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C unsigned long"))?;
|
||||
Ok((stored, None))
|
||||
}
|
||||
|
||||
fn member_ulonglong_value(
|
||||
value: &PyObject,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(u64, Option<&'static str>)> {
|
||||
let int_obj = value.try_index(vm)?;
|
||||
let big = int_obj.as_bigint();
|
||||
if big.sign() == malachite_bigint::Sign::Minus {
|
||||
let long_val = core::ffi::c_long::try_from(big)
|
||||
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))?;
|
||||
return Ok((
|
||||
long_val as u64,
|
||||
Some("Writing negative value into unsigned field"),
|
||||
));
|
||||
}
|
||||
let stored = u64::try_from(big).map_err(|_| {
|
||||
vm.new_overflow_error("Python int too large to convert to C unsigned long long")
|
||||
})?;
|
||||
Ok((stored, None))
|
||||
}
|
||||
|
||||
impl Representable for PyMemberDescriptor {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
|
||||
@@ -465,7 +465,11 @@ impl FrameObject {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(flags(DISALLOW_INSTANTIATION), with(Py, Representable))]
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<crate::PyObjectRef>(),
|
||||
flags(DISALLOW_INSTANTIATION),
|
||||
with(Py, Representable)
|
||||
)]
|
||||
impl FrameObject {
|
||||
#[pygetset]
|
||||
pub fn f_globals(&self) -> PyDictRef {
|
||||
|
||||
@@ -39,6 +39,7 @@ impl PyPayload for PyGenerator {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<crate::PyObjectRef>(),
|
||||
flags(DISALLOW_INSTANTIATION, HAS_WEAKREF),
|
||||
with(Py, IterNext, Iterable, Representable, Destructor)
|
||||
)]
|
||||
|
||||
@@ -562,11 +562,11 @@ pub(crate) fn subs_parameters(
|
||||
};
|
||||
|
||||
if unpack {
|
||||
let arg_type = arg.class().fully_qualified_name(vm)?;
|
||||
let substituted_type = substituted_arg.class().fully_qualified_name(vm)?;
|
||||
let tuple = substituted_arg.try_to_ref::<PyTuple>(vm).map_err(|_| {
|
||||
vm.new_type_error(format!(
|
||||
"expected __typing_subst__ of {} objects to return a tuple, not {}",
|
||||
arg.class().fully_qualified_name(vm),
|
||||
substituted_arg.class().fully_qualified_name(vm),
|
||||
"expected __typing_subst__ of {arg_type} objects to return a tuple, not {substituted_type}",
|
||||
))
|
||||
})?;
|
||||
for elem in tuple {
|
||||
|
||||
@@ -675,6 +675,7 @@ impl Py<PyMemoryView> {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<isize>(),
|
||||
with(
|
||||
Py,
|
||||
Hashable,
|
||||
|
||||
@@ -12,6 +12,9 @@ use crate::{
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
#[pyclass(module = "types", name = "SimpleNamespace")]
|
||||
// The dict lives in the object extension, not the payload. The offset is
|
||||
// the dict cell at the front of that extension.
|
||||
#[pymember(name = "__dict__", offset = ::rustpython_vm::object::dict_member_offset())]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PyNamespace {}
|
||||
|
||||
|
||||
@@ -212,8 +212,8 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
|
||||
|
||||
if required {
|
||||
// Dict, weakref list, and slot cells sit in the prefix in front of
|
||||
// the payload, so they are not part of `slots.basicsize`. Only state
|
||||
// stored inside the payload counts.
|
||||
// the object header. `slots.basicsize` is the header plus the payload,
|
||||
// so only state stored inside the payload counts.
|
||||
let basicsize = vm.ctx.types.object_type.slots().basicsize;
|
||||
if obj.class().slots().basicsize > basicsize {
|
||||
return Err(vm.new_type_error(format!("cannot pickle '{}' object", obj.class().name())));
|
||||
@@ -343,14 +343,16 @@ impl PyBaseObject {
|
||||
#[pyslot]
|
||||
fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
let class = zelf.class();
|
||||
// A missing `__module__` is not an error here.
|
||||
let module = class.__module__(vm).ok();
|
||||
match (
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
module
|
||||
.as_ref()
|
||||
.and_then(|m| m.downcast_ref::<PyStr>())
|
||||
.map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => Err(vm.new_type_error("Unknown qualified name")),
|
||||
@@ -506,7 +508,11 @@ impl PyBaseObject {
|
||||
|
||||
#[pymethod]
|
||||
fn __sizeof__(zelf: PyObjectRef) -> usize {
|
||||
zelf.class().slots().basicsize
|
||||
// `slots.basicsize` includes the object header. This reports the payload.
|
||||
zelf.class()
|
||||
.slots()
|
||||
.basicsize
|
||||
.saturating_sub(crate::object::SIZEOF_PYOBJECT_HEAD)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,15 +198,13 @@ impl Representable for PyStaticMethod {
|
||||
let callable = zelf.callable.load_owned().repr(vm)?;
|
||||
let class = Self::class(&vm.ctx);
|
||||
|
||||
let module = class.__module__(vm)?;
|
||||
match (
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|m| m.as_wtf8()),
|
||||
module.downcast_ref::<PyStr>().map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => Err(vm.new_type_error("Unknown qualified name")),
|
||||
(Some(qualname), Some(module)) if module != "builtins" => {
|
||||
|
||||
+149
-126
@@ -9,32 +9,52 @@ use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::function::PyCell,
|
||||
class::PyClassImpl,
|
||||
common::lock::PyRwLock,
|
||||
function::{FuncArgs, IntoFuncArgs, OptionalArg},
|
||||
object::PyAtomicRef,
|
||||
types::{Callable, Constructor, GetAttr, GetDescriptor, Initializer, Representable},
|
||||
};
|
||||
|
||||
#[pyclass(module = false, name = "super", traverse)]
|
||||
#[derive(Debug)]
|
||||
pub struct PySuper {
|
||||
inner: PyRwLock<PySuperInner>,
|
||||
#[pymember(name = "__thisclass__")]
|
||||
typ: PyAtomicRef<Option<PyType>>,
|
||||
#[pymember(name = "__self__")]
|
||||
obj: PyAtomicRef<Option<PyObject>>,
|
||||
#[pymember(name = "__self_class__")]
|
||||
obj_type: PyAtomicRef<Option<PyType>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Traverse)]
|
||||
struct PySuperInner {
|
||||
fn bind_super(
|
||||
typ: PyTypeRef,
|
||||
obj: Option<(PyObjectRef, PyTypeRef)>,
|
||||
obj: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(PyTypeRef, Option<PyObjectRef>, Option<PyTypeRef>)> {
|
||||
if vm.is_none(&obj) {
|
||||
return Ok((typ, None, None));
|
||||
}
|
||||
let obj_type = super_check(&typ, &obj, vm)?;
|
||||
Ok((typ, Some(obj), Some(obj_type)))
|
||||
}
|
||||
|
||||
impl PySuperInner {
|
||||
fn new(typ: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let obj = if vm.is_none(&obj) {
|
||||
None
|
||||
} else {
|
||||
let obj_type = super_check(&typ, &obj, vm)?;
|
||||
Some((obj, obj_type))
|
||||
};
|
||||
Ok(Self { typ, obj })
|
||||
impl PySuper {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
typ: PyAtomicRef::from(None),
|
||||
obj: PyAtomicRef::from(None),
|
||||
obj_type: PyAtomicRef::from(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn store_bound(
|
||||
&self,
|
||||
typ: Option<PyTypeRef>,
|
||||
obj: Option<PyObjectRef>,
|
||||
obj_type: Option<PyTypeRef>,
|
||||
) {
|
||||
drop(self.typ.store(typ));
|
||||
drop(self.obj.store(obj));
|
||||
drop(self.obj_type.store(obj_type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,14 +68,9 @@ impl PyPayload for PySuper {
|
||||
impl Constructor for PySuper {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, _args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: PyRwLock::new(PySuperInner::new(
|
||||
vm.ctx.types.object_type.to_owned(), // is this correct?
|
||||
vm.ctx.none(),
|
||||
vm,
|
||||
)?),
|
||||
})
|
||||
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
|
||||
// `tp_new` leaves every field null. `tp_init` fills them in.
|
||||
Ok(Self::empty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,106 +95,109 @@ impl Initializer for PySuper {
|
||||
Self::Args { py_type, object }: Self::Args,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// Get the type:
|
||||
let (typ, obj) = if let OptionalArg::Present(ty) = py_type {
|
||||
(ty, object.unwrap_or_none(vm))
|
||||
} else {
|
||||
// Access the InterpreterFrame directly — no need to materialize
|
||||
// a FrameObject just to read code/locals.
|
||||
let iframe_ptr = crate::vm::thread::get_current_frame();
|
||||
if iframe_ptr.is_null() {
|
||||
return Err(vm.new_runtime_error("super(): no current frame"));
|
||||
}
|
||||
let iframe = unsafe { &*iframe_ptr };
|
||||
let code = iframe.code();
|
||||
|
||||
if code.arg_count == 0 {
|
||||
return Err(vm.new_runtime_error("super(): no arguments"));
|
||||
}
|
||||
|
||||
// SAFETY: InterpreterFrame is current and not concurrently mutated.
|
||||
use rustpython_compiler_core::bytecode::CO_FAST_CELL;
|
||||
let fastlocals = iframe.localsplus.fastlocals();
|
||||
let obj = fastlocals[0]
|
||||
.clone()
|
||||
.and_then(|val| {
|
||||
// If slot 0 is a merged cell (LOCAL|CELL), extract value from cell
|
||||
if code
|
||||
.localspluskinds
|
||||
.first()
|
||||
.is_some_and(|&k| k & CO_FAST_CELL != 0)
|
||||
{
|
||||
val.downcast_ref::<PyCell>().and_then(|c| c.get())
|
||||
} else {
|
||||
Some(val)
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| vm.new_runtime_error("super(): arg[0] deleted"))?;
|
||||
|
||||
let mut typ = None;
|
||||
// Search for __class__ in freevars using localspluskinds
|
||||
let nlocalsplus = code.localspluskinds.len();
|
||||
let nfrees = code.freevars.len();
|
||||
let free_start = nlocalsplus - nfrees;
|
||||
for (i, var) in code.freevars.iter().enumerate() {
|
||||
if var.as_bytes() == b"__class__" {
|
||||
let class = fastlocals[free_start + i]
|
||||
.as_ref()
|
||||
.and_then(|v| v.downcast_ref::<PyCell>())
|
||||
.and_then(|c| c.get())
|
||||
.ok_or_else(|| vm.new_runtime_error("super(): empty __class__ cell"))?;
|
||||
typ = Some(class.downcast().map_err(|o| {
|
||||
vm.new_type_error(format!(
|
||||
"super(): __class__ is not a type ({})",
|
||||
o.class().name()
|
||||
))
|
||||
})?);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let typ = typ.ok_or_else(|| {
|
||||
vm.new_type_error(
|
||||
"super must be called with 1 argument or from inside class method",
|
||||
)
|
||||
})?;
|
||||
|
||||
(typ, obj)
|
||||
let typ = match py_type {
|
||||
OptionalArg::Present(ty) => Some(ty),
|
||||
OptionalArg::Missing => None,
|
||||
};
|
||||
|
||||
let inner = PySuperInner::new(typ, obj, vm)?;
|
||||
*zelf.inner.write() = inner;
|
||||
|
||||
Ok(())
|
||||
let obj = match object {
|
||||
OptionalArg::Present(obj) => Some(obj),
|
||||
OptionalArg::Missing => None,
|
||||
};
|
||||
super_init_impl(zelf, typ, obj, vm)
|
||||
}
|
||||
}
|
||||
|
||||
/// `super_init_impl`. A null type takes the zero-argument path.
|
||||
fn super_init_impl(
|
||||
zelf: &Py<PySuper>,
|
||||
typ: Option<PyTypeRef>,
|
||||
obj: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let (typ, obj) = match typ {
|
||||
Some(typ) => (typ, obj.unwrap_or_else(|| vm.ctx.none())),
|
||||
None => super_init_without_args(vm)?,
|
||||
};
|
||||
let (typ, obj, obj_type) = bind_super(typ, obj, vm)?;
|
||||
zelf.store_bound(Some(typ), obj, obj_type);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn super_init_without_args(vm: &VirtualMachine) -> PyResult<(PyTypeRef, PyObjectRef)> {
|
||||
// Access the InterpreterFrame directly — no need to materialize
|
||||
// a FrameObject just to read code/locals.
|
||||
let iframe_ptr = crate::vm::thread::get_current_frame();
|
||||
if iframe_ptr.is_null() {
|
||||
return Err(vm.new_runtime_error("super(): no current frame"));
|
||||
}
|
||||
let iframe = unsafe { &*iframe_ptr };
|
||||
let code = iframe.code();
|
||||
|
||||
if code.arg_count == 0 {
|
||||
return Err(vm.new_runtime_error("super(): no arguments"));
|
||||
}
|
||||
|
||||
// SAFETY: InterpreterFrame is current and not concurrently mutated.
|
||||
use rustpython_compiler_core::bytecode::CO_FAST_CELL;
|
||||
let fastlocals = iframe.localsplus.fastlocals();
|
||||
let obj = fastlocals[0]
|
||||
.clone()
|
||||
.and_then(|val| {
|
||||
// If slot 0 is a merged cell (LOCAL|CELL), extract value from cell
|
||||
if code
|
||||
.localspluskinds
|
||||
.first()
|
||||
.is_some_and(|&k| k & CO_FAST_CELL != 0)
|
||||
{
|
||||
val.downcast_ref::<PyCell>().and_then(|c| c.get())
|
||||
} else {
|
||||
Some(val)
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| vm.new_runtime_error("super(): arg[0] deleted"))?;
|
||||
|
||||
let mut typ = None;
|
||||
// Search for __class__ in freevars using localspluskinds
|
||||
let nlocalsplus = code.localspluskinds.len();
|
||||
let nfrees = code.freevars.len();
|
||||
let free_start = nlocalsplus - nfrees;
|
||||
for (i, var) in code.freevars.iter().enumerate() {
|
||||
if var.as_bytes() == b"__class__" {
|
||||
let class = fastlocals[free_start + i]
|
||||
.as_ref()
|
||||
.and_then(|v| v.downcast_ref::<PyCell>())
|
||||
.and_then(|c| c.get())
|
||||
.ok_or_else(|| vm.new_runtime_error("super(): empty __class__ cell"))?;
|
||||
typ = Some(class.downcast().map_err(|o| {
|
||||
vm.new_type_error(format!(
|
||||
"super(): __class__ is not a type ({})",
|
||||
o.class().name()
|
||||
))
|
||||
})?);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let typ = typ.ok_or_else(|| {
|
||||
vm.new_type_error("super must be called with 1 argument or from inside class method")
|
||||
})?;
|
||||
|
||||
Ok((typ, obj))
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
with(GetAttr, GetDescriptor, Constructor, Initializer, Representable),
|
||||
flags(BASETYPE)
|
||||
)]
|
||||
impl PySuper {
|
||||
#[pygetset]
|
||||
fn __thisclass__(&self) -> PyTypeRef {
|
||||
self.inner.read().typ.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __self_class__(&self) -> Option<PyTypeRef> {
|
||||
Some(self.inner.read().obj.as_ref()?.1.clone())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __self__(&self) -> Option<PyObjectRef> {
|
||||
Some(self.inner.read().obj.as_ref()?.0.clone())
|
||||
}
|
||||
}
|
||||
impl PySuper {}
|
||||
|
||||
impl GetAttr for PySuper {
|
||||
fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
|
||||
let skip = |zelf: &Py<Self>, name| zelf.as_object().generic_getattr(name, vm);
|
||||
let (obj, start_type): (PyObjectRef, PyTypeRef) = match &zelf.inner.read().obj {
|
||||
Some(o) => o.clone(),
|
||||
None => return skip(zelf, name),
|
||||
let Some(obj) = zelf.obj.load_owned() else {
|
||||
return skip(zelf, name);
|
||||
};
|
||||
let Some(start_type) = zelf.obj_type.load_owned() else {
|
||||
return skip(zelf, name);
|
||||
};
|
||||
|
||||
// We want __class__ to return the class of the super object
|
||||
@@ -195,7 +213,9 @@ impl GetAttr for PySuper {
|
||||
// Both locks are dropped before any arbitrary Python code
|
||||
// (the descriptor call below) runs, so they can't be held
|
||||
// across a call that might re-enter and want them again.
|
||||
let su_type = zelf.inner.read().typ.clone();
|
||||
let Some(su_type) = zelf.typ.load_owned() else {
|
||||
return skip(zelf, name);
|
||||
};
|
||||
let descr = {
|
||||
let mro = start_type.mro.read();
|
||||
mro.iter()
|
||||
@@ -230,20 +250,23 @@ impl GetDescriptor for PySuper {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = Self::_unwrap(zelf_obj, obj, vm)?;
|
||||
if vm.is_none(obj) || zelf.inner.read().obj.is_some() {
|
||||
if vm.is_none(obj) || zelf.obj.deref().is_some() {
|
||||
return Ok(zelf_obj.to_owned());
|
||||
}
|
||||
let zelf_class = zelf.as_object().class();
|
||||
let typ = zelf.typ.load_owned();
|
||||
if zelf_class.is(vm.ctx.types.super_type) {
|
||||
let typ = zelf.inner.read().typ.clone();
|
||||
Ok(Self {
|
||||
inner: PyRwLock::new(PySuperInner::new(typ, obj.to_owned(), vm)?),
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
.into())
|
||||
let newobj = Self::empty().into_ref(&vm.ctx);
|
||||
// A null type takes the zero-argument path inside super_init_impl.
|
||||
super_init_impl(&newobj, typ, Some(obj.to_owned()), vm)?;
|
||||
Ok(newobj.into())
|
||||
} else {
|
||||
let typ = zelf.inner.read().typ.clone();
|
||||
PyType::call(zelf.class(), (typ, obj.to_owned()).into_args(vm), vm)
|
||||
// Call stops at the first null argument.
|
||||
let args = match typ {
|
||||
Some(typ) => (typ, obj.to_owned()).into_args(vm),
|
||||
None => FuncArgs::default(),
|
||||
};
|
||||
PyType::call(zelf.class(), args, vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,12 +274,12 @@ impl GetDescriptor for PySuper {
|
||||
impl Representable for PySuper {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
let type_name = zelf.inner.read().typ.name().to_owned();
|
||||
let obj = zelf.inner.read().obj.clone();
|
||||
let repr = match obj {
|
||||
Some((_, ref ty)) => {
|
||||
format!("<super: <class '{}'>, <{} object>>", type_name, ty.name())
|
||||
}
|
||||
let type_name = match zelf.typ.load_owned() {
|
||||
Some(ty) => ty.name().to_owned(),
|
||||
None => "NULL".to_owned(),
|
||||
};
|
||||
let repr = match zelf.obj_type.load_owned() {
|
||||
Some(ty) => format!("<super: <class '{}'>, <{} object>>", type_name, ty.name()),
|
||||
None => format!("<super: <class '{type_name}'>, NULL>"),
|
||||
};
|
||||
Ok(repr)
|
||||
|
||||
@@ -10,9 +10,11 @@ use rustpython_compiler_core::OneIndexed;
|
||||
#[derive(Debug)]
|
||||
pub struct PyTraceback {
|
||||
pub next: PyMutex<Option<PyTracebackRef>>,
|
||||
#[pymember(name = "tb_frame", audit_read)]
|
||||
pub frame: FrameObjectRef,
|
||||
#[pymember(name = "tb_lasti")]
|
||||
#[pytraverse(skip)]
|
||||
pub lasti: u32,
|
||||
pub lasti: i32,
|
||||
#[pytraverse(skip)]
|
||||
pub lineno: OneIndexed,
|
||||
}
|
||||
@@ -32,7 +34,7 @@ impl PyTraceback {
|
||||
pub const fn new(
|
||||
next: Option<PyRef<Self>>,
|
||||
frame: FrameObjectRef,
|
||||
lasti: u32,
|
||||
lasti: i32,
|
||||
lineno: OneIndexed,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -43,16 +45,6 @@ impl PyTraceback {
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn tb_frame(&self) -> FrameObjectRef {
|
||||
self.frame.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
const fn tb_lasti(&self) -> u32 {
|
||||
self.lasti
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
const fn tb_lineno(&self) -> usize {
|
||||
self.lineno.get()
|
||||
@@ -104,7 +96,7 @@ impl PyTraceback {
|
||||
}
|
||||
|
||||
impl Constructor for PyTraceback {
|
||||
type Args = (Option<PyRef<Self>>, FrameObjectRef, u32, usize);
|
||||
type Args = (Option<PyRef<Self>>, FrameObjectRef, i32, usize);
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let (next, frame, lasti, lineno) = args;
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::{
|
||||
},
|
||||
class::{PyClassDef, PyClassImpl, StaticType},
|
||||
common::{
|
||||
ascii,
|
||||
borrow::BorrowedValue,
|
||||
lock::{PyRwLock, PyRwLockReadGuard},
|
||||
},
|
||||
@@ -37,7 +36,7 @@ use core::{
|
||||
cell::Cell,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, Ordering},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering},
|
||||
};
|
||||
use indexmap::{IndexMap, map::Entry};
|
||||
use itertools::Itertools;
|
||||
@@ -56,11 +55,13 @@ pub struct PyType {
|
||||
pub mro: PyRwLock<Vec<PyTypeRef>>,
|
||||
pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
|
||||
pub attributes: TypeNamespace,
|
||||
#[pymember(name = "__itemsize__", path = "itemsize")]
|
||||
#[pymember(name = "__basicsize__", path = "basicsize")]
|
||||
#[pymember(name = "__flags__", path = "flags")]
|
||||
pub slots: PyTypeSlots,
|
||||
pub heaptype_ext: Option<Pin<Box<HeapTypeExt>>>,
|
||||
/// Type version tag for inline caching. 0 means unassigned/invalidated.
|
||||
pub tp_version_tag: AtomicU32,
|
||||
pub abc_tpflags: AtomicU64,
|
||||
}
|
||||
|
||||
/// Monotonic counter for type version tags. Once it reaches `u32::MAX`,
|
||||
@@ -851,9 +852,7 @@ impl PyType {
|
||||
|
||||
// Check each base in order and inherit the first collection flag found
|
||||
for base in bases {
|
||||
let base_flags = (base.slots.flags
|
||||
| PyTypeFlags::from_bits_truncate(base.abc_tpflags.load(Ordering::Acquire)))
|
||||
& COLLECTION_FLAGS;
|
||||
let base_flags = base.slots.flags.load() & COLLECTION_FLAGS;
|
||||
if !base_flags.is_empty() {
|
||||
slots.flags |= base_flags;
|
||||
return;
|
||||
@@ -861,40 +860,21 @@ impl PyType {
|
||||
}
|
||||
}
|
||||
|
||||
fn inherited_abc_tpflags(bases: &[PyRef<Self>]) -> u64 {
|
||||
const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
|
||||
PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
|
||||
);
|
||||
for base in bases {
|
||||
let base_flags =
|
||||
PyTypeFlags::from_bits_truncate(base.abc_tpflags.load(Ordering::Acquire))
|
||||
& COLLECTION_FLAGS;
|
||||
if !base_flags.is_empty() {
|
||||
return base_flags.bits();
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn has_patma_collection_flag(&self, flag: PyTypeFlags) -> bool {
|
||||
debug_assert!(matches!(flag, PyTypeFlags::SEQUENCE | PyTypeFlags::MAPPING));
|
||||
const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
|
||||
PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
|
||||
);
|
||||
let slot_flags = self.slots.flags & COLLECTION_FLAGS;
|
||||
if !slot_flags.is_empty() {
|
||||
return slot_flags.contains(flag);
|
||||
}
|
||||
PyTypeFlags::from_bits_truncate(self.abc_tpflags.load(Ordering::Acquire)).contains(flag)
|
||||
let slot_flags = self.slots.flags.load() & COLLECTION_FLAGS;
|
||||
slot_flags.contains(flag)
|
||||
}
|
||||
|
||||
pub fn set_is_abstract(&self, is_abstract: bool) {
|
||||
const MASK: u64 = PyTypeFlags::IS_ABSTRACT.bits();
|
||||
let _ = self
|
||||
.abc_tpflags
|
||||
.try_update(Ordering::AcqRel, Ordering::Acquire, |old| {
|
||||
Some(if is_abstract { old | MASK } else { old & !MASK })
|
||||
});
|
||||
if is_abstract {
|
||||
self.slots.flags.set(PyTypeFlags::IS_ABSTRACT);
|
||||
} else {
|
||||
self.slots.flags.remove(PyTypeFlags::IS_ABSTRACT);
|
||||
}
|
||||
self.modified();
|
||||
}
|
||||
|
||||
@@ -906,13 +886,7 @@ impl PyType {
|
||||
if flags.is_empty() {
|
||||
return;
|
||||
}
|
||||
let collection_bits = COLLECTION_FLAGS.bits();
|
||||
let flags_bits = flags.bits();
|
||||
let _ = self
|
||||
.abc_tpflags
|
||||
.try_update(Ordering::AcqRel, Ordering::Acquire, |old| {
|
||||
Some((old & !collection_bits) | flags_bits)
|
||||
});
|
||||
self.slots.flags.replace_masked(COLLECTION_FLAGS, flags);
|
||||
self.modified();
|
||||
for weak_ref in self.subclasses.read().iter() {
|
||||
if let Some(subclass) = weak_ref.upgrade()
|
||||
@@ -1052,7 +1026,6 @@ impl PyType {
|
||||
));
|
||||
}
|
||||
|
||||
let inherited_abc_tpflags = Self::inherited_abc_tpflags(bases.as_slice());
|
||||
let new_type = PyRef::new_ref(
|
||||
Self {
|
||||
base: Some(base).into(),
|
||||
@@ -1063,7 +1036,6 @@ impl PyType {
|
||||
slots,
|
||||
heaptype_ext: Some(Pin::new(Box::new(heaptype_ext))),
|
||||
tp_version_tag: AtomicU32::new(0),
|
||||
abc_tpflags: AtomicU64::new(inherited_abc_tpflags),
|
||||
},
|
||||
metaclass,
|
||||
None,
|
||||
@@ -1112,7 +1084,6 @@ impl PyType {
|
||||
slots.flags |= PyTypeFlags::MANAGED_WEAKREF;
|
||||
}
|
||||
|
||||
let inherited_abc_tpflags = Self::inherited_abc_tpflags(core::slice::from_ref(&base));
|
||||
let bases =
|
||||
PyTuple::new_ref_typed_with_type(vec![base.clone()], PyTuple::static_type().to_owned());
|
||||
let mro = base.mro_map_collect(|x| x.to_owned());
|
||||
@@ -1127,7 +1098,6 @@ impl PyType {
|
||||
slots,
|
||||
heaptype_ext: None,
|
||||
tp_version_tag: AtomicU32::new(0),
|
||||
abc_tpflags: AtomicU64::new(inherited_abc_tpflags),
|
||||
},
|
||||
metaclass,
|
||||
None,
|
||||
@@ -1586,17 +1556,19 @@ impl PyType {
|
||||
|
||||
/// The type's fully qualified name, the way the `%T` format code prints it:
|
||||
/// `module.qualname`, with a `builtins` or `__main__` module left off.
|
||||
pub fn fully_qualified_name(&self, vm: &VirtualMachine) -> String {
|
||||
pub fn fully_qualified_name(&self, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let qualname = self.__qualname__(vm);
|
||||
let qualname = qualname
|
||||
.downcast_ref::<PyStr>()
|
||||
.and_then(|qualname| qualname.to_str())
|
||||
.map_or_else(|| self.name().to_string(), str::to_owned);
|
||||
let module = self.__module__(vm);
|
||||
match module.downcast_ref::<PyStr>().and_then(|m| m.to_str()) {
|
||||
Some("builtins" | "__main__") | None => qualname,
|
||||
Some(module) => format!("{module}.{qualname}"),
|
||||
}
|
||||
let module = self.__module__(vm)?;
|
||||
Ok(
|
||||
match module.downcast_ref::<PyStr>().and_then(|m| m.to_str()) {
|
||||
Some("builtins" | "__main__") | None => qualname,
|
||||
Some(module) => format!("{module}.{qualname}"),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BorrowedValue<'_, str> {
|
||||
@@ -1722,6 +1694,7 @@ impl Py<PyType> {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<crate::builtins::descriptor::PyMemberDefLayout>(),
|
||||
with(
|
||||
Py,
|
||||
Constructor,
|
||||
@@ -1923,12 +1896,6 @@ impl PyType {
|
||||
result
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __flags__(&self) -> u64 {
|
||||
self.slots.flags.bits()
|
||||
| (self.abc_tpflags.load(Ordering::Acquire) & PyTypeFlags::IS_ABSTRACT.bits())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __abstractmethods__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult {
|
||||
if zelf.is(vm.ctx.types.type_type) {
|
||||
@@ -1970,16 +1937,6 @@ impl PyType {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __basicsize__(&self) -> usize {
|
||||
crate::object::SIZEOF_PYOBJECT_HEAD + self.slots.basicsize
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __itemsize__(&self) -> usize {
|
||||
self.slots.itemsize
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn __name__(&self, vm: &VirtualMachine) -> PyStrRef {
|
||||
self.name_inner(
|
||||
@@ -2243,20 +2200,22 @@ impl PyType {
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn __module__(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.attributes
|
||||
.get(identifier!(vm, __module__))
|
||||
// We need to exclude this method from going into recursion:
|
||||
.filter(|found| !found.fast_isinstance(vm.ctx.types.getset_type))
|
||||
.unwrap_or_else(|| {
|
||||
// For non-heap types, extract module from tp_name (e.g. "typing.TypeAliasType" -> "typing")
|
||||
let slot_name = self.slot_name();
|
||||
if let Some((module, _)) = slot_name.rsplit_once('.') {
|
||||
vm.ctx.intern_str(module).to_object()
|
||||
} else {
|
||||
vm.ctx.new_str(ascii!("builtins")).into()
|
||||
}
|
||||
})
|
||||
pub fn __module__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
// Heap types store the module in the type dict. Static types take the
|
||||
// text before the last `.` of the type name, or `builtins`.
|
||||
if self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
return self
|
||||
.attributes
|
||||
.get(identifier!(vm, __module__))
|
||||
.ok_or_else(|| vm.new_attribute_error("__module__"));
|
||||
}
|
||||
let slot_name = self.slot_name();
|
||||
let module = if let Some((module, _)) = slot_name.rsplit_once('.') {
|
||||
vm.ctx.intern_str(module)
|
||||
} else {
|
||||
vm.ctx.intern_str("builtins")
|
||||
};
|
||||
Ok(module.to_object())
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
@@ -2555,10 +2514,10 @@ impl Constructor for PyType {
|
||||
|
||||
if let Some(globals) = crate::frame::current_globals() {
|
||||
let entry = attributes.entry(identifier!(vm, __module__));
|
||||
if matches!(entry, Entry::Vacant(_)) {
|
||||
let module_name =
|
||||
vm.unwrap_or_none(globals.get_item_opt(identifier!(vm, __name__), vm)?);
|
||||
entry.or_insert(module_name);
|
||||
if let Entry::Vacant(entry) = entry
|
||||
&& let Some(module_name) = globals.get_item_opt(identifier!(vm, __name__), vm)?
|
||||
{
|
||||
entry.insert(module_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2602,14 +2561,10 @@ impl Constructor for PyType {
|
||||
tuple.try_into_typed(vm)?
|
||||
};
|
||||
|
||||
// Check if base has itemsize > 0 - can't add arbitrary slots to variable-size types
|
||||
// Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots
|
||||
// But types like weakref.ref have itemsize = 0 and DO allow slots
|
||||
let has_custom_slots = slots
|
||||
.as_slice()
|
||||
.iter()
|
||||
.any(|s| !matches!(s.as_bytes(), b"__dict__" | b"__weakref__"));
|
||||
if has_custom_slots && base.slots.itemsize > 0 {
|
||||
// Any nonempty __slots__ is rejected when the base has a variable
|
||||
// item size, including a tuple of only `__dict__` or `__weakref__`.
|
||||
// Types like weakref.ref have itemsize 0 and do allow slots.
|
||||
if !slots.as_slice().is_empty() && base.slots.itemsize > 0 {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"nonempty __slots__ not supported for subtype of '{}'",
|
||||
base.name()
|
||||
@@ -2726,14 +2681,16 @@ impl Constructor for PyType {
|
||||
// Add HAS_WEAKREF if:
|
||||
// 1. __slots__ is not defined (automatic weakref support), OR
|
||||
// 2. __weakref__ is in __slots__
|
||||
let may_add_weakref = !base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF);
|
||||
// A variable-size base does not gain a weakref slot.
|
||||
let may_add_weakref =
|
||||
base.slots.itemsize == 0 && !base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF);
|
||||
if (heaptype_slots.is_none() && may_add_weakref) || add_weakref {
|
||||
flags |= PyTypeFlags::HAS_WEAKREF | PyTypeFlags::MANAGED_WEAKREF;
|
||||
}
|
||||
|
||||
let (slots, heaptype_ext) = {
|
||||
let slots = PyTypeSlots {
|
||||
flags,
|
||||
flags: crate::types::PyAtomicTypeFlags::new(flags),
|
||||
member_count,
|
||||
itemsize: base.slots.itemsize,
|
||||
..PyTypeSlots::heap_default()
|
||||
@@ -3301,8 +3258,12 @@ impl AsNumber for PyType {
|
||||
impl Representable for PyType {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let module = zelf.__module__(vm);
|
||||
let module = module.downcast_ref::<PyStr>().map(|m| m.as_wtf8());
|
||||
// A missing `__module__` is not an error here.
|
||||
let module = zelf.__module__(vm).ok();
|
||||
let module = module
|
||||
.as_ref()
|
||||
.and_then(|m| m.downcast_ref::<PyStr>())
|
||||
.map(|m| m.as_wtf8());
|
||||
|
||||
let repr = match module {
|
||||
Some(module) if module != "builtins" => {
|
||||
@@ -3703,7 +3664,7 @@ fn mro_internal(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
|
||||
/// Returns true if the two types have different instance layouts.
|
||||
fn shape_differs(t1: &Py<PyType>, t2: &Py<PyType>) -> bool {
|
||||
t1.__basicsize__() != t2.__basicsize__() || t1.slots.itemsize != t2.slots.itemsize
|
||||
t1.slots.basicsize != t2.slots.basicsize || t1.slots.itemsize != t2.slots.itemsize
|
||||
}
|
||||
|
||||
fn solid_base<'a>(typ: &'a Py<PyType>, vm: &VirtualMachine) -> &'a Py<PyType> {
|
||||
|
||||
+11
-3
@@ -145,10 +145,16 @@ pub trait StaticType {
|
||||
where
|
||||
Self: PyClassImpl,
|
||||
{
|
||||
// inherit_special COPYVAL(tp_itemsize): the direct base, and only when
|
||||
// this type left the slot at 0. The base type object already exists.
|
||||
let mut slots = Self::make_slots();
|
||||
if slots.itemsize == 0 {
|
||||
slots.itemsize = Self::static_baseclass().slots.itemsize;
|
||||
}
|
||||
PyType::new_static(
|
||||
Self::static_baseclass().to_owned(),
|
||||
Default::default(),
|
||||
Self::make_slots(),
|
||||
slots,
|
||||
Self::static_metaclass().to_owned(),
|
||||
)
|
||||
.unwrap()
|
||||
@@ -263,7 +269,9 @@ pub trait PyClassImpl: PyClassDef {
|
||||
|
||||
let _ = ctx.intern_str(Self::NAME); // intern type name
|
||||
|
||||
if Self::TP_FLAGS.has_feature(PyTypeFlags::HAS_DICT) {
|
||||
if Self::TP_FLAGS.has_feature(PyTypeFlags::HAS_DICT)
|
||||
&& !Self::MEMBERS.iter().any(|member| member.name == "__dict__")
|
||||
{
|
||||
let __dict__ = identifier!(ctx, __dict__);
|
||||
class.set_attr(
|
||||
__dict__,
|
||||
@@ -376,7 +384,7 @@ pub trait PyClassImpl: PyClassDef {
|
||||
|
||||
fn make_slots() -> PyTypeSlots {
|
||||
let mut slots = PyTypeSlots {
|
||||
flags: Self::TP_FLAGS,
|
||||
flags: crate::types::PyAtomicTypeFlags::new(Self::TP_FLAGS),
|
||||
name: Self::TP_NAME,
|
||||
basicsize: Self::BASICSIZE,
|
||||
itemsize: Self::ITEMSIZE,
|
||||
|
||||
@@ -657,7 +657,7 @@ pub(crate) fn unraisable_while_closing(
|
||||
if e.traceback().is_none()
|
||||
&& let Some(frame) = coro.frame_opt()
|
||||
{
|
||||
let lasti = frame.lasti().saturating_mul(2);
|
||||
let lasti = frame.lasti().saturating_mul(2) as i32;
|
||||
let lineno = rustpython_compiler_core::OneIndexed::new(frame.lineno().max(1) as usize)
|
||||
.unwrap_or(rustpython_compiler_core::OneIndexed::MIN);
|
||||
let tb = PyTraceback::new(None, frame, lasti, lineno);
|
||||
|
||||
@@ -18,16 +18,23 @@ use crate::exceptions::types::PyBaseException;
|
||||
fn create_exception_group(ctx: &Context) -> PyRef<PyType> {
|
||||
let excs = &ctx.exceptions;
|
||||
let exception_group_slots = PyTypeSlots {
|
||||
flags: PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT,
|
||||
flags: crate::types::PyAtomicTypeFlags::new(
|
||||
PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT,
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
let mut attrs = crate::builtins::type_::PyAttributes::default();
|
||||
attrs.insert(
|
||||
crate::identifier!(ctx, __module__),
|
||||
ctx.intern_str("builtins").to_object(),
|
||||
);
|
||||
PyType::new_heap(
|
||||
"ExceptionGroup",
|
||||
vec![
|
||||
excs.base_exception_group.to_owned(),
|
||||
excs.exception_type.to_owned(),
|
||||
],
|
||||
Default::default(),
|
||||
attrs,
|
||||
exception_group_slots,
|
||||
ctx.types.type_type.to_owned(),
|
||||
ctx,
|
||||
|
||||
@@ -2689,7 +2689,7 @@ pub(crate) fn trampoline_handle_exception(
|
||||
// Add traceback entry at the call site.
|
||||
if let Some((loc, _end_loc)) = exec.code.locations.get(idx) {
|
||||
let next = exception.traceback();
|
||||
let new_traceback = PyTraceback::new(next, exec.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
let new_traceback = PyTraceback::new(next, exec.frame_object(vm), idx as i32 * 2, loc.line);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
}
|
||||
|
||||
@@ -3608,7 +3608,7 @@ impl ExecutingFrame<'_> {
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
self.frame_object(vm),
|
||||
idx as u32 * 2,
|
||||
idx as i32 * 2,
|
||||
loc.line,
|
||||
);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
@@ -3717,7 +3717,7 @@ impl ExecutingFrame<'_> {
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
frame.frame_object(vm),
|
||||
idx as u32 * 2,
|
||||
idx as i32 * 2,
|
||||
loc.line,
|
||||
);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
@@ -3808,7 +3808,7 @@ impl ExecutingFrame<'_> {
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
frame.frame_object(vm),
|
||||
idx as u32 * 2,
|
||||
idx as i32 * 2,
|
||||
loc.line,
|
||||
);
|
||||
vm_trace!(
|
||||
@@ -4017,7 +4017,7 @@ impl ExecutingFrame<'_> {
|
||||
let (loc, _end_loc) = self.code.locations[idx];
|
||||
let next = err.traceback();
|
||||
let new_traceback =
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as i32 * 2, loc.line);
|
||||
err.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
}
|
||||
|
||||
@@ -4077,7 +4077,7 @@ impl ExecutingFrame<'_> {
|
||||
let new_traceback = PyTraceback::new(
|
||||
next,
|
||||
self.frame_object(vm),
|
||||
idx as u32 * 2,
|
||||
idx as i32 * 2,
|
||||
loc.line,
|
||||
);
|
||||
err.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
@@ -4124,7 +4124,7 @@ impl ExecutingFrame<'_> {
|
||||
let (loc, _end_loc) = self.code.locations[idx];
|
||||
let next = exception.traceback();
|
||||
let new_traceback =
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line);
|
||||
PyTraceback::new(next, self.frame_object(vm), idx as i32 * 2, loc.line);
|
||||
exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx)));
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ impl PyMethodDef {
|
||||
zelf: None,
|
||||
value: self,
|
||||
module_object: None,
|
||||
module: None,
|
||||
module: crate::object::PyAtomicRef::new_empty(),
|
||||
_method_def_owner: None,
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ impl PyMethodDef {
|
||||
zelf: Some(obj),
|
||||
value: self,
|
||||
module_object: None,
|
||||
module: None,
|
||||
module: crate::object::PyAtomicRef::new_empty(),
|
||||
_method_def_owner: None,
|
||||
},
|
||||
class,
|
||||
@@ -201,7 +201,7 @@ impl PyMethodDef {
|
||||
zelf: Some(obj),
|
||||
value: self,
|
||||
module_object: None,
|
||||
module: None,
|
||||
module: crate::object::PyAtomicRef::new_empty(),
|
||||
_method_def_owner: None,
|
||||
};
|
||||
PyRef::new_ref(
|
||||
@@ -255,7 +255,7 @@ impl PyMethodDef {
|
||||
zelf: Some(class.to_owned().into()),
|
||||
value: self,
|
||||
module_object: None,
|
||||
module: None,
|
||||
module: crate::object::PyAtomicRef::new_empty(),
|
||||
_method_def_owner: None,
|
||||
};
|
||||
PyNativeMethod { func, class }.into_ref(ctx)
|
||||
|
||||
@@ -16,7 +16,7 @@ macro_rules! py_class {
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
let mut slots = $crate::types::PyTypeSlots::heap_default();
|
||||
slots.flags = $flags;
|
||||
slots.flags = $crate::types::PyAtomicTypeFlags::new($flags);
|
||||
$($crate::py_class!(@extract_slots($ctx, &mut slots, $name, $value));)*
|
||||
let py_class = $ctx.new_class(None, $class_name, $class_base, slots);
|
||||
$($crate::py_class!(@extract_attrs($ctx, &py_class, $name, $value));)*
|
||||
|
||||
@@ -514,9 +514,17 @@ impl<T> Py<T> {
|
||||
fn read_type_flags(&self) -> (crate::types::PyTypeFlags, usize) {
|
||||
let typ_ptr = self.typ.load_raw();
|
||||
let slots = unsafe { core::ptr::addr_of!((*typ_ptr).payload.slots) };
|
||||
let flags = unsafe { core::ptr::addr_of!((*slots).flags).read() };
|
||||
// SAFETY: `flags` is `PyAtomicTypeFlags`, transparent over `AtomicU64`.
|
||||
// The type object is live. This load does not form a reference to it.
|
||||
let bits = unsafe {
|
||||
(*core::ptr::addr_of!((*slots).flags).cast::<core::sync::atomic::AtomicU64>())
|
||||
.load(core::sync::atomic::Ordering::Acquire)
|
||||
};
|
||||
let member_count = unsafe { core::ptr::addr_of!((*slots).member_count).read() };
|
||||
(flags, member_count)
|
||||
(
|
||||
crate::types::PyTypeFlags::from_bits_truncate(bits),
|
||||
member_count,
|
||||
)
|
||||
}
|
||||
|
||||
/// Access the ObjExt prefix at a negative offset from this Py.
|
||||
@@ -1402,9 +1410,14 @@ impl<T: PyPayload + core::fmt::Debug> Py<T> {
|
||||
|
||||
if let Some(offset) = ext_start {
|
||||
let ext_ptr = alloc_ptr.add(offset) as *mut ObjExt;
|
||||
let flags = typ.slots.flags;
|
||||
let has_dict = flags.has_feature(crate::types::PyTypeFlags::HAS_DICT);
|
||||
let inline_values = flags.has_feature(crate::types::PyTypeFlags::INLINE_VALUES);
|
||||
let has_dict = typ
|
||||
.slots
|
||||
.flags
|
||||
.has_feature(crate::types::PyTypeFlags::HAS_DICT);
|
||||
let inline_values = typ
|
||||
.slots
|
||||
.flags
|
||||
.has_feature(crate::types::PyTypeFlags::INLINE_VALUES);
|
||||
ext_ptr.write(ObjExt::new(dict, has_dict, inline_values));
|
||||
}
|
||||
|
||||
@@ -3118,7 +3131,6 @@ pub(crate) fn init_type_hierarchy() -> BootstrapTypeHierarchy {
|
||||
slots: PyType::make_slots(),
|
||||
heaptype_ext: None,
|
||||
tp_version_tag: core::sync::atomic::AtomicU32::new(0),
|
||||
abc_tpflags: core::sync::atomic::AtomicU64::new(0),
|
||||
};
|
||||
let object_payload = PyType {
|
||||
base: unsafe { PyAtomicRef::from_optional_ref_without_retag(None) },
|
||||
@@ -3129,7 +3141,6 @@ pub(crate) fn init_type_hierarchy() -> BootstrapTypeHierarchy {
|
||||
slots: object::PyBaseObject::make_slots(),
|
||||
heaptype_ext: None,
|
||||
tp_version_tag: core::sync::atomic::AtomicU32::new(0),
|
||||
abc_tpflags: core::sync::atomic::AtomicU64::new(0),
|
||||
};
|
||||
let tuple_payload = PyType {
|
||||
base: unsafe {
|
||||
@@ -3144,7 +3155,6 @@ pub(crate) fn init_type_hierarchy() -> BootstrapTypeHierarchy {
|
||||
slots: tuple::PyTuple::make_slots(),
|
||||
heaptype_ext: None,
|
||||
tp_version_tag: core::sync::atomic::AtomicU32::new(0),
|
||||
abc_tpflags: core::sync::atomic::AtomicU64::new(0),
|
||||
};
|
||||
|
||||
let object_element =
|
||||
@@ -3192,7 +3202,6 @@ pub(crate) fn init_type_hierarchy() -> BootstrapTypeHierarchy {
|
||||
slots: PyWeak::make_slots(),
|
||||
heaptype_ext: None,
|
||||
tp_version_tag: core::sync::atomic::AtomicU32::new(0),
|
||||
abc_tpflags: core::sync::atomic::AtomicU64::new(0),
|
||||
};
|
||||
let weakref_type = PyRef::new_ref(weakref_payload, type_type.clone(), None);
|
||||
// Static type: untrack from GC (was tracked by new_ref because PyType has HAS_TRAVERSE)
|
||||
|
||||
@@ -252,6 +252,7 @@ const _: () = assert!(
|
||||
== core::mem::align_of::<PyAtomicRef<()>>()
|
||||
&& core::mem::offset_of!(PyAtomicRef<Option<PyObject>>, inner)
|
||||
== core::mem::offset_of!(PyAtomicRef<()>, inner)
|
||||
&& core::mem::offset_of!(PyAtomicRef<Option<PyObject>>, inner) == 0
|
||||
);
|
||||
|
||||
impl<T> Drop for PyAtomicRef<T> {
|
||||
@@ -367,6 +368,32 @@ impl<T: PyPayload> PyAtomicRef<T> {
|
||||
frame.iframe().cold().temporary_refs.lock().push(old.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Strong reference to the current value.
|
||||
///
|
||||
/// The cell is never null. A concurrent store may drop the previous value;
|
||||
/// the incref is retried until it applies to the pointer still in the slot.
|
||||
/// A null load is retried rather than forged.
|
||||
pub(crate) fn load_owned(&self) -> PyRef<T> {
|
||||
loop {
|
||||
if let Some(obj) = cell_load_owned(&self.inner) {
|
||||
// SAFETY: this cell is only stored with `PyRef<T>`.
|
||||
return unsafe { obj.downcast_unchecked() };
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace the stored reference. Returns the previous one, still owned.
|
||||
///
|
||||
/// `None` only if the cell was empty. Callers that publish through
|
||||
/// `From<PyRef<T>>` and `store` keep a `T` in the slot.
|
||||
pub(crate) fn store(&self, value: PyRef<T>) -> Option<PyRef<T>> {
|
||||
cell_store(&self.inner, Some(value.into())).map(|obj| {
|
||||
// SAFETY: this cell is only stored with `PyRef<T>`.
|
||||
unsafe { obj.downcast_unchecked() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyPayload> From<Option<PyRef<T>>> for PyAtomicRef<Option<T>> {
|
||||
@@ -460,13 +487,6 @@ impl<T: PyPayload> PyAtomicRef<Option<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_cell<T>() -> PyAtomicRef<T> {
|
||||
PyAtomicRef {
|
||||
inner: Radium::new(null_mut()),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn cell_load_ptr(inner: &PyAtomic<*mut u8>) -> *mut PyObject {
|
||||
inner.load(Ordering::Acquire).cast()
|
||||
}
|
||||
@@ -629,8 +649,20 @@ impl From<Option<PyObjectRef>> for PyAtomicRef<Option<PyObject>> {
|
||||
|
||||
impl PyAtomicRef<Option<PyObject>> {
|
||||
/// Empty slot. The pointer is null and owns no reference.
|
||||
pub(crate) fn new_empty() -> Self {
|
||||
empty_cell()
|
||||
pub(crate) const fn new_empty() -> Self {
|
||||
Self {
|
||||
inner: {
|
||||
#[cfg(feature = "threading")]
|
||||
{
|
||||
core::sync::atomic::AtomicPtr::new(null_mut())
|
||||
}
|
||||
#[cfg(not(feature = "threading"))]
|
||||
{
|
||||
core::cell::Cell::new(null_mut())
|
||||
}
|
||||
},
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrowed pointer currently stored. Null when the slot is empty.
|
||||
|
||||
@@ -125,6 +125,13 @@ unsafe impl<T: crate::PyPayload> Traverse for super::ext::PyAtomicRef<Option<T>>
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: crate::PyPayload> Traverse for super::ext::PyAtomicRef<T> {
|
||||
#[inline]
|
||||
fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
|
||||
traverse_fn(self.as_object());
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Traverse for super::ext::PyAtomicRef<PyObject> {
|
||||
#[inline]
|
||||
fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
|
||||
|
||||
@@ -252,9 +252,7 @@ mod _abc {
|
||||
// _abc_register propagates Py_TPFLAGS_SEQUENCE/MAPPING
|
||||
// recursively so MATCH_SEQUENCE/MATCH_MAPPING see ABC registration.
|
||||
let collection_mask = PyTypeFlags::SEQUENCE | PyTypeFlags::MAPPING;
|
||||
let collection_flags = (cls_type.slots.flags
|
||||
| PyTypeFlags::from_bits_truncate(cls_type.abc_tpflags.load(Ordering::Acquire)))
|
||||
& collection_mask;
|
||||
let collection_flags = cls_type.slots.flags.load() & collection_mask;
|
||||
if !subclass_type.is(vm.ctx.types.str_type)
|
||||
&& !subclass_type.is(vm.ctx.types.bytes_type)
|
||||
&& !subclass_type.is(vm.ctx.types.bytearray_type)
|
||||
|
||||
+134
-146
@@ -83,13 +83,8 @@ macro_rules! impl_base_node {
|
||||
|
||||
#[extend_class]
|
||||
fn extend_class(ctx: &Context, class: &'static Py<PyType>) {
|
||||
// AST types are mutable (heap types in CPython, not IMMUTABLETYPE)
|
||||
// Safety: called during type initialization before any concurrent access
|
||||
unsafe {
|
||||
let flags = &class.slots.flags as *const crate::types::PyTypeFlags
|
||||
as *mut crate::types::PyTypeFlags;
|
||||
(*flags).remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
}
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE).
|
||||
class.slots.flags.remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
class.set_attr(
|
||||
identifier!(ctx, _fields),
|
||||
ctx.empty_tuple.clone().into(),
|
||||
@@ -132,13 +127,8 @@ macro_rules! impl_base_node {
|
||||
|
||||
#[extend_class]
|
||||
fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
|
||||
// AST types are mutable (heap types in CPython, not IMMUTABLETYPE)
|
||||
// Safety: called during type initialization before any concurrent access
|
||||
unsafe {
|
||||
let flags = &class.slots.flags as *const crate::types::PyTypeFlags
|
||||
as *mut crate::types::PyTypeFlags;
|
||||
(*flags).remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
}
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE).
|
||||
class.slots.flags.remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
class.set_attr(
|
||||
identifier!(ctx, _fields),
|
||||
ctx.new_tuple(vec![
|
||||
@@ -179,30 +169,30 @@ macro_rules! impl_base_node {
|
||||
};
|
||||
}
|
||||
|
||||
#[pyclass(module = "_ast", name = "mod", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "mod", base = NodeAst)]
|
||||
pub(crate) struct NodeMod(NodeAst);
|
||||
|
||||
impl_base_node!(NodeMod);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Module", base = NodeMod)]
|
||||
#[pyclass(module = "ast", name = "Module", base = NodeMod)]
|
||||
pub(crate) struct NodeModModule,
|
||||
fields: ["body", "type_ignores"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Interactive", base = NodeMod)]
|
||||
#[pyclass(module = "ast", name = "Interactive", base = NodeMod)]
|
||||
pub(crate) struct NodeModInteractive,
|
||||
fields: ["body"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Expression", base = NodeMod)]
|
||||
#[pyclass(module = "ast", name = "Expression", base = NodeMod)]
|
||||
pub(crate) struct NodeModExpression,
|
||||
fields: ["body"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "stmt", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "stmt", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeStmt(NodeAst);
|
||||
|
||||
@@ -213,199 +203,199 @@ impl_base_node!(
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FunctionType", base = NodeMod)]
|
||||
#[pyclass(module = "ast", name = "FunctionType", base = NodeMod)]
|
||||
pub(crate) struct NodeModFunctionType,
|
||||
fields: ["argtypes", "returns"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FunctionDef", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "FunctionDef", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtFunctionDef,
|
||||
fields: ["name", "args", "body", "decorator_list", "returns", "type_comment", "type_params"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "AsyncFunctionDef", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "AsyncFunctionDef", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAsyncFunctionDef,
|
||||
fields: ["name", "args", "body", "decorator_list", "returns", "type_comment", "type_params"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "ClassDef", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "ClassDef", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtClassDef,
|
||||
fields: ["name", "bases", "keywords", "body", "decorator_list", "type_params"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Return", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Return", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtReturn,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Delete", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Delete", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtDelete,
|
||||
fields: ["targets"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Assign", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Assign", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAssign,
|
||||
fields: ["targets", "value", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TypeAlias", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "TypeAlias", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtTypeAlias,
|
||||
fields: ["name", "type_params", "value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "AugAssign", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "AugAssign", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAugAssign,
|
||||
fields: ["target", "op", "value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "AnnAssign", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "AnnAssign", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAnnAssign,
|
||||
fields: ["target", "annotation", "value", "simple"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "For", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "For", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtFor,
|
||||
fields: ["target", "iter", "body", "orelse", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "AsyncFor", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "AsyncFor", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAsyncFor,
|
||||
fields: ["target", "iter", "body", "orelse", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "While", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "While", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtWhile,
|
||||
fields: ["test", "body", "orelse"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "If", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "If", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtIf,
|
||||
fields: ["test", "body", "orelse"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "With", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "With", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtWith,
|
||||
fields: ["items", "body", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "AsyncWith", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "AsyncWith", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAsyncWith,
|
||||
fields: ["items", "body", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Match", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Match", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtMatch,
|
||||
fields: ["subject", "cases"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Raise", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Raise", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtRaise,
|
||||
fields: ["exc", "cause"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Try", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Try", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtTry,
|
||||
fields: ["body", "handlers", "orelse", "finalbody"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TryStar", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "TryStar", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtTryStar,
|
||||
fields: ["body", "handlers", "orelse", "finalbody"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Assert", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Assert", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtAssert,
|
||||
fields: ["test", "msg"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Import", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Import", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtImport,
|
||||
fields: ["names"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "ImportFrom", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "ImportFrom", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtImportFrom,
|
||||
fields: ["module", "names", "level"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Global", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Global", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtGlobal,
|
||||
fields: ["names"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Nonlocal", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Nonlocal", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtNonlocal,
|
||||
fields: ["names"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Expr", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Expr", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtExpr,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Pass", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Pass", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtPass,
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Break", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Break", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtBreak,
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "expr", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "expr", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExpr(NodeAst);
|
||||
|
||||
@@ -416,159 +406,159 @@ impl_base_node!(
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Continue", base = NodeStmt)]
|
||||
#[pyclass(module = "ast", name = "Continue", base = NodeStmt)]
|
||||
pub(crate) struct NodeStmtContinue,
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "BoolOp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "BoolOp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprBoolOp,
|
||||
fields: ["op", "values"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "NamedExpr", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "NamedExpr", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprNamedExpr,
|
||||
fields: ["target", "value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "BinOp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "BinOp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprBinOp,
|
||||
fields: ["left", "op", "right"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "UnaryOp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "UnaryOp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprUnaryOp,
|
||||
fields: ["op", "operand"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Lambda", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Lambda", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprLambda,
|
||||
fields: ["args", "body"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "IfExp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "IfExp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprIfExp,
|
||||
fields: ["test", "body", "orelse"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Dict", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Dict", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprDict,
|
||||
fields: ["keys", "values"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Set", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Set", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprSet,
|
||||
fields: ["elts"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "ListComp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "ListComp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprListComp,
|
||||
fields: ["elt", "generators"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "SetComp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "SetComp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprSetComp,
|
||||
fields: ["elt", "generators"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "DictComp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "DictComp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprDictComp,
|
||||
fields: ["key", "value", "generators"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "GeneratorExp", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "GeneratorExp", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprGeneratorExp,
|
||||
fields: ["elt", "generators"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Await", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Await", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprAwait,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Yield", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Yield", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprYield,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "YieldFrom", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "YieldFrom", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprYieldFrom,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Compare", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Compare", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprCompare,
|
||||
fields: ["left", "ops", "comparators"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Call", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Call", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprCall,
|
||||
fields: ["func", "args", "keywords"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FormattedValue", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "FormattedValue", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprFormattedValue,
|
||||
fields: ["value", "conversion", "format_spec"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "JoinedStr", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "JoinedStr", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprJoinedStr,
|
||||
fields: ["values"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TemplateStr", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "TemplateStr", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprTemplateStr,
|
||||
fields: ["values"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Interpolation", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Interpolation", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprInterpolation,
|
||||
fields: ["value", "str", "conversion", "format_spec"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "Constant", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Constant", base = NodeExpr)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExprConstant(NodeExpr);
|
||||
|
||||
@@ -576,13 +566,11 @@ pub(crate) struct NodeExprConstant(NodeExpr);
|
||||
impl NodeExprConstant {
|
||||
#[extend_class]
|
||||
fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE)
|
||||
// Safety: called during type initialization before any concurrent access
|
||||
unsafe {
|
||||
let flags = &class.slots.flags as *const crate::types::PyTypeFlags
|
||||
as *mut crate::types::PyTypeFlags;
|
||||
(*flags).remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
}
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE).
|
||||
class
|
||||
.slots
|
||||
.flags
|
||||
.remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
class.set_attr(
|
||||
identifier!(ctx, _fields),
|
||||
ctx.new_tuple(vec![
|
||||
@@ -615,245 +603,245 @@ impl NodeExprConstant {
|
||||
}
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Attribute", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Attribute", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprAttribute,
|
||||
fields: ["value", "attr", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Subscript", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Subscript", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprSubscript,
|
||||
fields: ["value", "slice", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Starred", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Starred", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprStarred,
|
||||
fields: ["value", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Name", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Name", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprName,
|
||||
fields: ["id", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "List", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "List", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprList,
|
||||
fields: ["elts", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Tuple", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Tuple", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprTuple,
|
||||
fields: ["elts", "ctx"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "expr_context", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "expr_context", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExprContext(NodeAst);
|
||||
|
||||
impl_base_node!(NodeExprContext);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Slice", base = NodeExpr)]
|
||||
#[pyclass(module = "ast", name = "Slice", base = NodeExpr)]
|
||||
pub(crate) struct NodeExprSlice,
|
||||
fields: ["lower", "upper", "step"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Load", base = NodeExprContext)]
|
||||
#[pyclass(module = "ast", name = "Load", base = NodeExprContext)]
|
||||
pub(crate) struct NodeExprContextLoad,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Store", base = NodeExprContext)]
|
||||
#[pyclass(module = "ast", name = "Store", base = NodeExprContext)]
|
||||
pub(crate) struct NodeExprContextStore,
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "boolop", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "boolop", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeBoolOp(NodeAst);
|
||||
|
||||
impl_base_node!(NodeBoolOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Del", base = NodeExprContext)]
|
||||
#[pyclass(module = "ast", name = "Del", base = NodeExprContext)]
|
||||
pub(crate) struct NodeExprContextDel,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "And", base = NodeBoolOp)]
|
||||
#[pyclass(module = "ast", name = "And", base = NodeBoolOp)]
|
||||
pub(crate) struct NodeBoolOpAnd,
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "operator", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "operator", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeOperator(NodeAst);
|
||||
|
||||
impl_base_node!(NodeOperator);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Or", base = NodeBoolOp)]
|
||||
#[pyclass(module = "ast", name = "Or", base = NodeBoolOp)]
|
||||
pub(crate) struct NodeBoolOpOr,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Add", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Add", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorAdd,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Sub", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Sub", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorSub,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Mult", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Mult", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorMult,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatMult", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "MatMult", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorMatMult,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Div", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Div", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorDiv,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Mod", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Mod", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorMod,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Pow", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "Pow", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorPow,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "LShift", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "LShift", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorLShift,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "RShift", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "RShift", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorRShift,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "BitOr", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "BitOr", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorBitOr,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "BitXor", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "BitXor", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorBitXor,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "BitAnd", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "BitAnd", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorBitAnd,
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "unaryop", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "unaryop", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeUnaryOp(NodeAst);
|
||||
|
||||
impl_base_node!(NodeUnaryOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FloorDiv", base = NodeOperator)]
|
||||
#[pyclass(module = "ast", name = "FloorDiv", base = NodeOperator)]
|
||||
pub(crate) struct NodeOperatorFloorDiv,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Invert", base = NodeUnaryOp)]
|
||||
#[pyclass(module = "ast", name = "Invert", base = NodeUnaryOp)]
|
||||
pub(crate) struct NodeUnaryOpInvert,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Not", base = NodeUnaryOp)]
|
||||
#[pyclass(module = "ast", name = "Not", base = NodeUnaryOp)]
|
||||
pub(crate) struct NodeUnaryOpNot,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "UAdd", base = NodeUnaryOp)]
|
||||
#[pyclass(module = "ast", name = "UAdd", base = NodeUnaryOp)]
|
||||
pub(crate) struct NodeUnaryOpUAdd,
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "cmpop", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "cmpop", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeCmpOp(NodeAst);
|
||||
|
||||
impl_base_node!(NodeCmpOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "USub", base = NodeUnaryOp)]
|
||||
#[pyclass(module = "ast", name = "USub", base = NodeUnaryOp)]
|
||||
pub(crate) struct NodeUnaryOpUSub,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Eq", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "Eq", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpEq,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "NotEq", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "NotEq", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpNotEq,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Lt", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "Lt", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpLt,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "LtE", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "LtE", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpLtE,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Gt", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "Gt", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpGt,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "GtE", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "GtE", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpGtE,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Is", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "Is", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpIs,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "IsNot", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "IsNot", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpIsNot,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "In", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "In", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpIn,
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "NotIn", base = NodeCmpOp)]
|
||||
#[pyclass(module = "ast", name = "NotIn", base = NodeCmpOp)]
|
||||
pub(crate) struct NodeCmpOpNotIn,
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "excepthandler", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "excepthandler", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExceptHandler(NodeAst);
|
||||
|
||||
@@ -864,52 +852,52 @@ impl_base_node!(
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "comprehension", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "comprehension", base = NodeAst)]
|
||||
pub(crate) struct NodeComprehension,
|
||||
fields: ["target", "iter", "ifs", "is_async"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "ExceptHandler", base = NodeExceptHandler)]
|
||||
#[pyclass(module = "ast", name = "ExceptHandler", base = NodeExceptHandler)]
|
||||
pub(crate) struct NodeExceptHandlerExceptHandler,
|
||||
fields: ["type", "name", "body"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "arguments", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "arguments", base = NodeAst)]
|
||||
pub(crate) struct NodeArguments,
|
||||
fields: ["posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "arg", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "arg", base = NodeAst)]
|
||||
pub(crate) struct NodeArg,
|
||||
fields: ["arg", "annotation", "type_comment"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "keyword", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "keyword", base = NodeAst)]
|
||||
pub(crate) struct NodeKeyword,
|
||||
fields: ["arg", "value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "alias", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "alias", base = NodeAst)]
|
||||
pub(crate) struct NodeAlias,
|
||||
fields: ["name", "asname"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "withitem", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "withitem", base = NodeAst)]
|
||||
pub(crate) struct NodeWithItem,
|
||||
fields: ["context_expr", "optional_vars"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "pattern", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "pattern", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodePattern(NodeAst);
|
||||
|
||||
@@ -919,74 +907,74 @@ impl_base_node!(
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "match_case", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "match_case", base = NodeAst)]
|
||||
pub(crate) struct NodeMatchCase,
|
||||
fields: ["pattern", "guard", "body"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchValue", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchValue", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchValue,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchSingleton", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchSingleton", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchSingleton,
|
||||
fields: ["value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchSequence", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchSequence", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchSequence,
|
||||
fields: ["patterns"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchMapping", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchMapping", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchMapping,
|
||||
fields: ["keys", "patterns", "rest"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchClass", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchClass", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchClass,
|
||||
fields: ["cls", "patterns", "kwd_attrs", "kwd_patterns"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchStar", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchStar", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchStar,
|
||||
fields: ["name"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchAs", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchAs", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchAs,
|
||||
fields: ["pattern", "name"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "type_ignore", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "type_ignore", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeTypeIgnore(NodeAst);
|
||||
|
||||
impl_base_node!(NodeTypeIgnore);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchOr", base = NodePattern)]
|
||||
#[pyclass(module = "ast", name = "MatchOr", base = NodePattern)]
|
||||
pub(crate) struct NodePatternMatchOr,
|
||||
fields: ["patterns"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
#[pyclass(module = "_ast", name = "type_param", base = NodeAst)]
|
||||
#[pyclass(module = "ast", name = "type_param", base = NodeAst)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeTypeParam(NodeAst);
|
||||
|
||||
@@ -996,27 +984,27 @@ impl_base_node!(
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TypeIgnore", base = NodeTypeIgnore)]
|
||||
#[pyclass(module = "ast", name = "TypeIgnore", base = NodeTypeIgnore)]
|
||||
pub(crate) struct NodeTypeIgnoreTypeIgnore,
|
||||
fields: ["lineno", "tag"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TypeVar", base = NodeTypeParam)]
|
||||
#[pyclass(module = "ast", name = "TypeVar", base = NodeTypeParam)]
|
||||
pub(crate) struct NodeTypeParamTypeVar,
|
||||
fields: ["name", "bound", "default_value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "ParamSpec", base = NodeTypeParam)]
|
||||
#[pyclass(module = "ast", name = "ParamSpec", base = NodeTypeParam)]
|
||||
pub(crate) struct NodeTypeParamParamSpec,
|
||||
fields: ["name", "default_value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TypeVarTuple", base = NodeTypeParam)]
|
||||
#[pyclass(module = "ast", name = "TypeVarTuple", base = NodeTypeParam)]
|
||||
pub(crate) struct NodeTypeParamTypeVarTuple,
|
||||
fields: ["name", "default_value"],
|
||||
attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"],
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) mod _ast {
|
||||
warn,
|
||||
};
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_ast", name = "AST")]
|
||||
#[pyclass(module = "ast", name = "AST")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct NodeAst;
|
||||
|
||||
@@ -24,13 +24,11 @@ pub(crate) mod _ast {
|
||||
impl NodeAst {
|
||||
#[extend_class]
|
||||
fn extend_class(ctx: &Context, class: &'static Py<PyType>) {
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE)
|
||||
// Safety: called during type initialization before any concurrent access
|
||||
unsafe {
|
||||
let flags = &class.slots.flags as *const crate::types::PyTypeFlags
|
||||
as *mut crate::types::PyTypeFlags;
|
||||
(*flags).remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
}
|
||||
// AST types are mutable (heap types, not IMMUTABLETYPE).
|
||||
class
|
||||
.slots
|
||||
.flags
|
||||
.remove(crate::types::PyTypeFlags::IMMUTABLETYPE);
|
||||
let empty_tuple = ctx.empty_tuple.clone();
|
||||
class.set_str_attr("_fields", empty_tuple.clone(), ctx);
|
||||
class.set_str_attr("_attributes", empty_tuple.clone(), ctx);
|
||||
|
||||
@@ -312,9 +312,9 @@ pub(crate) mod _ctypes {
|
||||
// Create slots with IMMUTABLETYPE flag
|
||||
let slots = PyTypeSlots {
|
||||
name: "COMError",
|
||||
flags: PyTypeFlags::heap_type_flags()
|
||||
| PyTypeFlags::HAS_DICT
|
||||
| PyTypeFlags::IMMUTABLETYPE,
|
||||
flags: crate::types::PyAtomicTypeFlags::new(
|
||||
PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT | PyTypeFlags::IMMUTABLETYPE,
|
||||
),
|
||||
..PyTypeSlots::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -2004,7 +2004,17 @@ impl PyCThunk {
|
||||
unsafe impl Send for PyCThunk {}
|
||||
unsafe impl Sync for PyCThunk {}
|
||||
|
||||
#[pyclass]
|
||||
/// `ffi_type`: `size_t` size, two `unsigned short`s, then a pointer.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
struct FfiTypeLayout {
|
||||
_size: usize,
|
||||
_alignment: u16,
|
||||
_type_code: u16,
|
||||
_elements: *mut *mut u8,
|
||||
}
|
||||
|
||||
#[pyclass(itemsize = core::mem::size_of::<FfiTypeLayout>())]
|
||||
impl PyCThunk {
|
||||
#[pygetset]
|
||||
fn callable(&self) -> PyObjectRef {
|
||||
|
||||
@@ -3,7 +3,7 @@ pub(crate) use _functools::module_def;
|
||||
#[pymodule]
|
||||
mod _functools {
|
||||
use crate::{
|
||||
Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{
|
||||
PyBoundMethod, PyDict, PyDictRef, PyGenericAlias, PyTuple, PyType, PyTypeRef, object,
|
||||
},
|
||||
@@ -11,13 +11,13 @@ mod _functools {
|
||||
function::{
|
||||
Either, FuncArgs, KwArgs, OptionalOption, PosArgs, PyComparisonValue, PySetterValue,
|
||||
},
|
||||
object::{AsObject, TraverseFn},
|
||||
object::{AsObject, PyAtomicRef, TraverseFn},
|
||||
protocol::PyIter,
|
||||
pyclass,
|
||||
recursion::ReprGuard,
|
||||
types::{Callable, Constructor, GetDescriptor, PyComparisonOp, Representable},
|
||||
};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use parking_lot::lock_api::RawReentrantMutex as GenericRawReentrantMutex;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
@@ -225,34 +225,27 @@ mod _functools {
|
||||
#[pyclass(name = "partial", module = "functools", traverse = "manual")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(super) struct PyPartial {
|
||||
inner: PyRwLock<PyPartialInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PyPartialInner {
|
||||
func: PyObjectRef,
|
||||
args: PyRef<PyTuple>,
|
||||
keywords: PyRef<PyDict>,
|
||||
phcount: usize,
|
||||
#[pymember]
|
||||
func: PyAtomicRef<PyObject>,
|
||||
#[pymember]
|
||||
args: PyAtomicRef<PyTuple>,
|
||||
#[pymember]
|
||||
keywords: PyAtomicRef<PyDict>,
|
||||
phcount: AtomicUsize,
|
||||
}
|
||||
|
||||
// SAFETY: Each owned reference is visited once, without cloning it.
|
||||
unsafe impl crate::object::Traverse for PyPartial {
|
||||
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
|
||||
if let Some(inner) = self.inner.try_read_recursive() {
|
||||
inner.func.traverse(tracer_fn);
|
||||
inner.args.traverse(tracer_fn);
|
||||
inner.keywords.traverse(tracer_fn);
|
||||
}
|
||||
self.func.traverse(tracer_fn);
|
||||
self.args.traverse(tracer_fn);
|
||||
self.keywords.traverse(tracer_fn);
|
||||
}
|
||||
|
||||
fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
|
||||
// __setstate__ can make func point directly back to this partial.
|
||||
// The args tuple and keywords dict have their own cycle clearing.
|
||||
out.push(core::mem::replace(
|
||||
&mut self.inner.get_mut().func,
|
||||
Context::genesis().none(),
|
||||
));
|
||||
out.push(self.func.store(Context::genesis().none()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,21 +254,6 @@ mod _functools {
|
||||
flags(BASETYPE, HAS_DICT, HAS_WEAKREF)
|
||||
)]
|
||||
impl PyPartial {
|
||||
#[pygetset]
|
||||
fn func(&self) -> PyObjectRef {
|
||||
self.inner.read().func.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn args(&self) -> PyRef<PyTuple> {
|
||||
self.inner.read().args.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn keywords(&self) -> PyRef<PyDict> {
|
||||
self.inner.read().keywords.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __dict__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyDictRef {
|
||||
zelf.as_object()
|
||||
@@ -294,7 +272,9 @@ mod _functools {
|
||||
|
||||
#[pymethod]
|
||||
fn __reduce__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let inner = zelf.inner.read();
|
||||
let func = zelf.func.load_owned();
|
||||
let args = zelf.args.load_owned();
|
||||
let keywords = zelf.keywords.load_owned();
|
||||
let partial_type = zelf.class();
|
||||
|
||||
// Get __dict__ if it exists and is not empty
|
||||
@@ -303,16 +283,13 @@ mod _functools {
|
||||
_ => vm.ctx.none(),
|
||||
};
|
||||
|
||||
let state = vm.ctx.new_tuple(vec![
|
||||
inner.func.clone(),
|
||||
inner.args.clone().into(),
|
||||
inner.keywords.clone().into(),
|
||||
dict_obj,
|
||||
]);
|
||||
let state =
|
||||
vm.ctx
|
||||
.new_tuple(vec![func.clone(), args.into(), keywords.into(), dict_obj]);
|
||||
vm.ctx
|
||||
.new_tuple(vec![
|
||||
partial_type.to_owned().into(),
|
||||
vm.ctx.new_tuple(vec![inner.func.clone()]).into(),
|
||||
vm.ctx.new_tuple(vec![func]).into(),
|
||||
state.into(),
|
||||
])
|
||||
.into()
|
||||
@@ -383,19 +360,10 @@ mod _functools {
|
||||
}
|
||||
let phcount = count_placeholders(args_slice);
|
||||
|
||||
// Actually update the state
|
||||
let old_inner = core::mem::replace(
|
||||
&mut *zelf.inner.write(),
|
||||
PyPartialInner {
|
||||
func: func.clone(),
|
||||
// Handle args - use the already validated tuple
|
||||
args: args_tuple,
|
||||
// Handle keywords - keep the original type
|
||||
keywords: keywords_dict,
|
||||
phcount,
|
||||
},
|
||||
);
|
||||
drop(old_inner);
|
||||
zelf.func.store(func.clone());
|
||||
zelf.args.store(args_tuple);
|
||||
zelf.keywords.store(keywords_dict);
|
||||
zelf.phcount.store(phcount, Ordering::Relaxed);
|
||||
|
||||
// Update __dict__ if provided
|
||||
let Some(instance_dict) = zelf.as_object().dict() else {
|
||||
@@ -489,8 +457,10 @@ mod _functools {
|
||||
// Handle nested partial objects
|
||||
let (final_func, final_args, final_keywords) =
|
||||
if let Some(partial) = func.downcast_ref::<Self>() {
|
||||
let inner = partial.inner.read();
|
||||
let stored_args = inner.args.as_slice();
|
||||
let stored_args_obj = partial.args.load_owned();
|
||||
let stored_func = partial.func.load_owned();
|
||||
let stored_keywords = partial.keywords.load_owned();
|
||||
let stored_args = stored_args_obj.as_slice();
|
||||
|
||||
// Merge placeholders: replace placeholders in stored_args with new args
|
||||
let mut merged_args = Vec::with_capacity(stored_args.len() + args_slice.len());
|
||||
@@ -511,7 +481,7 @@ mod _functools {
|
||||
// Append remaining new args
|
||||
merged_args.extend(new_args_iter.cloned());
|
||||
|
||||
(inner.func.clone(), merged_args, inner.keywords.clone())
|
||||
(stored_func, merged_args, stored_keywords)
|
||||
} else {
|
||||
(func.clone(), args_slice.to_vec(), vm.ctx.new_dict())
|
||||
};
|
||||
@@ -529,12 +499,10 @@ mod _functools {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: PyRwLock::new(PyPartialInner {
|
||||
func: final_func,
|
||||
args: vm.ctx.new_tuple(final_args),
|
||||
keywords: final_keywords,
|
||||
phcount,
|
||||
}),
|
||||
func: PyAtomicRef::from(final_func),
|
||||
args: PyAtomicRef::from(vm.ctx.new_tuple(final_args)),
|
||||
keywords: PyAtomicRef::from(final_keywords),
|
||||
phcount: AtomicUsize::new(phcount),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -544,15 +512,10 @@ mod _functools {
|
||||
|
||||
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
// Clone and release lock before calling Python code to prevent deadlock
|
||||
let (func, stored_args, keywords, phcount) = {
|
||||
let inner = zelf.inner.read();
|
||||
(
|
||||
inner.func.clone(),
|
||||
inner.args.clone(),
|
||||
inner.keywords.clone(),
|
||||
inner.phcount,
|
||||
)
|
||||
};
|
||||
let func = zelf.func.load_owned();
|
||||
let stored_args = zelf.args.load_owned();
|
||||
let keywords = zelf.keywords.load_owned();
|
||||
let phcount = zelf.phcount.load(Ordering::Relaxed);
|
||||
|
||||
// Check if we have enough args to fill placeholders
|
||||
if phcount > 0 && args.args.len() < phcount {
|
||||
@@ -629,14 +592,9 @@ mod _functools {
|
||||
let obj = zelf.as_object();
|
||||
if let Some(_guard) = ReprGuard::enter(vm, obj) {
|
||||
// Clone and release lock before calling Python code to prevent deadlock
|
||||
let (func, args, keywords) = {
|
||||
let inner = zelf.inner.read();
|
||||
(
|
||||
inner.func.clone(),
|
||||
inner.args.clone(),
|
||||
inner.keywords.clone(),
|
||||
)
|
||||
};
|
||||
let func = zelf.func.load_owned();
|
||||
let args = zelf.args.load_owned();
|
||||
let keywords = zelf.keywords.load_owned();
|
||||
|
||||
let qualname = zelf.class().__qualname__(vm);
|
||||
let qualname_wtf8 = qualname
|
||||
@@ -645,7 +603,7 @@ mod _functools {
|
||||
|| Wtf8Buf::from(zelf.class().name().to_owned()),
|
||||
|s| s.as_wtf8().to_owned(),
|
||||
);
|
||||
let module = zelf.class().__module__(vm);
|
||||
let module = zelf.class().__module__(vm)?;
|
||||
|
||||
let mut result = Wtf8Buf::new();
|
||||
if let Ok(module_str) = module.downcast::<crate::builtins::PyStr>() {
|
||||
|
||||
+377
-235
File diff suppressed because it is too large.
Load diff
@@ -263,7 +263,15 @@ mod _sre {
|
||||
items: Vec<(usize, PyObjectRef)>,
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
/// One `TemplateObject.items` element: `Py_ssize_t` index, object pointer.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
struct SreTemplateItem {
|
||||
_index: isize,
|
||||
_literal: *mut PyObject,
|
||||
}
|
||||
|
||||
#[pyclass(itemsize = core::mem::size_of::<SreTemplateItem>())]
|
||||
impl Template {
|
||||
fn compile(
|
||||
pattern: PyRef<Pattern>,
|
||||
@@ -378,7 +386,11 @@ mod _sre {
|
||||
}};
|
||||
}
|
||||
|
||||
#[pyclass(with(Hashable, Comparable, Representable), flags(HAS_WEAKREF))]
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<u32>(),
|
||||
with(Hashable, Comparable, Representable),
|
||||
flags(HAS_WEAKREF)
|
||||
)]
|
||||
impl Pattern {
|
||||
fn downcast_str<'a>(string: &'a PyObject, vm: &VirtualMachine) -> PyResult<&'a Py<PyStr>> {
|
||||
string.downcast_ref::<PyStr>().ok_or_else(|| {
|
||||
@@ -817,7 +829,11 @@ mod _sre {
|
||||
regs: Vec<(isize, isize)>,
|
||||
}
|
||||
|
||||
#[pyclass(with(AsMapping, Representable), flags(DISALLOW_INSTANTIATION))]
|
||||
#[pyclass(
|
||||
itemsize = core::mem::size_of::<isize>(),
|
||||
with(AsMapping, Representable),
|
||||
flags(DISALLOW_INSTANTIATION)
|
||||
)]
|
||||
impl Match {
|
||||
pub(crate) fn new(state: &mut State, pattern: PyRef<Pattern>, string: PyObjectRef) -> Self {
|
||||
let string_position = state.cursor.position;
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) mod _thread {
|
||||
OptionalArg, Param, ParamKind, PosArgs, PySetterValue, TimeoutSeconds,
|
||||
},
|
||||
object::{Traverse, TraverseFn},
|
||||
types::{Constructor, GetAttr, Representable, SetAttr},
|
||||
types::{Constructor, GetAttr, PyStructSequence, Representable, SetAttr},
|
||||
};
|
||||
|
||||
use alloc::sync::{Arc, Weak};
|
||||
@@ -1000,81 +1000,69 @@ pub(crate) mod _thread {
|
||||
}
|
||||
|
||||
// This allows threading.py to import _excepthook and _ExceptHookArgs from _thread
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_thread", name = "_ExceptHookArgs")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct ExceptHookArgs {
|
||||
#[pystruct_sequence_data]
|
||||
struct ExceptHookArgsData {
|
||||
exc_type: crate::PyObjectRef,
|
||||
exc_value: crate::PyObjectRef,
|
||||
exc_traceback: crate::PyObjectRef,
|
||||
thread: crate::PyObjectRef,
|
||||
}
|
||||
|
||||
#[pyclass(with(Constructor))]
|
||||
impl ExceptHookArgs {
|
||||
#[pygetset]
|
||||
fn exc_type(&self) -> crate::PyObjectRef {
|
||||
self.exc_type.clone()
|
||||
}
|
||||
#[pyattr]
|
||||
#[pystruct_sequence(
|
||||
name = "_ExceptHookArgs",
|
||||
module = "_thread",
|
||||
data = "ExceptHookArgsData"
|
||||
)]
|
||||
struct PyExceptHookArgs;
|
||||
|
||||
#[pygetset]
|
||||
fn exc_value(&self) -> crate::PyObjectRef {
|
||||
self.exc_value.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn exc_traceback(&self) -> crate::PyObjectRef {
|
||||
self.exc_traceback.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn thread(&self) -> crate::PyObjectRef {
|
||||
self.thread.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct ExceptHookNewArgs {
|
||||
#[pyarg(positional, default, py_default = "()")]
|
||||
iterable: crate::function::OptionalArg<crate::PyObjectRef>,
|
||||
}
|
||||
|
||||
impl Constructor for ExceptHookArgs {
|
||||
// Takes a single iterable argument like namedtuple
|
||||
type Args = ExceptHookNewArgs;
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
// Convert the argument to a list/tuple and extract elements
|
||||
let seq: Vec<crate::PyObjectRef> = match args.iterable {
|
||||
crate::function::OptionalArg::Present(iterable) => iterable.try_to_value(vm)?,
|
||||
crate::function::OptionalArg::Missing => Vec::new(),
|
||||
};
|
||||
if seq.len() != 4 {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"_ExceptHookArgs expected 4 arguments, got {}",
|
||||
seq.len()
|
||||
)));
|
||||
}
|
||||
Ok(Self {
|
||||
exc_type: seq[0].clone(),
|
||||
exc_value: seq[1].clone(),
|
||||
exc_traceback: seq[2].clone(),
|
||||
thread: seq[3].clone(),
|
||||
})
|
||||
#[pyclass(with(PyStructSequence))]
|
||||
impl PyExceptHookArgs {
|
||||
#[extend_class]
|
||||
fn extend_pyclass(ctx: &crate::vm::Context, class: &'static Py<crate::builtins::PyType>) {
|
||||
// (type, (sequence, dict)). Set before the trait installs its default.
|
||||
const EXCEPT_HOOK_ARGS_REDUCE: crate::function::PyMethodDef =
|
||||
crate::function::PyMethodDef::new_const(
|
||||
"__reduce__",
|
||||
|zelf: crate::PyRef<crate::builtins::PyTuple>,
|
||||
vm: &VirtualMachine|
|
||||
-> PyTupleRef {
|
||||
vm.new_tuple((
|
||||
zelf.class().to_owned(),
|
||||
(
|
||||
vm.ctx.new_tuple(zelf.as_slice().to_vec()),
|
||||
vm.ctx.new_dict(),
|
||||
),
|
||||
))
|
||||
},
|
||||
crate::function::PyMethodFlags::METHOD,
|
||||
crate::function::ItemDoc::NONE,
|
||||
);
|
||||
class.set_attr(
|
||||
ctx.intern_str("__reduce__"),
|
||||
EXCEPT_HOOK_ARGS_REDUCE.to_proper_method(class, ctx),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn _excepthook(args: crate::PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// Type check: args must be _ExceptHookArgs
|
||||
let args = args.downcast::<ExceptHookArgs>().map_err(|_| {
|
||||
vm.new_type_error("_thread._excepthook argument type must be _ExceptHookArgs")
|
||||
let args = args.downcast::<PyExceptHookArgs>().map_err(|_| {
|
||||
vm.new_type_error("_thread.excepthook argument type must be ExceptHookArgs")
|
||||
})?;
|
||||
|
||||
let exc_type = args.exc_type.clone();
|
||||
let exc_value = args.exc_value.clone();
|
||||
let exc_traceback = args.exc_traceback.clone();
|
||||
let thread = args.thread.clone();
|
||||
let fields = args.0.as_slice();
|
||||
let (Some(exc_type), Some(exc_value), Some(exc_traceback), Some(thread)) =
|
||||
(fields.first(), fields.get(1), fields.get(2), fields.get(3))
|
||||
else {
|
||||
return Err(
|
||||
vm.new_type_error("_thread.excepthook argument type must be ExceptHookArgs")
|
||||
);
|
||||
};
|
||||
let exc_type = exc_type.clone();
|
||||
let exc_value = exc_value.clone();
|
||||
let exc_traceback = exc_traceback.clone();
|
||||
let thread = thread.clone();
|
||||
|
||||
// Silently ignore SystemExit (identity check)
|
||||
if exc_type.is(vm.ctx.exceptions.system_exit.as_ref()) {
|
||||
|
||||
@@ -1187,7 +1187,7 @@ pub mod sys {
|
||||
);
|
||||
|
||||
// Print module name (if not builtins or __main__)
|
||||
let module_name = unraisable.exc_type.__module__(vm);
|
||||
let module_name = unraisable.exc_type.__module__(vm)?;
|
||||
if let Ok(module_str) = module_name.downcast::<PyStr>() {
|
||||
let module = module_str.as_wtf8();
|
||||
if module != "builtins" && module != "__main__" {
|
||||
|
||||
+107
-3
@@ -134,6 +134,8 @@ pub struct PyTypeSlots {
|
||||
/// For heap types, `__name__` must alive
|
||||
pub(crate) name: &'static str, // tp_name with <module>.<class> for print, not class name
|
||||
|
||||
/// Full `tp_basicsize`: object header plus payload. `0` before type
|
||||
/// creation means "inherit the base size".
|
||||
pub basicsize: usize,
|
||||
pub itemsize: usize, // tp_itemsize
|
||||
|
||||
@@ -171,8 +173,9 @@ pub struct PyTypeSlots {
|
||||
|
||||
pub methods: &'static [PyMethodDef],
|
||||
|
||||
// Flags to define presence of optional/expanded features
|
||||
pub flags: PyTypeFlags,
|
||||
// Flags to define presence of optional/expanded features.
|
||||
// One atomic word: runtime code sets and clears bits in place.
|
||||
pub flags: PyAtomicTypeFlags,
|
||||
|
||||
// tp_doc
|
||||
pub doc: ItemDoc,
|
||||
@@ -215,7 +218,7 @@ impl PyTypeSlots {
|
||||
pub fn new(name: &'static str, flags: PyTypeFlags) -> Self {
|
||||
Self {
|
||||
name,
|
||||
flags,
|
||||
flags: PyAtomicTypeFlags::new(flags),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -300,6 +303,107 @@ impl Default for PyTypeFlags {
|
||||
}
|
||||
}
|
||||
|
||||
/// `tp_flags` as one atomic word. Reads match [`PyTypeFlags`]; bits that change
|
||||
/// after the type is published are set and cleared in place.
|
||||
#[repr(transparent)]
|
||||
pub struct PyAtomicTypeFlags(core::sync::atomic::AtomicU64);
|
||||
|
||||
impl core::fmt::Debug for PyAtomicTypeFlags {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.load().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PyAtomicTypeFlags {
|
||||
fn default() -> Self {
|
||||
Self::new(PyTypeFlags::DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyAtomicTypeFlags {
|
||||
#[must_use]
|
||||
pub const fn new(flags: PyTypeFlags) -> Self {
|
||||
Self(core::sync::atomic::AtomicU64::new(flags.bits()))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn load(&self) -> PyTypeFlags {
|
||||
PyTypeFlags::from_bits_truncate(self.bits())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn bits(&self) -> u64 {
|
||||
self.0.load(core::sync::atomic::Ordering::Acquire)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn has_feature(&self, flag: PyTypeFlags) -> bool {
|
||||
self.load().has_feature(flag)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn contains(&self, flag: PyTypeFlags) -> bool {
|
||||
self.load().contains(flag)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn intersects(&self, flag: PyTypeFlags) -> bool {
|
||||
self.load().intersects(flag)
|
||||
}
|
||||
|
||||
pub fn remove(&self, flag: PyTypeFlags) {
|
||||
self.0
|
||||
.fetch_and(!flag.bits(), core::sync::atomic::Ordering::AcqRel);
|
||||
}
|
||||
|
||||
pub fn set(&self, flag: PyTypeFlags) {
|
||||
self.0
|
||||
.fetch_or(flag.bits(), core::sync::atomic::Ordering::AcqRel);
|
||||
}
|
||||
|
||||
/// Replace `mask` bits with `value & mask`.
|
||||
pub fn replace_masked(&self, mask: PyTypeFlags, value: PyTypeFlags) {
|
||||
let mask_bits = mask.bits();
|
||||
let value_bits = (value & mask).bits();
|
||||
let _ = self.0.fetch_update(
|
||||
core::sync::atomic::Ordering::AcqRel,
|
||||
core::sync::atomic::Ordering::Acquire,
|
||||
|old| Some((old & !mask_bits) | value_bits),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[must_use]
|
||||
pub fn is_created_with_flags(&self) -> bool {
|
||||
self.load().is_created_with_flags()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOrAssign<PyTypeFlags> for PyAtomicTypeFlags {
|
||||
fn bitor_assign(&mut self, rhs: PyTypeFlags) {
|
||||
self.set(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = assert!(
|
||||
core::mem::size_of::<PyAtomicTypeFlags>()
|
||||
== core::mem::size_of::<core::sync::atomic::AtomicU64>()
|
||||
&& core::mem::align_of::<PyAtomicTypeFlags>()
|
||||
== core::mem::align_of::<core::sync::atomic::AtomicU64>()
|
||||
&& core::mem::size_of::<core::sync::atomic::AtomicU64>() == core::mem::size_of::<u64>()
|
||||
&& core::mem::align_of::<core::sync::atomic::AtomicU64>() == core::mem::align_of::<u64>()
|
||||
);
|
||||
|
||||
impl crate::builtins::descriptor::MemberLayout for PyAtomicTypeFlags {
|
||||
const KIND: crate::builtins::descriptor::MemberKind = {
|
||||
if core::mem::size_of::<core::ffi::c_ulong>() == 8 {
|
||||
crate::builtins::descriptor::MemberKind::ULong
|
||||
} else {
|
||||
crate::builtins::descriptor::MemberKind::ULongLong
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult;
|
||||
/// Vectorcall function pointer (PEP 590).
|
||||
/// args: owned positional args followed by kwarg values.
|
||||
|
||||
@@ -622,7 +622,9 @@ impl Context {
|
||||
let slots = PyTypeSlots {
|
||||
name: interned_name.as_str(),
|
||||
basicsize: 0,
|
||||
flags: PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT,
|
||||
flags: crate::types::PyAtomicTypeFlags::new(
|
||||
PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT,
|
||||
),
|
||||
..PyTypeSlots::default()
|
||||
};
|
||||
PyType::new_heap(
|
||||
|
||||
@@ -434,7 +434,7 @@ impl VirtualMachine {
|
||||
) -> PyBaseExceptionRef {
|
||||
debug_assert_eq!(
|
||||
exc_type.slots.basicsize,
|
||||
core::mem::size_of::<PyBaseException>(),
|
||||
crate::object::SIZEOF_PYOBJECT_HEAD + core::mem::size_of::<PyBaseException>(),
|
||||
"vm.new_simple_exception() requires a BaseException-sized type, got {}",
|
||||
exc_type.name()
|
||||
);
|
||||
@@ -452,7 +452,9 @@ impl VirtualMachine {
|
||||
/// [`invoke_exception`][Self::invoke_exception] or
|
||||
/// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor].
|
||||
pub fn new_exception(&self, exc_type: PyTypeRef, args: Vec<PyObjectRef>) -> PyBaseExceptionRef {
|
||||
if exc_type.slots.basicsize == core::mem::size_of::<PyBaseException>() {
|
||||
if exc_type.slots.basicsize
|
||||
== crate::object::SIZEOF_PYOBJECT_HEAD + core::mem::size_of::<PyBaseException>()
|
||||
{
|
||||
self.new_simple_exception(exc_type, args)
|
||||
} else {
|
||||
self.invoke_exception(&exc_type, args).unwrap_or_else(|e| e)
|
||||
@@ -468,7 +470,7 @@ impl VirtualMachine {
|
||||
{
|
||||
debug_assert_eq!(
|
||||
cls.slots.basicsize,
|
||||
size_of::<T>(),
|
||||
crate::object::SIZEOF_PYOBJECT_HEAD + size_of::<T>(),
|
||||
"vm.new_payload_exception::<{}>() called with mismatched type '{}'",
|
||||
core::any::type_name::<T>(),
|
||||
cls.name()
|
||||
@@ -494,7 +496,10 @@ impl VirtualMachine {
|
||||
errno: Option<i32>,
|
||||
msg: impl ToPyObject,
|
||||
) -> PyRef<PyOSError> {
|
||||
debug_assert_eq!(exc_type.slots.basicsize, core::mem::size_of::<PyOSError>());
|
||||
debug_assert_eq!(
|
||||
exc_type.slots.basicsize,
|
||||
crate::object::SIZEOF_PYOBJECT_HEAD + core::mem::size_of::<PyOSError>()
|
||||
);
|
||||
|
||||
OSErrorBuilder::with_subtype(exc_type, errno, msg, self).build(self)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user