mirror of https://github.com/python/cpython
gh-112075: Make instance attributes stored in inline "dict" thread safe (#114742)
Make instance attributes stored in inline "dict" thread safe on free-threaded builds
This commit is contained in:
parent
1446024124
commit
8b541c017e
|
@ -493,6 +493,7 @@ do { \
|
||||||
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
|
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
|
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
|
||||||
|
PyAPI_FUNC(void) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict);
|
||||||
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
|
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
|
||||||
|
|
||||||
#define TYPE_MAX_WATCHERS 8
|
#define TYPE_MAX_WATCHERS 8
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#ifndef Py_INTERNAL_DICT_H
|
#ifndef Py_INTERNAL_DICT_H
|
||||||
#define Py_INTERNAL_DICT_H
|
#define Py_INTERNAL_DICT_H
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -9,9 +8,10 @@ extern "C" {
|
||||||
# error "this header requires Py_BUILD_CORE define"
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "pycore_freelist.h" // _PyFreeListState
|
#include "pycore_freelist.h" // _PyFreeListState
|
||||||
#include "pycore_identifier.h" // _Py_Identifier
|
#include "pycore_identifier.h" // _Py_Identifier
|
||||||
#include "pycore_object.h" // PyManagedDictPointer
|
#include "pycore_object.h" // PyManagedDictPointer
|
||||||
|
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE
|
||||||
|
|
||||||
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
||||||
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
||||||
|
@ -249,7 +249,7 @@ _PyDict_NotifyEvent(PyInterpreterState *interp,
|
||||||
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
|
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern PyDictObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj);
|
extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj);
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *)_PyDict_FromItems(
|
PyAPI_FUNC(PyObject *)_PyDict_FromItems(
|
||||||
PyObject *const *keys, Py_ssize_t keys_offset,
|
PyObject *const *keys, Py_ssize_t keys_offset,
|
||||||
|
@ -277,7 +277,6 @@ _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix)
|
||||||
static inline size_t
|
static inline size_t
|
||||||
shared_keys_usable_size(PyDictKeysObject *keys)
|
shared_keys_usable_size(PyDictKeysObject *keys)
|
||||||
{
|
{
|
||||||
#ifdef Py_GIL_DISABLED
|
|
||||||
// dk_usable will decrease for each instance that is created and each
|
// dk_usable will decrease for each instance that is created and each
|
||||||
// value that is added. dk_nentries will increase for each value that
|
// value that is added. dk_nentries will increase for each value that
|
||||||
// is added. We want to always return the right value or larger.
|
// is added. We want to always return the right value or larger.
|
||||||
|
@ -285,11 +284,9 @@ shared_keys_usable_size(PyDictKeysObject *keys)
|
||||||
// second, and conversely here we read dk_usable first and dk_entries
|
// second, and conversely here we read dk_usable first and dk_entries
|
||||||
// second (to avoid the case where we read entries before the increment
|
// second (to avoid the case where we read entries before the increment
|
||||||
// and read usable after the decrement)
|
// and read usable after the decrement)
|
||||||
return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) +
|
Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_usable);
|
||||||
_Py_atomic_load_ssize_acquire(&keys->dk_nentries));
|
Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_nentries);
|
||||||
#else
|
return dk_nentries + dk_usable;
|
||||||
return (size_t)keys->dk_nentries + (size_t)keys->dk_usable;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t
|
static inline size_t
|
||||||
|
|
|
@ -12,6 +12,7 @@ extern "C" {
|
||||||
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
|
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
|
||||||
#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall()
|
#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall()
|
||||||
#include "pycore_interp.h" // PyInterpreterState.gc
|
#include "pycore_interp.h" // PyInterpreterState.gc
|
||||||
|
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_PTR_RELAXED
|
||||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||||
|
|
||||||
/* Check if an object is consistent. For example, ensure that the reference
|
/* Check if an object is consistent. For example, ensure that the reference
|
||||||
|
@ -659,10 +660,10 @@ extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *);
|
||||||
extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int);
|
extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int);
|
||||||
|
|
||||||
void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
|
void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
|
||||||
extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
extern int _PyObject_StoreInstanceAttribute(PyObject *obj,
|
||||||
PyObject *name, PyObject *value);
|
PyObject *name, PyObject *value);
|
||||||
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
|
extern bool _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name,
|
||||||
PyObject *name);
|
PyObject **attr);
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
|
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
|
||||||
|
@ -683,6 +684,13 @@ _PyObject_ManagedDictPointer(PyObject *obj)
|
||||||
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
|
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline PyDictObject *
|
||||||
|
_PyObject_GetManagedDict(PyObject *obj)
|
||||||
|
{
|
||||||
|
PyManagedDictPointer *dorv = _PyObject_ManagedDictPointer(obj);
|
||||||
|
return (PyDictObject *)FT_ATOMIC_LOAD_PTR_RELAXED(dorv->dict);
|
||||||
|
}
|
||||||
|
|
||||||
static inline PyDictValues *
|
static inline PyDictValues *
|
||||||
_PyObject_InlineValues(PyObject *obj)
|
_PyObject_InlineValues(PyObject *obj)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,7 +21,10 @@ extern "C" {
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
#define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value)
|
#define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value)
|
||||||
|
#define FT_ATOMIC_STORE_PTR(value, new_value) _Py_atomic_store_ptr(&value, new_value)
|
||||||
#define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value)
|
#define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value)
|
||||||
|
#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) \
|
||||||
|
_Py_atomic_load_ssize_acquire(&value)
|
||||||
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) \
|
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) \
|
||||||
_Py_atomic_load_ssize_relaxed(&value)
|
_Py_atomic_load_ssize_relaxed(&value)
|
||||||
#define FT_ATOMIC_STORE_PTR(value, new_value) \
|
#define FT_ATOMIC_STORE_PTR(value, new_value) \
|
||||||
|
@ -30,6 +33,12 @@ extern "C" {
|
||||||
_Py_atomic_load_ptr_acquire(&value)
|
_Py_atomic_load_ptr_acquire(&value)
|
||||||
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) \
|
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) \
|
||||||
_Py_atomic_load_uintptr_acquire(&value)
|
_Py_atomic_load_uintptr_acquire(&value)
|
||||||
|
#define FT_ATOMIC_LOAD_PTR_RELAXED(value) \
|
||||||
|
_Py_atomic_load_ptr_relaxed(&value)
|
||||||
|
#define FT_ATOMIC_LOAD_UINT8(value) \
|
||||||
|
_Py_atomic_load_uint8(&value)
|
||||||
|
#define FT_ATOMIC_STORE_UINT8(value, new_value) \
|
||||||
|
_Py_atomic_store_uint8(&value, new_value)
|
||||||
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \
|
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \
|
||||||
_Py_atomic_store_ptr_relaxed(&value, new_value)
|
_Py_atomic_store_ptr_relaxed(&value, new_value)
|
||||||
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \
|
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \
|
||||||
|
@ -43,11 +52,16 @@ extern "C" {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define FT_ATOMIC_LOAD_PTR(value) value
|
#define FT_ATOMIC_LOAD_PTR(value) value
|
||||||
|
#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value
|
||||||
#define FT_ATOMIC_LOAD_SSIZE(value) value
|
#define FT_ATOMIC_LOAD_SSIZE(value) value
|
||||||
|
#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) value
|
||||||
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) value
|
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) value
|
||||||
#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value
|
#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value
|
||||||
#define FT_ATOMIC_LOAD_PTR_ACQUIRE(value) value
|
#define FT_ATOMIC_LOAD_PTR_ACQUIRE(value) value
|
||||||
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) value
|
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) value
|
||||||
|
#define FT_ATOMIC_LOAD_PTR_RELAXED(value) value
|
||||||
|
#define FT_ATOMIC_LOAD_UINT8(value) value
|
||||||
|
#define FT_ATOMIC_STORE_UINT8(value, new_value) value = new_value
|
||||||
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value
|
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value
|
||||||
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value
|
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value
|
||||||
#define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value
|
#define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value
|
||||||
|
|
|
@ -873,6 +873,15 @@ class TestInlineValues(unittest.TestCase):
|
||||||
obj.foo = None # Aborted here
|
obj.foo = None # Aborted here
|
||||||
self.assertEqual(obj.__dict__, {"foo":None})
|
self.assertEqual(obj.__dict__, {"foo":None})
|
||||||
|
|
||||||
|
def test_store_attr_deleted_dict(self):
|
||||||
|
class Foo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
f = Foo()
|
||||||
|
del f.__dict__
|
||||||
|
f.a = 3
|
||||||
|
self.assertEqual(f.a, 3)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1752,7 +1752,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
uint64_t new_version = _PyDict_NotifyEvent(
|
||||||
interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
interp, PyDict_EVENT_MODIFIED, mp, key, value);
|
||||||
if (_PyDict_HasSplitTable(mp)) {
|
if (_PyDict_HasSplitTable(mp)) {
|
||||||
mp->ma_values->values[ix] = value;
|
STORE_SPLIT_VALUE(mp, ix, value);
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
||||||
mp->ma_used++;
|
mp->ma_used++;
|
||||||
|
@ -2514,7 +2514,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
|
||||||
mp->ma_version_tag = new_version;
|
mp->ma_version_tag = new_version;
|
||||||
if (_PyDict_HasSplitTable(mp)) {
|
if (_PyDict_HasSplitTable(mp)) {
|
||||||
assert(old_value == mp->ma_values->values[ix]);
|
assert(old_value == mp->ma_values->values[ix]);
|
||||||
mp->ma_values->values[ix] = NULL;
|
STORE_SPLIT_VALUE(mp, ix, NULL);
|
||||||
assert(ix < SHARED_KEYS_MAX_SIZE);
|
assert(ix < SHARED_KEYS_MAX_SIZE);
|
||||||
/* Update order */
|
/* Update order */
|
||||||
delete_index_from_values(mp->ma_values, ix);
|
delete_index_from_values(mp->ma_values, ix);
|
||||||
|
@ -4226,7 +4226,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
|
||||||
assert(_PyDict_HasSplitTable(mp));
|
assert(_PyDict_HasSplitTable(mp));
|
||||||
assert(mp->ma_values->values[ix] == NULL);
|
assert(mp->ma_values->values[ix] == NULL);
|
||||||
MAINTAIN_TRACKING(mp, key, value);
|
MAINTAIN_TRACKING(mp, key, value);
|
||||||
mp->ma_values->values[ix] = Py_NewRef(value);
|
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
|
||||||
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
|
||||||
mp->ma_used++;
|
mp->ma_used++;
|
||||||
mp->ma_version_tag = new_version;
|
mp->ma_version_tag = new_version;
|
||||||
|
@ -6616,28 +6616,79 @@ make_dict_from_instance_attributes(PyInterpreterState *interp,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDictObject *
|
static PyDictObject *
|
||||||
_PyObject_MakeDictFromInstanceAttributes(PyObject *obj)
|
materialize_managed_dict_lock_held(PyObject *obj)
|
||||||
{
|
{
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
|
||||||
|
|
||||||
|
OBJECT_STAT_INC(dict_materialized_on_request);
|
||||||
|
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
||||||
OBJECT_STAT_INC(dict_materialized_on_request);
|
OBJECT_STAT_INC(dict_materialized_on_request);
|
||||||
return make_dict_from_instance_attributes(interp, keys, values);
|
PyDictObject *dict = make_dict_from_instance_attributes(interp, keys, values);
|
||||||
|
FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
|
||||||
|
(PyDictObject *)dict);
|
||||||
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDictObject *
|
||||||
|
_PyObject_MaterializeManagedDict(PyObject *obj)
|
||||||
|
{
|
||||||
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict != NULL) {
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
|
|
||||||
int
|
#ifdef Py_GIL_DISABLED
|
||||||
_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict != NULL) {
|
||||||
|
// We raced with another thread creating the dict
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dict = materialize_managed_dict_lock_held(obj);
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
exit:
|
||||||
|
#endif
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_or_del_lock_held(PyDictObject *dict, PyObject *name, PyObject *value)
|
||||||
|
{
|
||||||
|
if (value == NULL) {
|
||||||
|
Py_hash_t hash;
|
||||||
|
if (!PyUnicode_CheckExact(name) || (hash = unicode_get_hash(name)) == -1) {
|
||||||
|
hash = PyObject_Hash(name);
|
||||||
|
if (hash == -1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return delitem_knownhash_lock_held((PyObject *)dict, name, hash);
|
||||||
|
} else {
|
||||||
|
return setitem_lock_held(dict, name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called with either the object's lock or the dict's lock held
|
||||||
|
// depending on whether or not a dict has been materialized for
|
||||||
|
// the object.
|
||||||
|
static int
|
||||||
|
store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
|
||||||
PyObject *name, PyObject *value)
|
PyObject *name, PyObject *value)
|
||||||
{
|
{
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
|
||||||
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
||||||
assert(keys != NULL);
|
assert(keys != NULL);
|
||||||
assert(values != NULL);
|
assert(values != NULL);
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
Py_ssize_t ix = DKIX_EMPTY;
|
Py_ssize_t ix = DKIX_EMPTY;
|
||||||
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
|
assert(dict == NULL || ((PyDictObject *)dict)->ma_values == values);
|
||||||
if (PyUnicode_CheckExact(name)) {
|
if (PyUnicode_CheckExact(name)) {
|
||||||
Py_hash_t hash = unicode_get_hash(name);
|
Py_hash_t hash = unicode_get_hash(name);
|
||||||
if (hash == -1) {
|
if (hash == -1) {
|
||||||
|
@ -6674,25 +6725,33 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict;
|
|
||||||
if (ix == DKIX_EMPTY) {
|
if (ix == DKIX_EMPTY) {
|
||||||
|
int res;
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
dict = make_dict_from_instance_attributes(
|
// Make the dict but don't publish it in the object
|
||||||
interp, keys, values);
|
// so that no one else will see it.
|
||||||
if (dict == NULL) {
|
dict = make_dict_from_instance_attributes(PyInterpreterState_Get(), keys, values);
|
||||||
|
if (dict == NULL ||
|
||||||
|
set_or_del_lock_held(dict, name, value) < 0) {
|
||||||
|
Py_XDECREF(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
_PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict;
|
|
||||||
}
|
FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
|
||||||
if (value == NULL) {
|
(PyDictObject *)dict);
|
||||||
return PyDict_DelItem((PyObject *)dict, name);
|
return 0;
|
||||||
}
|
|
||||||
else {
|
|
||||||
return PyDict_SetItem((PyObject *)dict, name, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(dict);
|
||||||
|
|
||||||
|
res = set_or_del_lock_held (dict, name, value);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *old_value = values->values[ix];
|
PyObject *old_value = values->values[ix];
|
||||||
values->values[ix] = Py_XNewRef(value);
|
FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value));
|
||||||
|
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
PyErr_Format(PyExc_AttributeError,
|
PyErr_Format(PyExc_AttributeError,
|
||||||
|
@ -6719,6 +6778,72 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
store_instance_attr_dict(PyObject *obj, PyDictObject *dict, PyObject *name, PyObject *value)
|
||||||
|
{
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
|
int res;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(dict);
|
||||||
|
if (dict->ma_values == values) {
|
||||||
|
res = store_instance_attr_lock_held(obj, values, name, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = set_or_del_lock_held(dict, name, value);
|
||||||
|
}
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value)
|
||||||
|
{
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
|
if (!FT_ATOMIC_LOAD_UINT8(values->valid)) {
|
||||||
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict == NULL) {
|
||||||
|
dict = (PyDictObject *)PyObject_GenericGetDict(obj, NULL);
|
||||||
|
if (dict == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int res = store_instance_attr_dict(obj, dict, name, value);
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return store_instance_attr_dict(obj, dict, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
// We have a valid inline values, at least for now... There are two potential
|
||||||
|
// races with having the values become invalid. One is the dictionary
|
||||||
|
// being detached from the object. The other is if someone is inserting
|
||||||
|
// into the dictionary directly and therefore causing it to resize.
|
||||||
|
//
|
||||||
|
// If we haven't materialized the dictionary yet we lock on the object, which
|
||||||
|
// will also be used to prevent the dictionary from being materialized while
|
||||||
|
// we're doing the insertion. If we race and the dictionary gets created
|
||||||
|
// then we'll need to release the object lock and lock the dictionary to
|
||||||
|
// prevent resizing.
|
||||||
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict == NULL) {
|
||||||
|
int res;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
|
|
||||||
|
if (dict == NULL) {
|
||||||
|
res = store_instance_attr_lock_held(obj, values, name, value);
|
||||||
|
}
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
if (dict == NULL) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return store_instance_attr_dict(obj, dict, name, value);
|
||||||
|
#else
|
||||||
|
return store_instance_attr_lock_held(obj, values, name, value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Sanity check for managed dicts */
|
/* Sanity check for managed dicts */
|
||||||
#if 0
|
#if 0
|
||||||
#define CHECK(val) assert(val); if (!(val)) { return 0; }
|
#define CHECK(val) assert(val); if (!(val)) { return 0; }
|
||||||
|
@ -6750,19 +6875,79 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PyObject *
|
// Attempts to get an instance attribute from the inline values. Returns true
|
||||||
_PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
|
// if successful, or false if the caller needs to lookup in the dictionary.
|
||||||
PyObject *name)
|
bool
|
||||||
|
_PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr)
|
||||||
{
|
{
|
||||||
assert(PyUnicode_CheckExact(name));
|
assert(PyUnicode_CheckExact(name));
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
|
if (!FT_ATOMIC_LOAD_UINT8(values->valid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
||||||
assert(keys != NULL);
|
assert(keys != NULL);
|
||||||
Py_ssize_t ix = _PyDictKeys_StringLookup(keys, name);
|
Py_ssize_t ix = _PyDictKeys_StringLookup(keys, name);
|
||||||
if (ix == DKIX_EMPTY) {
|
if (ix == DKIX_EMPTY) {
|
||||||
return NULL;
|
*attr = NULL;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PyObject *value = _Py_atomic_load_ptr_relaxed(&values->values[ix]);
|
||||||
|
if (value == NULL || _Py_TryIncrefCompare(&values->values[ix], value)) {
|
||||||
|
*attr = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict == NULL) {
|
||||||
|
// No dict, lock the object to prevent one from being
|
||||||
|
// materialized...
|
||||||
|
bool success = false;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
|
|
||||||
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
|
if (dict == NULL) {
|
||||||
|
// Still no dict, we can read from the values
|
||||||
|
assert(values->valid);
|
||||||
|
value = values->values[ix];
|
||||||
|
*attr = Py_XNewRef(value);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a dictionary, we'll need to lock it to prevent
|
||||||
|
// the values from being resized.
|
||||||
|
assert(dict != NULL);
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(dict);
|
||||||
|
|
||||||
|
if (dict->ma_values == values && FT_ATOMIC_LOAD_UINT8(values->valid)) {
|
||||||
|
value = _Py_atomic_load_ptr_relaxed(&values->values[ix]);
|
||||||
|
*attr = Py_XNewRef(value);
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
// Caller needs to lookup from the dictionary
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
return success;
|
||||||
|
#else
|
||||||
PyObject *value = values->values[ix];
|
PyObject *value = values->values[ix];
|
||||||
return Py_XNewRef(value);
|
*attr = Py_XNewRef(value);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -6775,20 +6960,19 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
|
||||||
PyDictObject *dict;
|
PyDictObject *dict;
|
||||||
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
if (values->valid) {
|
if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
|
||||||
PyDictKeysObject *keys = CACHED_KEYS(tp);
|
PyDictKeysObject *keys = CACHED_KEYS(tp);
|
||||||
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
|
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
|
||||||
if (values->values[i] != NULL) {
|
if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
dict = _PyObject_ManagedDictPointer(obj)->dict;
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
}
|
}
|
||||||
else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
||||||
PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj);
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
dict = managed_dict->dict;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
||||||
|
@ -6820,53 +7004,115 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
|
||||||
|
{
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
|
||||||
|
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(obj);
|
||||||
|
|
||||||
|
Py_XINCREF(new_dict);
|
||||||
|
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict);
|
||||||
|
|
||||||
|
if (values->valid) {
|
||||||
|
FT_ATOMIC_STORE_UINT8(values->valid, 0);
|
||||||
|
for (Py_ssize_t i = 0; i < values->capacity; i++) {
|
||||||
|
Py_CLEAR(values->values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PyObject_ClearManagedDict(PyObject *obj)
|
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
|
||||||
{
|
{
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
||||||
PyTypeObject *tp = Py_TYPE(obj);
|
PyTypeObject *tp = Py_TYPE(obj);
|
||||||
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict;
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
if (dict) {
|
if (dict == NULL) {
|
||||||
_PyDict_DetachFromObject(dict, obj);
|
#ifdef Py_GIL_DISABLED
|
||||||
_PyObject_ManagedDictPointer(obj)->dict = NULL;
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
Py_DECREF(dict);
|
|
||||||
}
|
dict = _PyObject_ManagedDictPointer(obj)->dict;
|
||||||
else {
|
if (dict == NULL) {
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
set_dict_inline_values(obj, (PyDictObject *)new_dict);
|
||||||
if (values->valid) {
|
|
||||||
for (Py_ssize_t i = 0; i < values->capacity; i++) {
|
|
||||||
Py_CLEAR(values->values[i]);
|
|
||||||
}
|
|
||||||
values->valid = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
if (dict == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
set_dict_inline_values(obj, (PyDictObject *)new_dict);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_BEGIN_CRITICAL_SECTION2(dict, obj);
|
||||||
|
|
||||||
|
// We've locked dict, but the actual dict could have changed
|
||||||
|
// since we locked it.
|
||||||
|
dict = _PyObject_ManagedDictPointer(obj)->dict;
|
||||||
|
|
||||||
|
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
|
||||||
|
(PyDictObject *)Py_XNewRef(new_dict));
|
||||||
|
|
||||||
|
_PyDict_DetachFromObject(dict, obj);
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION2();
|
||||||
|
|
||||||
|
Py_XDECREF(dict);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict);
|
PyDictObject *dict;
|
||||||
|
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
|
|
||||||
|
dict = _PyObject_ManagedDictPointer(obj)->dict;
|
||||||
|
|
||||||
|
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
|
||||||
|
(PyDictObject *)Py_XNewRef(new_dict));
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
Py_XDECREF(dict);
|
||||||
}
|
}
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PyObject_ClearManagedDict(PyObject *obj)
|
||||||
|
{
|
||||||
|
_PyObject_SetManagedDict(obj, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
|
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
|
||||||
{
|
{
|
||||||
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
|
||||||
if (mp->ma_values == NULL || mp->ma_values != _PyObject_InlineValues(obj)) {
|
if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We could be called with an unlocked dict when the caller knows the
|
||||||
|
// values are already detached, so we assert after inline values check.
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
|
||||||
assert(mp->ma_values->embedded == 1);
|
assert(mp->ma_values->embedded == 1);
|
||||||
assert(mp->ma_values->valid == 1);
|
assert(mp->ma_values->valid == 1);
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
Py_BEGIN_CRITICAL_SECTION(mp);
|
|
||||||
mp->ma_values = copy_values(mp->ma_values);
|
PyDictValues *values = copy_values(mp->ma_values);
|
||||||
_PyObject_InlineValues(obj)->valid = 0;
|
|
||||||
Py_END_CRITICAL_SECTION();
|
if (values == NULL) {
|
||||||
if (mp->ma_values == NULL) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
mp->ma_values = values;
|
||||||
|
|
||||||
|
FT_ATOMIC_STORE_UINT8(_PyObject_InlineValues(obj)->valid, 0);
|
||||||
|
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
||||||
ASSERT_CONSISTENT(mp);
|
ASSERT_CONSISTENT(mp);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -6877,29 +7123,28 @@ PyObject_GenericGetDict(PyObject *obj, void *context)
|
||||||
{
|
{
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
PyTypeObject *tp = Py_TYPE(obj);
|
PyTypeObject *tp = Py_TYPE(obj);
|
||||||
|
PyDictObject *dict;
|
||||||
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
|
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
if (dict == NULL &&
|
if (dict == NULL &&
|
||||||
(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
|
(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
|
||||||
_PyObject_InlineValues(obj)->valid
|
FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(obj)->valid)) {
|
||||||
) {
|
dict = _PyObject_MaterializeManagedDict(obj);
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
|
||||||
OBJECT_STAT_INC(dict_materialized_on_request);
|
|
||||||
dict = make_dict_from_instance_attributes(
|
|
||||||
interp, CACHED_KEYS(tp), values);
|
|
||||||
if (dict != NULL) {
|
|
||||||
managed_dict->dict = (PyDictObject *)dict;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else if (dict == NULL) {
|
||||||
dict = managed_dict->dict;
|
Py_BEGIN_CRITICAL_SECTION(obj);
|
||||||
|
|
||||||
|
// Check again that we're not racing with someone else creating the dict
|
||||||
|
dict = _PyObject_GetManagedDict(obj);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
dictkeys_incref(CACHED_KEYS(tp));
|
|
||||||
OBJECT_STAT_INC(dict_materialized_on_request);
|
OBJECT_STAT_INC(dict_materialized_on_request);
|
||||||
|
dictkeys_incref(CACHED_KEYS(tp));
|
||||||
dict = (PyDictObject *)new_dict_with_shared_keys(interp, CACHED_KEYS(tp));
|
dict = (PyDictObject *)new_dict_with_shared_keys(interp, CACHED_KEYS(tp));
|
||||||
managed_dict->dict = (PyDictObject *)dict;
|
FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
|
||||||
|
(PyDictObject *)dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
}
|
}
|
||||||
return Py_XNewRef((PyObject *)dict);
|
return Py_XNewRef((PyObject *)dict);
|
||||||
}
|
}
|
||||||
|
@ -7109,7 +7354,7 @@ _PyObject_InlineValuesConsistencyCheck(PyObject *obj)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyDictObject *dict = (PyDictObject *)_PyObject_ManagedDictPointer(obj)->dict;
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||||
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
|
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
|
||||||
#include "pycore_context.h" // _PyContextTokenMissing_Type
|
#include "pycore_context.h" // _PyContextTokenMissing_Type
|
||||||
|
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
|
||||||
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
|
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
|
||||||
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
|
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
|
||||||
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
|
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic
|
#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic
|
||||||
#include "pycore_unionobject.h" // _PyUnion_Type
|
#include "pycore_unionobject.h" // _PyUnion_Type
|
||||||
|
|
||||||
|
|
||||||
#ifdef Py_LIMITED_API
|
#ifdef Py_LIMITED_API
|
||||||
// Prevent recursive call _Py_IncRef() <=> Py_INCREF()
|
// Prevent recursive call _Py_IncRef() <=> Py_INCREF()
|
||||||
# error "Py_LIMITED_API macro must not be defined"
|
# error "Py_LIMITED_API macro must not be defined"
|
||||||
|
@ -1403,16 +1405,15 @@ _PyObject_GetDictPtr(PyObject *obj)
|
||||||
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
|
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
|
||||||
return _PyObject_ComputedDictPointer(obj);
|
return _PyObject_ComputedDictPointer(obj);
|
||||||
}
|
}
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
|
PyDictObject *dict = _PyObject_GetManagedDict(obj);
|
||||||
if (managed_dict->dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
if (dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
PyDictObject *dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(obj);
|
dict = _PyObject_MaterializeManagedDict(obj);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
managed_dict->dict = dict;
|
|
||||||
}
|
}
|
||||||
return (PyObject **)&managed_dict->dict;
|
return (PyObject **)&_PyObject_ManagedDictPointer(obj)->dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
@ -1480,10 +1481,9 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PyObject *dict;
|
PyObject *dict, *attr;
|
||||||
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) {
|
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
_PyObject_TryGetInstanceAttribute(obj, name, &attr)) {
|
||||||
PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name);
|
|
||||||
if (attr != NULL) {
|
if (attr != NULL) {
|
||||||
*method = attr;
|
*method = attr;
|
||||||
Py_XDECREF(descr);
|
Py_XDECREF(descr);
|
||||||
|
@ -1492,8 +1492,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
||||||
dict = NULL;
|
dict = NULL;
|
||||||
}
|
}
|
||||||
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
||||||
PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj);
|
dict = (PyObject *)_PyObject_GetManagedDict(obj);
|
||||||
dict = (PyObject *)managed_dict->dict;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
||||||
|
@ -1586,26 +1585,23 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) {
|
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
|
||||||
PyDictValues *values = _PyObject_InlineValues(obj);
|
if (PyUnicode_CheckExact(name) &&
|
||||||
if (PyUnicode_CheckExact(name)) {
|
_PyObject_TryGetInstanceAttribute(obj, name, &res)) {
|
||||||
res = _PyObject_GetInstanceAttribute(obj, values, name);
|
|
||||||
if (res != NULL) {
|
if (res != NULL) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dict = (PyObject *)_PyObject_MakeDictFromInstanceAttributes(obj);
|
dict = (PyObject *)_PyObject_MaterializeManagedDict(obj);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
res = NULL;
|
res = NULL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
_PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
||||||
PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj);
|
dict = (PyObject *)_PyObject_GetManagedDict(obj);
|
||||||
dict = (PyObject *)managed_dict->dict;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
|
||||||
|
@ -1700,12 +1696,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
||||||
|
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
PyObject **dictptr;
|
PyObject **dictptr;
|
||||||
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) {
|
|
||||||
res = _PyObject_StoreInstanceAttribute(
|
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
|
||||||
obj, _PyObject_InlineValues(obj), name, value);
|
res = _PyObject_StoreInstanceAttribute(obj, name, value);
|
||||||
goto error_check;
|
goto error_check;
|
||||||
}
|
}
|
||||||
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
|
||||||
|
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
|
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
|
||||||
dictptr = (PyObject **)&managed_dict->dict;
|
dictptr = (PyObject **)&managed_dict->dict;
|
||||||
}
|
}
|
||||||
|
@ -1779,7 +1776,7 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
|
||||||
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
||||||
if (dictptr == NULL) {
|
if (dictptr == NULL) {
|
||||||
if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) &&
|
if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) &&
|
||||||
_PyObject_ManagedDictPointer(obj)->dict == NULL
|
_PyObject_GetManagedDict(obj) == NULL
|
||||||
) {
|
) {
|
||||||
/* Was unable to convert to dict */
|
/* Was unable to convert to dict */
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
|
|
@ -3165,9 +3165,9 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context)
|
||||||
"not a '%.200s'", Py_TYPE(value)->tp_name);
|
"not a '%.200s'", Py_TYPE(value)->tp_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
||||||
PyObject_ClearManagedDict(obj);
|
_PyObject_SetManagedDict(obj, value);
|
||||||
_PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)Py_XNewRef(value);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dictptr = _PyObject_ComputedDictPointer(obj);
|
dictptr = _PyObject_ComputedDictPointer(obj);
|
||||||
|
@ -6194,15 +6194,27 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
|
||||||
/* Changing the class will change the implicit dict keys,
|
/* Changing the class will change the implicit dict keys,
|
||||||
* so we must materialize the dictionary first. */
|
* so we must materialize the dictionary first. */
|
||||||
if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
PyDictObject *dict = _PyObject_ManagedDictPointer(self)->dict;
|
PyDictObject *dict = _PyObject_MaterializeManagedDict(self);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(self);
|
return -1;
|
||||||
if (dict == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
_PyObject_ManagedDictPointer(self)->dict = dict;
|
|
||||||
}
|
}
|
||||||
if (_PyDict_DetachFromObject(dict, self)) {
|
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
Py_BEGIN_CRITICAL_SECTION2(self, dict);
|
||||||
|
|
||||||
|
// If we raced after materialization and replaced the dict
|
||||||
|
// then the materialized dict should no longer have the
|
||||||
|
// inline values in which case detach is a nop.
|
||||||
|
assert(_PyObject_GetManagedDict(self) == dict ||
|
||||||
|
dict->ma_values != _PyObject_InlineValues(self));
|
||||||
|
|
||||||
|
if (_PyDict_DetachFromObject(dict, self) < 0) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_END_CRITICAL_SECTION2();
|
||||||
|
if (error) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1947,15 +1947,13 @@ dummy_func(
|
||||||
|
|
||||||
op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) {
|
op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) {
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(dict == NULL);
|
DEOPT_IF(dict == NULL);
|
||||||
assert(PyDict_CheckExact((PyObject *)dict));
|
assert(PyDict_CheckExact((PyObject *)dict));
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) {
|
op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) {
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
|
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
||||||
if (DK_IS_UNICODE(dict->ma_keys)) {
|
if (DK_IS_UNICODE(dict->ma_keys)) {
|
||||||
|
@ -2072,14 +2070,15 @@ dummy_func(
|
||||||
op(_GUARD_DORV_NO_DICT, (owner -- owner)) {
|
op(_GUARD_DORV_NO_DICT, (owner -- owner)) {
|
||||||
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict);
|
DEOPT_IF(_PyObject_GetManagedDict(owner));
|
||||||
DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0);
|
DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) {
|
op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) {
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_ManagedDictPointer(owner)->dict == NULL);
|
assert(_PyObject_GetManagedDict(owner) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner);
|
PyDictValues *values = _PyObject_InlineValues(owner);
|
||||||
|
|
||||||
PyObject *old_value = values->values[index];
|
PyObject *old_value = values->values[index];
|
||||||
values->values[index] = value;
|
values->values[index] = value;
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
|
@ -2088,6 +2087,7 @@ dummy_func(
|
||||||
else {
|
else {
|
||||||
Py_DECREF(old_value);
|
Py_DECREF(old_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_DECREF(owner);
|
Py_DECREF(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2102,8 +2102,7 @@ dummy_func(
|
||||||
assert(type_version != 0);
|
assert(type_version != 0);
|
||||||
DEOPT_IF(tp->tp_version_tag != type_version);
|
DEOPT_IF(tp->tp_version_tag != type_version);
|
||||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(dict == NULL);
|
DEOPT_IF(dict == NULL);
|
||||||
assert(PyDict_CheckExact((PyObject *)dict));
|
assert(PyDict_CheckExact((PyObject *)dict));
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||||
|
|
|
@ -1998,8 +1998,7 @@
|
||||||
PyObject *owner;
|
PyObject *owner;
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
|
@ -2015,8 +2014,7 @@
|
||||||
oparg = CURRENT_OPARG();
|
oparg = CURRENT_OPARG();
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
uint16_t hint = (uint16_t)CURRENT_OPERAND();
|
uint16_t hint = (uint16_t)CURRENT_OPERAND();
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
if (hint >= (size_t)dict->ma_keys->dk_nentries) {
|
if (hint >= (size_t)dict->ma_keys->dk_nentries) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
|
@ -2159,7 +2157,7 @@
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
if (_PyObject_ManagedDictPointer(owner)->dict) {
|
if (_PyObject_GetManagedDict(owner)) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
}
|
}
|
||||||
|
@ -2177,7 +2175,7 @@
|
||||||
value = stack_pointer[-2];
|
value = stack_pointer[-2];
|
||||||
uint16_t index = (uint16_t)CURRENT_OPERAND();
|
uint16_t index = (uint16_t)CURRENT_OPERAND();
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_ManagedDictPointer(owner)->dict == NULL);
|
assert(_PyObject_GetManagedDict(owner) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner);
|
PyDictValues *values = _PyObject_InlineValues(owner);
|
||||||
PyObject *old_value = values->values[index];
|
PyObject *old_value = values->values[index];
|
||||||
values->values[index] = value;
|
values->values[index] = value;
|
||||||
|
|
|
@ -4017,16 +4017,14 @@
|
||||||
// _CHECK_ATTR_WITH_HINT
|
// _CHECK_ATTR_WITH_HINT
|
||||||
{
|
{
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(dict == NULL, LOAD_ATTR);
|
DEOPT_IF(dict == NULL, LOAD_ATTR);
|
||||||
assert(PyDict_CheckExact((PyObject *)dict));
|
assert(PyDict_CheckExact((PyObject *)dict));
|
||||||
}
|
}
|
||||||
// _LOAD_ATTR_WITH_HINT
|
// _LOAD_ATTR_WITH_HINT
|
||||||
{
|
{
|
||||||
uint16_t hint = read_u16(&this_instr[4].cache);
|
uint16_t hint = read_u16(&this_instr[4].cache);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
|
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
||||||
if (DK_IS_UNICODE(dict->ma_keys)) {
|
if (DK_IS_UNICODE(dict->ma_keys)) {
|
||||||
|
@ -5309,7 +5307,7 @@
|
||||||
{
|
{
|
||||||
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
assert(Py_TYPE(owner)->tp_dictoffset < 0);
|
||||||
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict, STORE_ATTR);
|
DEOPT_IF(_PyObject_GetManagedDict(owner), STORE_ATTR);
|
||||||
DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0, STORE_ATTR);
|
DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0, STORE_ATTR);
|
||||||
}
|
}
|
||||||
// _STORE_ATTR_INSTANCE_VALUE
|
// _STORE_ATTR_INSTANCE_VALUE
|
||||||
|
@ -5317,7 +5315,7 @@
|
||||||
{
|
{
|
||||||
uint16_t index = read_u16(&this_instr[4].cache);
|
uint16_t index = read_u16(&this_instr[4].cache);
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_ManagedDictPointer(owner)->dict == NULL);
|
assert(_PyObject_GetManagedDict(owner) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner);
|
PyDictValues *values = _PyObject_InlineValues(owner);
|
||||||
PyObject *old_value = values->values[index];
|
PyObject *old_value = values->values[index];
|
||||||
values->values[index] = value;
|
values->values[index] = value;
|
||||||
|
@ -5380,8 +5378,7 @@
|
||||||
assert(type_version != 0);
|
assert(type_version != 0);
|
||||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
DEOPT_IF(dict == NULL, STORE_ATTR);
|
DEOPT_IF(dict == NULL, STORE_ATTR);
|
||||||
assert(PyDict_CheckExact((PyObject *)dict));
|
assert(PyDict_CheckExact((PyObject *)dict));
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||||
|
|
|
@ -852,8 +852,7 @@ specialize_dict_access(
|
||||||
instr->op.code = values_op;
|
instr->op.code = values_op;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner);
|
PyDictObject *dict = _PyObject_GetManagedDict(owner);
|
||||||
PyDictObject *dict = managed_dict->dict;
|
|
||||||
if (dict == NULL || !PyDict_CheckExact(dict)) {
|
if (dict == NULL || !PyDict_CheckExact(dict)) {
|
||||||
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT);
|
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -354,6 +354,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool:
|
||||||
NON_ESCAPING_FUNCTIONS = (
|
NON_ESCAPING_FUNCTIONS = (
|
||||||
"Py_INCREF",
|
"Py_INCREF",
|
||||||
"_PyManagedDictPointer_IsValues",
|
"_PyManagedDictPointer_IsValues",
|
||||||
|
"_PyObject_GetManagedDict",
|
||||||
"_PyObject_ManagedDictPointer",
|
"_PyObject_ManagedDictPointer",
|
||||||
"_PyObject_InlineValues",
|
"_PyObject_InlineValues",
|
||||||
"_PyDictValues_AddToInsertionOrder",
|
"_PyDictValues_AddToInsertionOrder",
|
||||||
|
|
Loading…
Reference in New Issue