mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
bpo-45340: Don't create object dictionaries unless actually needed (GH-28802)
* Never change types' cached keys. It could invalidate inline attribute objects. * Lazily create object dictionaries. * Update specialization of LOAD/STORE_ATTR. * Don't update shared keys version for deletion of value. * Update gdb support to handle instance values. * Rename SPLIT_KEYS opcodes to INSTANCE_VALUE.
This commit is contained in:
parent
97308dfcdc
commit
a8b9350964
18 changed files with 721 additions and 400 deletions
185
Objects/object.c
185
Objects/object.c
|
@ -5,6 +5,7 @@
|
|||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
|
||||
#include "pycore_context.h"
|
||||
#include "pycore_dict.h"
|
||||
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
|
||||
#include "pycore_object.h" // _PyType_CheckConsistency()
|
||||
#include "pycore_pyerrors.h" // _PyErr_Occurred()
|
||||
|
@ -1065,10 +1066,8 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Helper to get a pointer to an object's __dict__ slot, if any */
|
||||
|
||||
PyObject **
|
||||
_PyObject_GetDictPtr(PyObject *obj)
|
||||
_PyObject_DictPointer(PyObject *obj)
|
||||
{
|
||||
Py_ssize_t dictoffset;
|
||||
PyTypeObject *tp = Py_TYPE(obj);
|
||||
|
@ -1090,6 +1089,35 @@ _PyObject_GetDictPtr(PyObject *obj)
|
|||
return (PyObject **) ((char *)obj + dictoffset);
|
||||
}
|
||||
|
||||
/* Helper to get a pointer to an object's __dict__ slot, if any.
|
||||
* Creates the dict from inline attributes if necessary.
|
||||
* Does not set an exception. */
|
||||
PyObject **
|
||||
_PyObject_GetDictPtr(PyObject *obj)
|
||||
{
|
||||
PyObject **dict_ptr = _PyObject_DictPointer(obj);
|
||||
if (dict_ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*dict_ptr != NULL) {
|
||||
return dict_ptr;
|
||||
}
|
||||
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj);
|
||||
if (values_ptr == NULL || *values_ptr == NULL) {
|
||||
return dict_ptr;
|
||||
}
|
||||
PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr);
|
||||
if (dict == NULL) {
|
||||
PyErr_Clear();
|
||||
return NULL;
|
||||
}
|
||||
assert(*dict_ptr == NULL);
|
||||
assert(*values_ptr != NULL);
|
||||
*values_ptr = NULL;
|
||||
*dict_ptr = dict;
|
||||
return dict_ptr;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyObject_SelfIter(PyObject *obj)
|
||||
{
|
||||
|
@ -1136,7 +1164,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
|||
}
|
||||
}
|
||||
|
||||
if (tp->tp_getattro != PyObject_GenericGetAttr || !PyUnicode_Check(name)) {
|
||||
if (tp->tp_getattro != PyObject_GenericGetAttr || !PyUnicode_CheckExact(name)) {
|
||||
*method = PyObject_GetAttr(obj, name);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1156,23 +1184,34 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
||||
PyObject *dict;
|
||||
if (dictptr != NULL && (dict = *dictptr) != NULL) {
|
||||
Py_INCREF(dict);
|
||||
PyObject *attr = PyDict_GetItemWithError(dict, name);
|
||||
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj);
|
||||
if (values_ptr && *values_ptr) {
|
||||
assert(*_PyObject_DictPointer(obj) == NULL);
|
||||
PyObject *attr = _PyObject_GetInstanceAttribute(obj, *values_ptr, name);
|
||||
if (attr != NULL) {
|
||||
*method = Py_NewRef(attr);
|
||||
Py_DECREF(dict);
|
||||
*method = attr;
|
||||
Py_XDECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
Py_DECREF(dict);
|
||||
}
|
||||
else {
|
||||
PyObject **dictptr = _PyObject_DictPointer(obj);
|
||||
PyObject *dict;
|
||||
if (dictptr != NULL && (dict = *dictptr) != NULL) {
|
||||
Py_INCREF(dict);
|
||||
PyObject *attr = PyDict_GetItemWithError(dict, name);
|
||||
if (attr != NULL) {
|
||||
*method = Py_NewRef(attr);
|
||||
Py_DECREF(dict);
|
||||
Py_XDECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
Py_DECREF(dict);
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
Py_XDECREF(descr);
|
||||
return 0;
|
||||
if (PyErr_Occurred()) {
|
||||
Py_XDECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1200,6 +1239,17 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
|||
return 0;
|
||||
}
|
||||
|
||||
PyDictValues **
|
||||
_PyObject_ValuesPointer(PyObject *obj)
|
||||
{
|
||||
PyTypeObject *tp = Py_TYPE(obj);
|
||||
Py_ssize_t offset = tp->tp_inline_values_offset;
|
||||
if (offset == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return (PyDictValues **) ((char *)obj + offset);
|
||||
}
|
||||
|
||||
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
|
||||
|
||||
PyObject *
|
||||
|
@ -1247,25 +1297,46 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (dict == NULL) {
|
||||
/* Inline _PyObject_GetDictPtr */
|
||||
dictoffset = tp->tp_dictoffset;
|
||||
if (dictoffset != 0) {
|
||||
if (dictoffset < 0) {
|
||||
Py_ssize_t tsize = Py_SIZE(obj);
|
||||
if (tsize < 0) {
|
||||
tsize = -tsize;
|
||||
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj);
|
||||
if (values_ptr && *values_ptr) {
|
||||
if (PyUnicode_CheckExact(name)) {
|
||||
assert(*_PyObject_DictPointer(obj) == NULL);
|
||||
res = _PyObject_GetInstanceAttribute(obj, *values_ptr, name);
|
||||
if (res != NULL) {
|
||||
goto done;
|
||||
}
|
||||
size_t size = _PyObject_VAR_SIZE(tp, tsize);
|
||||
_PyObject_ASSERT(obj, size <= PY_SSIZE_T_MAX);
|
||||
|
||||
dictoffset += (Py_ssize_t)size;
|
||||
_PyObject_ASSERT(obj, dictoffset > 0);
|
||||
_PyObject_ASSERT(obj, dictoffset % SIZEOF_VOID_P == 0);
|
||||
}
|
||||
dictptr = (PyObject **) ((char *)obj + dictoffset);
|
||||
dict = *dictptr;
|
||||
else {
|
||||
dictptr = _PyObject_DictPointer(obj);
|
||||
assert(dictptr != NULL && *dictptr == NULL);
|
||||
*dictptr = dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr);
|
||||
if (dict == NULL) {
|
||||
res = NULL;
|
||||
goto done;
|
||||
}
|
||||
*values_ptr = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Inline _PyObject_DictPointer */
|
||||
dictoffset = tp->tp_dictoffset;
|
||||
if (dictoffset != 0) {
|
||||
if (dictoffset < 0) {
|
||||
Py_ssize_t tsize = Py_SIZE(obj);
|
||||
if (tsize < 0) {
|
||||
tsize = -tsize;
|
||||
}
|
||||
size_t size = _PyObject_VAR_SIZE(tp, tsize);
|
||||
_PyObject_ASSERT(obj, size <= PY_SSIZE_T_MAX);
|
||||
|
||||
dictoffset += (Py_ssize_t)size;
|
||||
_PyObject_ASSERT(obj, dictoffset > 0);
|
||||
_PyObject_ASSERT(obj, dictoffset % SIZEOF_VOID_P == 0);
|
||||
}
|
||||
dictptr = (PyObject **) ((char *)obj + dictoffset);
|
||||
dict = *dictptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dict != NULL) {
|
||||
|
@ -1328,7 +1399,6 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
PyTypeObject *tp = Py_TYPE(obj);
|
||||
PyObject *descr;
|
||||
descrsetfunc f;
|
||||
PyObject **dictptr;
|
||||
int res = -1;
|
||||
|
||||
if (!PyUnicode_Check(name)){
|
||||
|
@ -1354,30 +1424,30 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
}
|
||||
}
|
||||
|
||||
/* XXX [Steve Dower] These are really noisy - worth it? */
|
||||
/*if (PyType_Check(obj) || PyModule_Check(obj)) {
|
||||
if (value && PySys_Audit("object.__setattr__", "OOO", obj, name, value) < 0)
|
||||
return -1;
|
||||
if (!value && PySys_Audit("object.__delattr__", "OO", obj, name) < 0)
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
if (dict == NULL) {
|
||||
dictptr = _PyObject_GetDictPtr(obj);
|
||||
if (dictptr == NULL) {
|
||||
if (descr == NULL) {
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.100s' object has no attribute '%U'",
|
||||
tp->tp_name, name);
|
||||
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj);
|
||||
if (values_ptr && *values_ptr) {
|
||||
res = _PyObject_StoreInstanceAttribute(obj, *values_ptr, name, value);
|
||||
}
|
||||
else {
|
||||
PyObject **dictptr = _PyObject_DictPointer(obj);
|
||||
if (dictptr == NULL) {
|
||||
if (descr == NULL) {
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.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 {
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.50s' object attribute '%U' is read-only",
|
||||
tp->tp_name, name);
|
||||
res = _PyObjectDict_SetItem(tp, dictptr, name, value);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
res = _PyObjectDict_SetItem(tp, dictptr, name, value);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(dict);
|
||||
|
@ -1407,8 +1477,15 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
|
|||
{
|
||||
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
||||
if (dictptr == NULL) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"This object has no __dict__");
|
||||
PyDictValues** values_ptr = _PyObject_ValuesPointer(obj);
|
||||
if (values_ptr != NULL && *values_ptr != NULL) {
|
||||
/* Was unable to convert to dict */
|
||||
PyErr_NoMemory();
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"This object has no __dict__");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (value == NULL) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue