GH-95245: Store object values and dict pointers in single tagged pointer. (GH-95278)

This commit is contained in:
Mark Shannon 2022-08-01 14:34:54 +01:00 committed by GitHub
parent fb75d015f4
commit de388c0a7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 271 additions and 203 deletions

View File

@ -83,6 +83,3 @@ typedef struct {
PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *); PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *);
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg);
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *self);

View File

@ -510,3 +510,7 @@ Py_DEPRECATED(3.11) typedef int UsingDeprecatedTrashcanMacro;
#define Py_TRASHCAN_SAFE_END(op) \ #define Py_TRASHCAN_SAFE_END(op) \
Py_TRASHCAN_END; \ Py_TRASHCAN_END; \
} while(0); } while(0);
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj);

View File

@ -274,24 +274,49 @@ extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
PyObject *name); PyObject *name);
static inline PyDictValues **_PyObject_ValuesPointer(PyObject *obj) typedef union {
PyObject *dict;
/* Use a char* to generate a warning if directly assigning a PyDictValues */
char *values;
} PyDictOrValues;
static inline PyDictOrValues *
_PyObject_DictOrValuesPointer(PyObject *obj)
{ {
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
return ((PyDictValues **)obj)-4; return ((PyDictOrValues *)obj)-3;
} }
static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj) static inline int
_PyDictOrValues_IsValues(PyDictOrValues dorv)
{ {
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); return ((uintptr_t)dorv.values) & 1;
return ((PyObject **)obj)-3; }
static inline PyDictValues *
_PyDictOrValues_GetValues(PyDictOrValues dorv)
{
assert(_PyDictOrValues_IsValues(dorv));
return (PyDictValues *)(dorv.values + 1);
}
static inline PyObject *
_PyDictOrValues_GetDict(PyDictOrValues dorv)
{
assert(!_PyDictOrValues_IsValues(dorv));
return dorv.dict;
}
static inline void
_PyDictOrValues_SetValues(PyDictOrValues *ptr, PyDictValues *values)
{
ptr->values = ((char *)values) - 1;
} }
#define MANAGED_DICT_OFFSET (((int)sizeof(PyObject *))*-3) #define MANAGED_DICT_OFFSET (((int)sizeof(PyObject *))*-3)
extern PyObject ** _PyObject_DictPointer(PyObject *); extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
extern int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg); extern void _PyObject_FreeInstanceAttributes(PyObject *obj);
extern void _PyObject_ClearInstanceAttributes(PyObject *self);
extern void _PyObject_FreeInstanceAttributes(PyObject *self);
extern int _PyObject_IsInstanceDictEmpty(PyObject *); extern int _PyObject_IsInstanceDictEmpty(PyObject *);
extern PyObject* _PyType_GetSubclasses(PyTypeObject *); extern PyObject* _PyType_GetSubclasses(PyTypeObject *);

View File

@ -0,0 +1,2 @@
Merge managed dict and values pointer into a single tagged pointer to save
one word in the pre-header.

View File

