aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2023-03-04 17:20:42 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-03-07 08:25:18 +0000
commitad398d4801ccb808fe67deab2f1b47c54237b544 (patch)
treeee4be990cfa60d67b68cb48585b97e2aaf926bd8
parent668f36dfb55dafeed694265959ec07c91b9f94ad (diff)
PyEnum: fix a leak in forgiveness mode
When forgiveness mode is used, there is a memory leak. In this case, an PyErr_Fetch is not closed by PyErr_Restore. The error variables were not cleared, causing the leak. Task-number: PYSIDE-1735 Change-Id: I6bda598a800c351c6f13c3a99ee2e63a7e6f11dc Fixes: PYSIDE-2169 Reviewed-by: Adrian Herrmann <adrian.herrmann@qt.io> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> (cherry picked from commit 53e14d6f150cebfd4b0a84335a7d413f4ae72ee3) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--sources/shiboken6/libshiboken/sbkfeature_base.cpp176
1 files changed, 94 insertions, 82 deletions
diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
index 6cb77d17e..e19d5c34f 100644
--- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp
+++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
@@ -172,6 +172,91 @@ static PyObject *replaceNoArgWithZero(PyObject *callable)
return PyObject_CallFunctionObjArgs(partial, callable, zero, nullptr);
}
+static PyObject *lookupUnqualifiedOrOldEnum(PyTypeObject *type, PyObject *name)
+{
+ static PyTypeObject *const EnumMeta = getPyEnumMeta();
+ static PyObject *const _member_map_ = String::createStaticString("_member_map_");
+ // This is similar to `find_name_in_mro`, but instead of looking directly into
+ // tp_dict, we also search for the attribute in local classes of that dict (Part 2).
+ PyObject *mro = type->tp_mro;
+ PyObject *result{};
+ assert(PyTuple_Check(mro));
+ Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+ for (idx = 0; idx < n; ++idx) {
+ auto *base = PyTuple_GET_ITEM(mro, idx);
+ auto *type_base = reinterpret_cast<PyTypeObject *>(base);
+ auto sotp = PepType_SOTP(type_base);
+ // The EnumFlagInfo structure tells us if there are Enums at all.
+ const char **enumFlagInfo = sotp->enumFlagInfo;
+ if (!(enumFlagInfo))
+ continue;
+ if (!sotp->enumFlagsDict)
+ initEnumFlagsDict(type_base);
+ bool useFakeRenames = !(Enum::enumOption & Enum::ENOPT_NO_FAKERENAMES);
+ if (useFakeRenames) {
+ auto *rename = PyDict_GetItem(sotp->enumFlagsDict, name);
+ if (rename) {
+ /*
+ * Part 1: Look into the enumFlagsDict if we have an old flags name.
+ * -------------------------------------------------------------
+ * We need to replace the parameterless
+
+ QtCore.Qt.Alignment()
+
+ * by the one-parameter call
+
+ QtCore.Qt.AlignmentFlag(0)
+
+ * That means: We need to bind the zero as default into a wrapper and
+ * return that to be called.
+ *
+ * Addendum:
+ * ---------
+ * We first need to look into the current opcode of the bytecode to find
+ * out if we have a call like above or just a type lookup.
+ */
+ auto *flagType = PyDict_GetItem(type_base->tp_dict, rename);
+ if (currentOpcode_Is_CallMethNoArgs())
+ return replaceNoArgWithZero(flagType);
+ Py_INCREF(flagType);
+ return flagType;
+ }
+ }
+ bool useFakeShortcuts = !(Enum::enumOption & Enum::ENOPT_NO_FAKESHORTCUT);
+ if (useFakeShortcuts) {
+ auto *dict = type_base->tp_dict;
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(dict, &pos, &key, &value)) {
+ /*
+ * Part 2: Check for a duplication into outer scope.
+ * -------------------------------------------------
+ * We need to replace the shortcut
+
+ QtCore.Qt.AlignLeft
+
+ * by the correct call
+
+ QtCore.Qt.AlignmentFlag.AlignLeft
+
+ * That means: We need to search all Enums of the class.
+ */
+ if (Py_TYPE(value) == EnumMeta) {
+ auto *valtype = reinterpret_cast<PyTypeObject *>(value);
+ auto *member_map = PyDict_GetItem(valtype->tp_dict, _member_map_);
+ if (member_map && PyDict_Check(member_map)) {
+ result = PyDict_GetItem(member_map, name);
+ Py_XINCREF(result);
+ if (result)
+ return result;
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
{
/*
@@ -184,7 +269,6 @@ PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
static PyObject *const ignAttr1 = PyName::qtStaticMetaObject();
static PyObject *const ignAttr2 = PyMagicName::get();
static PyTypeObject *const EnumMeta = getPyEnumMeta();
- static PyObject *const _member_map_ = String::createStaticString("_member_map_");
if (SelectFeatureSet != nullptr)
SelectFeatureSet(type);
@@ -194,7 +278,7 @@ PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
// The PYI files now look correct, but the old duplication is
// emulated here. This should be removed in Qt 7, see `parser.py`.
//
- // FIXME PYSIDE7 should remove this forgivingness:
+ // FIXME PYSIDE7 should remove this forgiveness:
//
// The duplication of enum values into the enclosing scope, allowing to write
// Qt.AlignLeft instead of Qt.Alignment.AlignLeft, is still implemented but
@@ -211,88 +295,16 @@ PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
}
if (!ret && name != ignAttr1 && name != ignAttr2) {
- PyObject *error_type, *error_value, *error_traceback;
+ PyObject *error_type{}, *error_value{}, *error_traceback{};
PyErr_Fetch(&error_type, &error_value, &error_traceback);
-
- // This is similar to `find_name_in_mro`, but instead of looking directly into
- // tp_dict, we also search for the attribute in local classes of that dict (Part 2).
- PyObject *mro = type->tp_mro;
- assert(PyTuple_Check(mro));
- size_t idx, n = PyTuple_GET_SIZE(mro);
- for (idx = 0; idx < n; ++idx) {
- auto *base = PyTuple_GET_ITEM(mro, idx);
- auto *type_base = reinterpret_cast<PyTypeObject *>(base);
- auto sotp = PepType_SOTP(type_base);
- // The EnumFlagInfo structure tells us if there are Enums at all.
- const char **enumFlagInfo = sotp->enumFlagInfo;
- if (!(enumFlagInfo))
- continue;
- if (!sotp->enumFlagsDict)
- initEnumFlagsDict(type_base);
- bool useFakeRenames = !(Enum::enumOption & Enum::ENOPT_NO_FAKERENAMES);
- if (useFakeRenames) {
- auto *rename = PyDict_GetItem(sotp->enumFlagsDict, name);
- if (rename) {
- /*
- * Part 1: Look into the enumFlagsDict if we have an old flags name.
- * -------------------------------------------------------------
- * We need to replace the parameterless
-
- QtCore.Qt.Alignment()
-
- * by the one-parameter call
-
- QtCore.Qt.AlignmentFlag(0)
-
- * That means: We need to bind the zero as default into a wrapper and
- * return that to be called.
- *
- * Addendum:
- * ---------
- * We first need to look into the current opcode of the bytecode to find
- * out if we have a call like above or just a type lookup.
- */
- auto *flagType = PyDict_GetItem(type_base->tp_dict, rename);
- if (currentOpcode_Is_CallMethNoArgs())
- return replaceNoArgWithZero(flagType);
- Py_INCREF(flagType);
- return flagType;
- }
- }
- bool useFakeShortcuts = !(Enum::enumOption & Enum::ENOPT_NO_FAKESHORTCUT);
- if (useFakeShortcuts) {
- auto *dict = type_base->tp_dict;
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- while (PyDict_Next(dict, &pos, &key, &value)) {
- /*
- * Part 2: Check for a duplication into outer scope.
- * -------------------------------------------------
- * We need to replace the shortcut
-
- QtCore.Qt.AlignLeft
-
- * by the correct call
-
- QtCore.Qt.AlignmentFlag.AlignLeft
-
- * That means: We need to search all Enums of the class.
- */
- if (Py_TYPE(value) == EnumMeta) {
- auto *valtype = reinterpret_cast<PyTypeObject *>(value);
- auto *member_map = PyDict_GetItem(valtype->tp_dict, _member_map_);
- if (member_map && PyDict_Check(member_map)) {
- auto *result = PyDict_GetItem(member_map, name);
- if (result) {
- Py_INCREF(result);
- return result;
- }
- }
- }
- }
- }
+ ret = lookupUnqualifiedOrOldEnum(type, name);
+ if (ret) {
+ Py_DECREF(error_type);
+ Py_XDECREF(error_value);
+ Py_XDECREF(error_traceback);
+ } else {
+ PyErr_Restore(error_type, error_value, error_traceback);
}
- PyErr_Restore(error_type, error_value, error_traceback);
}
return ret;
}