Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions Include/internal/pycore_regions.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,21 @@ PyObject* _Py_ResetInvariant(void);
#define Py_ResetInvariant() _Py_ResetInvariant()

// Invariant placeholder
bool _Pyrona_AddReference(PyObject* target, PyObject* new_ref);
#define Pyrona_ADDREFERENCE(a, b) _Pyrona_AddReference(a, b)
#define Pyrona_REMOVEREFERENCE(a, b) // TODO
bool _Py_RegionAddReference(PyObject* src, PyObject* new_tgt);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the renaming! 🙏

#define Py_REGIONADDREFERENCE(a, b) _Py_RegionAddReference(_PyObject_CAST(a), b)

void _Py_RegionAddLocalReference(PyObject* new_tgt);
#define Py_REGIONADDLOCALREFERENCE(b) _Py_RegionAddLocalReference(b)

// Helper macros to count the number of arguments
#define _COUNT_ARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
#define COUNT_ARGS(...) _COUNT_ARGS(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

bool _Pyrona_AddReferences(PyObject* target, int new_refc, ...);
#define Pyrona_ADDREFERENCES(a, ...) _Pyrona_AddReferences(a, COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)
bool _Py_RegionAddReferences(PyObject* src, int new_tgtc, ...);
#define Py_REGIONADDREFERENCES(a, ...) _Py_RegionAddReferences(_PyObject_CAST(a), COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)

void _Py_RegionRemoveReference(PyObject* src, PyObject* old_tgt);
#define Py_REGIONREMOVEREFERENCE(a, b) _Py_RegionRemoveReference(_PyObject_CAST(a), b)

#ifdef NDEBUG
#define _Py_VPYDBG(fmt, ...)
Expand All @@ -83,6 +89,32 @@ int _PyRegion_is_closed(PyObject* region);
int _PyCown_release(PyObject *self);
int _PyCown_is_released(PyObject *self);


#ifdef _Py_TYPEOF
#define Py_CLEAR_OBJECT_FIELD(op, field) \
do { \
_Py_TYPEOF(op)* _tmp_field_ptr = &(field); \
_Py_TYPEOF(op) _tmp_old_field = (*_tmp_field_ptr); \
if (_tmp_old_field != NULL) { \
*_tmp_field_ptr = _Py_NULL; \
Py_REGIONREMOVEREFERENCE(op, _tmp_old_field); \
Py_DECREF(_tmp_old_field); \
} \
} while (0)
#else
#define Py_CLEAR_OBJECT_FIELD(op, field) \
do { \
PyObject **_tmp_field_ptr = _Py_CAST(PyObject**, &(op)); \
PyObject *_tmp_old_field = (*_tmp_field_ptr); \
if (_tmp_old_field != NULL) { \
PyObject *_null_ptr = _Py_NULL; \
memcpy(_tmp_field_ptr, &_null_ptr, sizeof(PyObject*)); \
Py_REGIONREMOVEREFERENCE(op, _tmp_old_field); \
Py_DECREF(_tmp_old_field); \
} \
} while (0)
#endif

#ifdef __cplusplus
}
#endif
Expand Down
80 changes: 80 additions & 0 deletions Lib/test/test_regions_dictobject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import unittest

class TestRegionsDictObject(unittest.TestCase):
def setUp(self):
enableinvariant()

def test_dict_insert_empty_dict(self):
# Create Region with Empty dictionary
r = Region()
d = {}
r.body = d
n = {}
# Add local object to region
d["foo"] = n
self.assertTrue(r.owns_object(n))

def test_dict_insert_nonempty_dict(self):
# Create Region with Nonempty dictionary
r = Region()
d = {}
d["bar"] = 1
r.body = d
# Add local object to region
n = {}
d["foo"] = n
self.assertTrue(r.owns_object(n))

def test_dict_update_dict(self):
# Create Region with Nonempty dictionary
r = Region()
d = {}
n1 = {}
d["foo"] = n1
r.body = d
# Update dictionary to contain a local object
n2 = {}
d["foo"] = n2
self.assertTrue(r.owns_object(n2))

def test_dict_clear(self):
# Create Region with Nonempty dictionary
r = Region()
d = {}
n = {}
d["foo"] = n
r.body = d
# Clear dictionary
d.clear()
# As LRC is not checked by the invariant, this test cannot
# check anything useful yet.

def test_dict_copy(self):
r = Region()
d = {}
r.body = d
r2 = Region()
d["foo"] = r2
d.copy()

def test_dict_setdefault(self):
r = Region("outer")
d = {}
r.body = d
r2 = Region("inner")
d["foo"] = r2
d.setdefault("foo", r2)
self.assertRaises(RegionError, d.setdefault, "bar", r2)

def test_dict_update(self):
# Create a region containing two dictionaries
r = Region()
d = {}
r.body = d
d2 = {}
r.body2 = d2
# Add a contained region to the first dictionary
d["reg"] = Region()
# Update the second dictionary to contain the elements of the first
self.assertRaises(RegionError, d2.update, d)
self.assertRaises(RegionError, d2.update, d)
Loading
Loading