@ -5368,7 +5368,7 @@ init_inline_values(PyObject *obj, PyTypeObject *tp)
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
values->values[i] = NULL; values->values[i] = NULL;
} }
*_PyObject_ValuesPointer(obj) = values; _PyDictOrValues_SetValues(_PyObject_DictOrValuesPointer(obj), values);
return 0; return 0;
} }
@ -5394,7 +5394,7 @@ _PyObject_InitializeDict(PyObject *obj)
if (dict == NULL) { if (dict == NULL) {
return -1; return -1;
} }
PyObject **dictptr = _PyObject_DictPointer(obj); PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
*dictptr = dict; *dictptr = dict;
return 0; return 0;
} }
@ -5422,7 +5422,6 @@ make_dict_from_instance_attributes(PyDictKeysObject *keys, PyDictValues *values)
PyObject * PyObject *
_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values) _PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values)
{ {
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
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(keys, values); return make_dict_from_instance_attributes(keys, values);
@ -5458,8 +5457,7 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
if (dict == NULL) { if (dict == NULL) {
return -1; return -1;
} }
*_PyObject_ValuesPointer(obj) = NULL; _PyObject_DictOrValuesPointer(obj)->dict = dict;
*_PyObject_ManagedDictPointer(obj) = dict;
if (value == NULL) { if (value == NULL) {
return PyDict_DelItem(dict, name); return PyDict_DelItem(dict, name);
} }
@ -5488,6 +5486,37 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
return 0; return 0;
} }
/* Sanity check for managed dicts */
#if 0
#define CHECK(val) assert(val); if (!(val)) { return 0; }
int
_PyObject_ManagedDictValidityCheck(PyObject *obj)
{
PyTypeObject *tp = Py_TYPE(obj);
CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
if (_PyDictOrValues_IsValues(*dorv_ptr)) {
PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
int size = ((uint8_t *)values)[-2];
int count = 0;
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
if (values->values[i] != NULL) {
count++;
}
}
CHECK(size == count);
}
else {
if (dorv_ptr->dict != NULL) {
CHECK(PyDict_Check(dorv_ptr->dict));
}
}
return 1;
}
#endif
PyObject * PyObject *
_PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
PyObject *name) PyObject *name)
@ -5511,105 +5540,94 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
if (tp->tp_dictoffset == 0) { if (tp->tp_dictoffset == 0) {
return 1; return 1;
} }
PyObject **dictptr; PyObject *dict;
if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
PyDictValues *values = *_PyObject_ValuesPointer(obj); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
if (values) { if (_PyDictOrValues_IsValues(dorv)) {
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 (_PyDictOrValues_GetValues(dorv)->values[i] != NULL) {
return 0; return 0;
} }
} }
return 1; return 1;
} }
dictptr = _PyObject_ManagedDictPointer(obj); dict = _PyDictOrValues_GetDict(dorv);
} }
else { else {
dictptr = _PyObject_DictPointer(obj); PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
dict = *dictptr;
} }
PyObject *dict = *dictptr;
if (dict == NULL) { if (dict == NULL) {
return 1; return 1;
} }
return ((PyDictObject *)dict)->ma_used == 0; return ((PyDictObject *)dict)->ma_used == 0;
} }
int
_PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg)
{
PyTypeObject *tp = Py_TYPE(self);
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictValues **values_ptr = _PyObject_ValuesPointer(self);
if (*values_ptr == NULL) {
return 0;
}
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
Py_VISIT((*values_ptr)->values[i]);
}
return 0;
}
void
_PyObject_ClearInstanceAttributes(PyObject *self)
{
PyTypeObject *tp = Py_TYPE(self);
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictValues **values_ptr = _PyObject_ValuesPointer(self);
if (*values_ptr == NULL) {
return;
}
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
Py_CLEAR((*values_ptr)->values[i]);
}
}
void void
_PyObject_FreeInstanceAttributes(PyObject *self) _PyObject_FreeInstanceAttributes(PyObject *self)
{ {
PyTypeObject *tp = Py_TYPE(self); PyTypeObject *tp = Py_TYPE(self);
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictValues **values_ptr = _PyObject_ValuesPointer(self); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
if (*values_ptr == NULL) { if (!_PyDictOrValues_IsValues(dorv)) {
return; return;
} }
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
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++) {
Py_XDECREF((*values_ptr)->values[i]); Py_XDECREF(values->values[i]);
} }
free_values(*values_ptr); free_values(values);
} }
int int
_PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
{ {
PyTypeObject *tp = Py_TYPE(self); PyTypeObject *tp = Py_TYPE(obj);
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
return 0; return 0;
} }
assert(tp->tp_dictoffset); assert(tp->tp_dictoffset);
int err = _PyObject_VisitInstanceAttributes(self, visit, arg); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
if (err) { if (_PyDictOrValues_IsValues(dorv)) {
return err; PyDictValues *values = _PyDictOrValues_GetValues(dorv);
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
Py_VISIT(values->values[i]);
}
}
else {
PyObject *dict = _PyDictOrValues_GetDict(dorv);
Py_VISIT(dict);
} }
Py_VISIT(*_PyObject_ManagedDictPointer(self));
return 0; return 0;
} }
void void
_PyObject_ClearManagedDict(PyObject *self) _PyObject_ClearManagedDict(PyObject *obj)
{ {
PyTypeObject *tp = Py_TYPE(self); PyTypeObject *tp = Py_TYPE(obj);
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
return; return;
} }
_PyObject_FreeInstanceAttributes(self); PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
*_PyObject_ValuesPointer(self) = NULL; if (_PyDictOrValues_IsValues(*dorv_ptr)) {
Py_CLEAR(*_PyObject_ManagedDictPointer(self)); PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
Py_CLEAR(values->values[i]);
}
dorv_ptr->dict = NULL;
free_values(values);
}
else {
PyObject *dict = dorv_ptr->dict;
if (dict) {
dorv_ptr->dict = NULL;
Py_DECREF(dict);
}
}
} }
PyObject * PyObject *
@ -5618,25 +5636,26 @@ PyObject_GenericGetDict(PyObject *obj, void *context)
PyObject *dict; PyObject *dict;
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) { if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj); PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
PyObject **dictptr = _PyObject_ManagedDictPointer(obj); if (_PyDictOrValues_IsValues(*dorv_ptr)) {
if (*values_ptr) { PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
assert(*dictptr == NULL);
OBJECT_STAT_INC(dict_materialized_on_request); OBJECT_STAT_INC(dict_materialized_on_request);
*dictptr = dict = make_dict_from_instance_attributes(CACHED_KEYS(tp), *values_ptr); dict = make_dict_from_instance_attributes(CACHED_KEYS(tp), values);
if (dict != NULL) { if (dict != NULL) {
*values_ptr = NULL; dorv_ptr->dict = dict;
} }
} }
else if (*dictptr == NULL) {
*dictptr = dict = PyDict_New();
}
else { else {
dict = *dictptr; dict = _PyDictOrValues_GetDict(*dorv_ptr);
if (dict == NULL) {
dictkeys_incref(CACHED_KEYS(tp));
dict = new_dict_with_shared_keys(CACHED_KEYS(tp));
dorv_ptr->dict = dict;
}
} }
} }
else { else {
PyObject **dictptr = _PyObject_DictPointer(obj); PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr == NULL) { if (dictptr == NULL) {
PyErr_SetString(PyExc_AttributeError, PyErr_SetString(PyExc_AttributeError,
"This object has no __dict__"); "This object has no __dict__");

View File

@ -1054,14 +1054,12 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
} }
PyObject ** PyObject **
_PyObject_DictPointer(PyObject *obj) _PyObject_ComputedDictPointer(PyObject *obj)
{ {
Py_ssize_t dictoffset; Py_ssize_t dictoffset;
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { assert((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0);
return _PyObject_ManagedDictPointer(obj);
}
dictoffset = tp->tp_dictoffset; dictoffset = tp->tp_dictoffset;
if (dictoffset == 0) if (dictoffset == 0)
return NULL; return NULL;
@ -1086,22 +1084,18 @@ PyObject **
_PyObject_GetDictPtr(PyObject *obj) _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_DictPointer(obj); return _PyObject_ComputedDictPointer(obj);
} }
PyObject **dict_ptr = _PyObject_ManagedDictPointer(obj); PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj); if (_PyDictOrValues_IsValues(*dorv_ptr)) {
if (*values_ptr == NULL) { PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, _PyDictOrValues_GetValues(*dorv_ptr));
return dict_ptr; if (dict == NULL) {
PyErr_Clear();
return NULL;
}
dorv_ptr->dict = dict;
} }
assert(*dict_ptr == NULL); return &dorv_ptr->dict;
PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr);
if (dict == NULL) {
PyErr_Clear();
return NULL;
}
*values_ptr = NULL;
*dict_ptr = dict;
return dict_ptr;
} }
PyObject * PyObject *
@ -1170,36 +1164,46 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
} }
} }
} }
PyDictValues *values; PyObject *dict;
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
(values = *_PyObject_ValuesPointer(obj))) PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj);
{ if (_PyDictOrValues_IsValues(*dorv_ptr)) {
assert(*_PyObject_DictPointer(obj) == NULL); PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name);
if (attr != NULL) { if (attr != NULL) {
*method = attr; *method = attr;
Py_XDECREF(descr); Py_XDECREF(descr);
return 0; return 0;
}
dict = NULL;
}
else {
dict = dorv_ptr->dict;
} }
} }
else { else {
PyObject **dictptr = _PyObject_DictPointer(obj); PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
PyObject *dict; if (dictptr != NULL) {
if (dictptr != NULL && (dict = *dictptr) != NULL) { dict = *dictptr;
Py_INCREF(dict); }
PyObject *attr = PyDict_GetItemWithError(dict, name); else {
if (attr != NULL) { dict = NULL;
*method = Py_NewRef(attr); }
Py_DECREF(dict); }
Py_XDECREF(descr); if (dict != NULL) {
return 0; Py_INCREF(dict);
} PyObject *attr = PyDict_GetItemWithError(dict, name);
if (attr != NULL) {
*method = Py_NewRef(attr);
Py_DECREF(dict); Py_DECREF(dict);
Py_XDECREF(descr);
return 0;
}
Py_DECREF(dict);
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
Py_XDECREF(descr); Py_XDECREF(descr);
return 0; return 0;
}
} }
} }
@ -1243,7 +1247,6 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
PyObject *descr = NULL; PyObject *descr = NULL;
PyObject *res = NULL; PyObject *res = NULL;
descrgetfunc f; descrgetfunc f;
PyObject **dictptr;
if (!PyUnicode_Check(name)){ if (!PyUnicode_Check(name)){
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
@ -1274,30 +1277,31 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
} }
} }
if (dict == NULL) { if (dict == NULL) {
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
*_PyObject_ValuesPointer(obj)) PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj);
{ if (_PyDictOrValues_IsValues(*dorv_ptr)) {
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj); PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
if (PyUnicode_CheckExact(name)) { if (PyUnicode_CheckExact(name)) {
assert(*_PyObject_DictPointer(obj) == NULL); res = _PyObject_GetInstanceAttribute(obj, values, name);
res = _PyObject_GetInstanceAttribute(obj, *values_ptr, name); if (res != NULL) {
if (res != NULL) { goto done;
goto done; }
}
else {
dict = _PyObject_MakeDictFromInstanceAttributes(obj, values);
if (dict == NULL) {
res = NULL;
goto done;
}
dorv_ptr->dict = dict;
} }
} }
else { else {
dictptr = _PyObject_DictPointer(obj); dict = _PyDictOrValues_GetDict(*dorv_ptr);
assert(dictptr != NULL && *dictptr == NULL);
*dictptr = dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr);
if (dict == NULL) {
res = NULL;
goto done;
}
*values_ptr = NULL;
} }
} }
else { else {
dictptr = _PyObject_DictPointer(obj); PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr) { if (dictptr) {
dict = *dictptr; dict = *dictptr;
} }
@ -1389,27 +1393,34 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
} }
if (dict == NULL) { if (dict == NULL) {
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && *_PyObject_ValuesPointer(obj)) { PyObject **dictptr;
res = _PyObject_StoreInstanceAttribute(obj, *_PyObject_ValuesPointer(obj), name, value); if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
if (_PyDictOrValues_IsValues(*dorv_ptr)) {
res = _PyObject_StoreInstanceAttribute(
obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value);
goto error_check;
}
dictptr = &dorv_ptr->dict;
} }
else { else {
PyObject **dictptr = _PyObject_DictPointer(obj); dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr == NULL) { }
if (descr == NULL) { if (dictptr == NULL) {
PyErr_Format(PyExc_AttributeError, if (descr == NULL) {
"'%.100s' object has no attribute '%U'", PyErr_Format(PyExc_AttributeError,
tp->tp_name, name); "'%.100s' object has no attribute '%U'",
} tp->tp_name, name);
else {
PyErr_Format(PyExc_AttributeError,
"'%.50s' object attribute '%U' is read-only",
tp->tp_name, name);
}
goto done;
} }
else { else {
res = _PyObjectDict_SetItem(tp, dictptr, name, value); PyErr_Format(PyExc_AttributeError,
"'%.50s' object attribute '%U' is read-only",
tp->tp_name, name);
} }
goto done;
}
else {
res = _PyObjectDict_SetItem(tp, dictptr, name, value);
} }
} }
else { else {
@ -1420,6 +1431,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
res = PyDict_SetItem(dict, name, value); res = PyDict_SetItem(dict, name, value);
Py_DECREF(dict); Py_DECREF(dict);
} }
error_check:
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
if (PyType_IsSubtype(tp, &PyType_Type)) { if (PyType_IsSubtype(tp, &PyType_Type)) {
PyErr_Format(PyExc_AttributeError, PyErr_Format(PyExc_AttributeError,
@ -1451,7 +1463,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_MANAGED_DICT) && if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT) &&
*_PyObject_ValuesPointer(obj) != NULL) _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(obj)))
{ {
/* Was unable to convert to dict */ /* Was unable to convert to dict */
PyErr_NoMemory(); PyErr_NoMemory();

View File

@ -1310,15 +1310,13 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
} }
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
assert(type->tp_dictoffset); int err = _PyObject_VisitManagedDict(self, visit, arg);
int err = _PyObject_VisitInstanceAttributes(self, visit, arg);
if (err) { if (err) {
return err; return err;
} }
} }
else if (type->tp_dictoffset != base->tp_dictoffset) {
if (type->tp_dictoffset != base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self);
PyObject **dictptr = _PyObject_DictPointer(self);
if (dictptr && *dictptr) if (dictptr && *dictptr)
Py_VISIT(*dictptr); Py_VISIT(*dictptr);
} }
@ -1379,10 +1377,10 @@ subtype_clear(PyObject *self)
/* Clear the instance dict (if any), to break cycles involving only /* Clear the instance dict (if any), to break cycles involving only
__dict__ slots (as in the case 'self.__dict__ is self'). */ __dict__ slots (as in the case 'self.__dict__ is self'). */
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
_PyObject_ClearInstanceAttributes(self); _PyObject_ClearManagedDict(self);
} }
if (type->tp_dictoffset != base->tp_dictoffset) { else if (type->tp_dictoffset != base->tp_dictoffset) {
PyObject **dictptr = _PyObject_DictPointer(self); PyObject **dictptr = _PyObject_ComputedDictPointer(self);
if (dictptr && *dictptr) if (dictptr && *dictptr)
Py_CLEAR(*dictptr); Py_CLEAR(*dictptr);
} }
@ -1526,18 +1524,17 @@ subtype_dealloc(PyObject *self)
/* If we added a dict, DECREF it, or free inline values. */ /* If we added a dict, DECREF it, or free inline values. */
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
PyObject **dictptr = _PyObject_ManagedDictPointer(self); PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(self);
if (*dictptr != NULL) { if (_PyDictOrValues_IsValues(*dorv_ptr)) {
assert(*_PyObject_ValuesPointer(self) == NULL);
Py_DECREF(*dictptr);
*dictptr = NULL;
}
else {
_PyObject_FreeInstanceAttributes(self); _PyObject_FreeInstanceAttributes(self);
} }
else {
Py_XDECREF(_PyDictOrValues_GetDict(*dorv_ptr));
}
dorv_ptr->values = NULL;
} }
else if (type->tp_dictoffset && !base->tp_dictoffset) { else if (type->tp_dictoffset && !base->tp_dictoffset) {
PyObject **dictptr = _PyObject_DictPointer(self); PyObject **dictptr = _PyObject_ComputedDictPointer(self);
if (dictptr != NULL) { if (dictptr != NULL) {
PyObject *dict = *dictptr; PyObject *dict = *dictptr;
if (dict != NULL) { if (dict != NULL) {
@ -5137,7 +5134,9 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
* so we must materialize the dictionary first. */ * so we must materialize the dictionary first. */
assert((oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT) == (newto->tp_flags & Py_TPFLAGS_MANAGED_DICT)); assert((oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT) == (newto->tp_flags & Py_TPFLAGS_MANAGED_DICT));
_PyObject_GetDictPtr(self); _PyObject_GetDictPtr(self);
if (oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT && *_PyObject_ValuesPointer(self)) { if (oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT &&
_PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(self)))
{
/* Was unable to convert to dict */ /* Was unable to convert to dict */
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;

View File

@ -3564,9 +3564,9 @@ handle_eval_breaker:
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
assert(tp->tp_dictoffset < 0); assert(tp->tp_dictoffset < 0);
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictValues *values = *_PyObject_ValuesPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
DEOPT_IF(values == NULL, LOAD_ATTR); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
res = values->values[cache->index]; res = _PyDictOrValues_GetValues(dorv)->values[cache->index];
DEOPT_IF(res == NULL, LOAD_ATTR); DEOPT_IF(res == NULL, LOAD_ATTR);
STAT_INC(LOAD_ATTR, hit); STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res); Py_INCREF(res);
@ -3613,7 +3613,9 @@ handle_eval_breaker:
assert(type_version != 0); assert(type_version != 0);
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictObject *dict = *(PyDictObject **)_PyObject_ManagedDictPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
DEOPT_IF(dict == NULL, LOAD_ATTR); DEOPT_IF(dict == NULL, LOAD_ATTR);
assert(PyDict_CheckExact((PyObject *)dict)); assert(PyDict_CheckExact((PyObject *)dict));
PyObject *name = GETITEM(names, oparg>>1); PyObject *name = GETITEM(names, oparg>>1);
@ -3750,12 +3752,13 @@ handle_eval_breaker:
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);
PyDictValues *values = *_PyObject_ValuesPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
DEOPT_IF(values == NULL, STORE_ATTR); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR);
STAT_INC(STORE_ATTR, hit); STAT_INC(STORE_ATTR, hit);
Py_ssize_t index = cache->index; Py_ssize_t index = cache->index;
STACK_SHRINK(1); STACK_SHRINK(1);
PyObject *value = POP(); PyObject *value = POP();
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
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) {
@ -3778,7 +3781,9 @@ handle_eval_breaker:
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);
PyDictObject *dict = *(PyDictObject **)_PyObject_ManagedDictPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
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(names, oparg); PyObject *name = GETITEM(names, oparg);
@ -4680,8 +4685,8 @@ handle_eval_breaker:
assert(type_version != 0); assert(type_version != 0);
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictObject *dict = *(PyDictObject**)_PyObject_ManagedDictPointer(self); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
DEOPT_IF(dict != NULL, LOAD_ATTR); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls; PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls;
DEOPT_IF(self_heap_type->ht_cached_keys->dk_version != DEOPT_IF(self_heap_type->ht_cached_keys->dk_version !=
read_u32(cache->keys_version), LOAD_ATTR); read_u32(cache->keys_version), LOAD_ATTR);

View File

@ -635,9 +635,8 @@ specialize_dict_access(
return 0; return 0;
} }
_PyAttrCache *cache = (_PyAttrCache *)(instr + 1); _PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
PyObject **dictptr = _PyObject_ManagedDictPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
PyDictObject *dict = (PyDictObject *)*dictptr; if (_PyDictOrValues_IsValues(dorv)) {
if (dict == NULL) {
// Virtual dictionary // Virtual dictionary
PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys;
assert(PyUnicode_CheckExact(name)); assert(PyUnicode_CheckExact(name));
@ -652,7 +651,8 @@ specialize_dict_access(
_Py_SET_OPCODE(*instr, values_op); _Py_SET_OPCODE(*instr, values_op);
} }
else { else {
if (!PyDict_CheckExact(dict)) { PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
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;
} }
@ -995,9 +995,9 @@ PyObject *descr, DescriptorClassification kind)
ObjectDictKind dictkind; ObjectDictKind dictkind;
PyDictKeysObject *keys; PyDictKeysObject *keys;
if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
PyObject *dict = *_PyObject_ManagedDictPointer(owner); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys;
if (dict == NULL) { if (_PyDictOrValues_IsValues(dorv)) {
dictkind = MANAGED_VALUES; dictkind = MANAGED_VALUES;
} }
else { else {

View File

@ -489,6 +489,8 @@ class HeapTypeObjectPtr(PyObjectPtr):
dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset
PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer() PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer()
dictptr = dictptr.cast(PyObjectPtrPtr) dictptr = dictptr.cast(PyObjectPtrPtr)
if int(dictptr.dereference()) & 1:
return None
return PyObjectPtr.from_pyobject_ptr(dictptr.dereference()) return PyObjectPtr.from_pyobject_ptr(dictptr.dereference())
except RuntimeError: except RuntimeError:
# Corrupt data somewhere; fail safe # Corrupt data somewhere; fail safe
@ -502,12 +504,14 @@ class HeapTypeObjectPtr(PyObjectPtr):
has_values = int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT has_values = int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT
if not has_values: if not has_values:
return None return None
PyDictValuesPtrPtr = gdb.lookup_type("PyDictValues").pointer().pointer() charptrptr_t = _type_char_ptr().pointer()
valuesptr = self._gdbval.cast(PyDictValuesPtrPtr) - 4 ptr = self._gdbval.cast(charptrptr_t) - 3
values = valuesptr.dereference() char_ptr = ptr.dereference()
if int(values) == 0: if (int(char_ptr) & 1) == 0:
return None return None
values = values['values'] char_ptr += 1
values_ptr = char_ptr.cast(gdb.lookup_type("PyDictValues").pointer())
values = values_ptr['values']
return PyKeysValuesPair(self.get_cached_keys(), values) return PyKeysValuesPair(self.get_cached_keys(), values)
def get_cached_keys(self): def get_cached_keys(self):
@ -527,14 +531,15 @@ class HeapTypeObjectPtr(PyObjectPtr):
return ProxyAlreadyVisited('<...>') return ProxyAlreadyVisited('<...>')
visited.add(self.as_address()) visited.add(self.as_address())
pyop_attr_dict = self.get_attr_dict()
keys_values = self.get_keys_values() keys_values = self.get_keys_values()
if keys_values: if keys_values:
attr_dict = keys_values.proxyval(visited) attr_dict = keys_values.proxyval(visited)
elif pyop_attr_dict:
attr_dict = pyop_attr_dict.proxyval(visited)
else: else:
attr_dict = {} pyop_attr_dict = self.get_attr_dict()
if pyop_attr_dict:
attr_dict = pyop_attr_dict.proxyval(visited)
else:
attr_dict = {}
tp_name = self.safe_tp_name() tp_name = self.safe_tp_name()
# Class: # Class: