Skip to content

Commit 04cf5da

Browse files
authored
Object header slimming: prefix allocation for ObjExt (RustPython#7334)
* Object header slimming: prefix allocation for ObjExt Extract dict, weak_list, and slots fields from PyInner<T> into a separate ObjExt struct allocated as a prefix before PyInner using Layout::extend(). Objects that don't need these fields (int, str, float, list, tuple, dict, etc.) skip the prefix entirely. - Add HAS_WEAKREF flag to PyTypeFlags for per-type weakref control - Add HAS_EXT bit to GcBits indicating prefix presence - Define ObjExt struct with dict, weak_list, slots - Shrink PyInner header from ~80-88 bytes to ~32 bytes for lightweight objects - Update all accessor methods to go through ext_ref() - Update bootstrap type hierarchy to use prefix allocation - Add __weakref__ getset descriptor for heap types - Set HAS_WEAKREF on builtin types that support weak references - Remove test_weak_keyed_bad_delitem expectedFailure (now passes) * Add HAS_WEAKREF to _asyncio Future/Task, rename weakref helpers - Add HAS_WEAKREF flag to PyFuture and PyTask (matches CPython) - Rename subtype_getweakref/setweakref to subtype_get_weakref/set_weakref to fix cspell unknown word lint * Add HAS_WEAKREF to array, deque, _grouper; remove expectedFailure markers - Add HAS_WEAKREF to PyArray and PyDeque (matches CPython) - Add HAS_WEAKREF to PyItertoolsGrouper (internal use by groupby) - Remove 6 expectedFailure markers from test_dataclasses for weakref/slots tests that now pass * Add HAS_WEAKREF to code, union, partial, lock, IO, mmap, sre, sqlite3, typevar types Add HAS_WEAKREF flag to built-in types that support weakref: - PyCode, PyUnion, PyPartial, Lock, RLock - All IO base/concrete classes (_IOBase, _RawIOBase, _BufferedIOBase, _TextIOBase, BufferedReader, BufferedWriter, BufferedRandom, BufferedRWPair, TextIOWrapper, StringIO, BytesIO, FileIO, WindowsConsoleIO) - PyMmap, sre Pattern, sqlite3 Connection/Cursor - TypeVar, ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeVarTuple Remove 3 expectedFailure markers from test_descr for now-passing tests. * Add HAS_DICT to type flags and handle non-METHOD/CLASS in descr_get - Add HAS_DICT flag to PyType (type metaclass) alongside existing HAS_WEAKREF. All type objects are instances of type and need dict support, matching CPython's PyType_Type. - Replace unimplemented!() in PyMethodDescriptor::descr_get with fallback to bind obj directly, matching CPython's method_get which uses PyCFunction_NewEx for non-METH_METHOD methods. * Fix ext detection, HeapMethodDef ownership, WASM error - Remove HAS_EXT gc_bits flag; detect ext from type flags using raw pointer reads to avoid Stacked Borrows violations - Store HeapMethodDef owner in payload instead of dict hack - Clear dict entries in gc_clear_raw to break cycles - Add WASM error fallback when exception serialization fails
1 parent 3b91466 commit 04cf5da

40 files changed

Lines changed: 566 additions & 258 deletions

.cspell.dict/rust-more.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ bindgen
66
bitand
77
bitflags
88
bitor
9+
bitvec
910
bitxor
1011
bstr
1112
byteorder
@@ -58,6 +59,7 @@ powi
5859
prepended
5960
punct
6061
replacen
62+
retag
6163
rmatch
6264
rposition
6365
rsplitn
@@ -89,5 +91,3 @@ widestring
8991
winapi
9092
winresource
9193
winsock
92-
bitvec
93-
Bitvec

Lib/test/test_dataclasses/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3672,7 +3672,6 @@ class A:
36723672
self.assertEqual(obj.a, 'a')
36733673
self.assertEqual(obj.b, 'b')
36743674

3675-
@unittest.expectedFailure # TODO: RUSTPYTHON
36763675
def test_slots_no_weakref(self):
36773676
@dataclass(slots=True)
36783677
class A:
@@ -3687,7 +3686,6 @@ class A:
36873686
with self.assertRaises(AttributeError):
36883687
a.__weakref__
36893688

3690-
@unittest.expectedFailure # TODO: RUSTPYTHON
36913689
def test_slots_weakref(self):
36923690
@dataclass(slots=True, weakref_slot=True)
36933691
class A:
@@ -3748,7 +3746,6 @@ def test_weakref_slot_make_dataclass(self):
37483746
"weakref_slot is True but slots is False"):
37493747
B = make_dataclass('B', [('a', int),], weakref_slot=True)
37503748

3751-
@unittest.expectedFailure # TODO: RUSTPYTHON
37523749
def test_weakref_slot_subclass_weakref_slot(self):
37533750
@dataclass(slots=True, weakref_slot=True)
37543751
class Base:
@@ -3767,7 +3764,6 @@ class A(Base):
37673764
a_ref = weakref.ref(a)
37683765
self.assertIs(a.__weakref__, a_ref)
37693766

3770-
@unittest.expectedFailure # TODO: RUSTPYTHON
37713767
def test_weakref_slot_subclass_no_weakref_slot(self):
37723768
@dataclass(slots=True, weakref_slot=True)
37733769
class Base:
@@ -3785,7 +3781,6 @@ class A(Base):
37853781
a_ref = weakref.ref(a)
37863782
self.assertIs(a.__weakref__, a_ref)
37873783

3788-
@unittest.expectedFailure # TODO: RUSTPYTHON
37893784
def test_weakref_slot_normal_base_weakref_slot(self):
37903785
class Base:
37913786
__slots__ = ('__weakref__',)
@@ -3830,7 +3825,6 @@ class B[T2]:
38303825
self.assertTrue(B.__weakref__)
38313826
B()
38323827

3833-
@unittest.expectedFailure # TODO: RUSTPYTHON
38343828
def test_dataclass_derived_generic_from_base(self):
38353829
T = typing.TypeVar('T')
38363830

Lib/test/test_descr.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,6 @@ class X(object):
13211321
with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"):
13221322
X().a
13231323

1324-
@unittest.expectedFailure # TODO: RUSTPYTHON
13251324
def test_slots_special(self):
13261325
# Testing __dict__ and __weakref__ in __slots__...
13271326
class D(object):
@@ -2294,7 +2293,6 @@ def __contains__(self, value):
22942293
self.assertIn(i, p10)
22952294
self.assertNotIn(10, p10)
22962295

2297-
@unittest.expectedFailure # TODO: RUSTPYTHON
22982296
def test_weakrefs(self):
22992297
# Testing weak references...
23002298
import weakref
@@ -3976,7 +3974,6 @@ def __init__(self, x):
39763974
o = trash(o)
39773975
del o
39783976

3979-
@unittest.expectedFailure # TODO: RUSTPYTHON
39803977
def test_slots_multiple_inheritance(self):
39813978
# SF bug 575229, multiple inheritance w/ slots dumps core
39823979
class A(object):

Lib/test/test_weakref.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1862,7 +1862,6 @@ def test_weak_valued_delitem(self):
18621862
self.assertEqual(len(d), 1)
18631863
self.assertEqual(list(d.items()), [('something else', o2)])
18641864

1865-
@unittest.expectedFailure # TODO: RUSTPYTHON
18661865
def test_weak_keyed_bad_delitem(self):
18671866
d = weakref.WeakKeyDictionary()
18681867
o = Object('1')

crates/stdlib/src/_asyncio.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub(crate) mod _asyncio {
160160
}
161161

162162
#[pyclass(
163-
flags(BASETYPE, HAS_DICT),
163+
flags(BASETYPE, HAS_DICT, HAS_WEAKREF),
164164
with(Constructor, Initializer, Destructor, Representable, Iterable)
165165
)]
166166
impl PyFuture {
@@ -1169,7 +1169,7 @@ pub(crate) mod _asyncio {
11691169
}
11701170

11711171
#[pyclass(
1172-
flags(BASETYPE, HAS_DICT),
1172+
flags(BASETYPE, HAS_DICT, HAS_WEAKREF),
11731173
with(Constructor, Initializer, Destructor, Representable, Iterable)
11741174
)]
11751175
impl PyTask {

crates/stdlib/src/_sqlite3.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ mod _sqlite3 {
976976
}
977977
}
978978

979-
#[pyclass(with(Constructor, Callable, Initializer), flags(BASETYPE))]
979+
#[pyclass(with(Constructor, Callable, Initializer), flags(BASETYPE, HAS_WEAKREF))]
980980
impl Connection {
981981
fn drop_db(&self) {
982982
self.db.lock().take();
@@ -1629,7 +1629,10 @@ mod _sqlite3 {
16291629
size: Option<c_int>,
16301630
}
16311631

1632-
#[pyclass(with(Constructor, Initializer, IterNext, Iterable), flags(BASETYPE))]
1632+
#[pyclass(
1633+
with(Constructor, Initializer, IterNext, Iterable),
1634+
flags(BASETYPE, HAS_WEAKREF)
1635+
)]
16331636
impl Cursor {
16341637
fn new(
16351638
connection: PyRef<Connection>,

crates/stdlib/src/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ mod array {
698698
}
699699

700700
#[pyclass(
701-
flags(BASETYPE),
701+
flags(BASETYPE, HAS_WEAKREF),
702702
with(
703703
Comparable,
704704
AsBuffer,

crates/stdlib/src/mmap.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ mod mmap {
851851

852852
#[pyclass(
853853
with(Constructor, AsMapping, AsSequence, AsBuffer, Representable),
854-
flags(BASETYPE)
854+
flags(BASETYPE, HAS_WEAKREF)
855855
)]
856856
impl PyMmap {
857857
fn as_bytes_mut(&self) -> BorrowedValueMut<'_, [u8]> {

crates/stdlib/src/re.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ mod re {
317317
#[pyfunction]
318318
fn purge(_vm: &VirtualMachine) {}
319319

320-
#[pyclass]
320+
#[pyclass(flags(HAS_WEAKREF))]
321321
impl PyPattern {
322322
#[pymethod(name = "match")]
323323
fn match_(&self, text: PyStrRef) -> Option<PyMatch> {

crates/vm/src/builtins/asyncgenerator.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ impl PyPayload for PyAsyncGen {
4040
}
4141
}
4242

43-
#[pyclass(flags(DISALLOW_INSTANTIATION), with(PyRef, Representable, Destructor))]
43+
#[pyclass(
44+
flags(DISALLOW_INSTANTIATION, HAS_WEAKREF),
45+
with(PyRef, Representable, Destructor)
46+
)]
4447
impl PyAsyncGen {
4548
pub const fn as_coro(&self) -> &Coro {
4649
&self.inner

0 commit comments

Comments
 (0)