aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/helper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/helper.cpp')
-rw-r--r--sources/shiboken6/libshiboken/helper.cpp452
1 files changed, 385 insertions, 67 deletions
diff --git a/sources/shiboken6/libshiboken/helper.cpp b/sources/shiboken6/libshiboken/helper.cpp
index 02f8177f3..46af68956 100644
--- a/sources/shiboken6/libshiboken/helper.cpp
+++ b/sources/shiboken6/libshiboken/helper.cpp
@@ -1,67 +1,64 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt for Python.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "helper.h"
+#include "basewrapper_p.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
+#include "sbkstaticstrings.h"
+#include "pep384impl.h"
+
+#include <algorithm>
+#include <optional>
#include <iomanip>
#include <iostream>
-
+#include <climits>
+#include <cstring>
#include <cstdarg>
+#include <cctype>
#ifdef _WIN32
-# ifndef NOMINMAX
-# define NOMINMAX
-# endif
-# include <windows.h>
+# include <sbkwindows.h>
#else
# include <pthread.h>
#endif
-#include <algorithm>
+static std::optional<std::string> getStringAttr(PyObject *obj, const char *what)
+{
+ if (PyObject_HasAttrString(obj, what) != 0) { // Check first to suppress error.
+ Shiboken::AutoDecRef result(PyObject_GetAttrString(obj, what));
+ if (PyUnicode_Check(result.object()) != 0)
+ return _PepUnicode_AsString(result.object());
+ }
+ return std::nullopt;
+}
-static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str)
+static std::optional<int> getIntAttr(PyObject *obj, const char *what)
{
- if (obj) {
- str << '"' << obj->tp_name << "\", 0x" << std::hex
- << obj->tp_flags << std::dec;
+ if (PyObject_HasAttrString(obj, what) != 0) { // Check first to suppress error.
+ Shiboken::AutoDecRef result(PyObject_GetAttrString(obj, what));
+ if (PyLong_Check(result.object()) != 0)
+ return PyLong_AsLong(result.object());
+ }
+ return std::nullopt;
+}
+
+static bool verbose = false;
+
+static void formatTypeTuple(PyObject *t, const char *what, std::ostream &str);
+
+static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str, bool verbose)
+{
+ if (obj == nullptr) {
+ str << '0';
+ return;
+ }
+
+ str << '"' << obj->tp_name << '"';
+ if (verbose) {
+ bool immutableType = false;
+ str << ", 0x" << std::hex << obj->tp_flags << std::dec;
if (obj->tp_flags & Py_TPFLAGS_HEAPTYPE)
str << " [heaptype]";
if (obj->tp_flags & Py_TPFLAGS_BASETYPE)
@@ -84,8 +81,59 @@ static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str)
str << " [type]";
if (obj->tp_flags & Py_TPFLAGS_IS_ABSTRACT)
str << " [abstract]";
- } else {
- str << '0';
+ if (obj->tp_flags & Py_TPFLAGS_READY)
+ str << " [ready]";
+ if (obj->tp_flags & Py_TPFLAGS_READYING)
+ str << " [readying]";
+ if (obj->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)
+ str << " [method_descriptor]";
+# ifndef Py_LIMITED_API
+ if (obj->tp_flags & Py_TPFLAGS_HAVE_VECTORCALL)
+ str << " [vectorcall]";
+# endif // !Py_LIMITED_API
+# if PY_VERSION_HEX >= 0x030A0000
+ immutableType = (obj->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) != 0;
+ if (immutableType)
+ str << " [immutabletype]";
+ if (obj->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)
+ str << " [disallow_instantiation]";
+# ifndef Py_LIMITED_API
+ if (obj->tp_flags & Py_TPFLAGS_MAPPING)
+ str << " [mapping]";
+ if (obj->tp_flags & Py_TPFLAGS_SEQUENCE)
+ str << " [sequence]";
+# endif // !Py_LIMITED_API
+# endif // 3.10
+ if (obj->tp_basicsize != 0)
+ str << ", basicsize=" << obj->tp_basicsize;
+ if (verbose) {
+ formatTypeTuple(obj->tp_bases, "bases", str);
+ formatTypeTuple(obj->tp_mro, "mro", str);
+ if (!immutableType) {
+ auto *underlying = reinterpret_cast<const PyObject *>(obj)->ob_type;
+ if (underlying != nullptr && underlying != obj) {
+ str << ", underlying=\"" << underlying->tp_name << '"';
+ }
+ }
+ }
+ }
+}
+
+static void formatTypeTuple(PyObject *t, const char *what, std::ostream &str)
+{
+ const Py_ssize_t size = t != nullptr && PyTuple_Check(t) != 0 ? PyTuple_Size(t) : 0;
+ if (size > 0) {
+ str << ", " << what << "=[" << size << "]{";
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ if (i != 0)
+ str << ", ";
+ Shiboken::AutoDecRef item(PyTuple_GetItem(t, i));
+ if (item.isNull())
+ str << '0'; // Observed with non-ready types
+ else
+ str << '"' << reinterpret_cast<PyTypeObject *>(item.object())->tp_name << '"';
+ }
+ str << '}';
}
}
@@ -110,24 +158,224 @@ static void formatPySequence(PyObject *obj, std::ostream &str)
str << '>';
}
-static void formatPyObject(PyObject *obj, std::ostream &str)
+static void formatPyTuple(PyObject *obj, std::ostream &str)
+{
+
+ const Py_ssize_t size = PyTuple_Size(obj);
+ str << size << " <";
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ if (i)
+ str << ", ";
+ str << '(';
+ PyObject *item = PyTuple_GetItem(obj, i);
+ formatPyObject(item, str);
+ str << ')';
+ Py_XDECREF(item);
+ }
+ str << '>';
+}
+
+static void formatPyDict(PyObject *obj, std::ostream &str)
{
- if (obj) {
- formatPyTypeObject(obj->ob_type, str);
- str << ", ";
- if (PyLong_Check(obj))
- str << PyLong_AsLong(obj);
- else if (PyFloat_Check(obj))
- str << PyFloat_AsDouble(obj);
- else if (PyUnicode_Check(obj))
- str << '"' << _PepUnicode_AsString(obj) << '"';
- else if (PySequence_Check(obj))
- formatPySequence(obj, str);
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ str << '{';
+ while (PyDict_Next(obj, &pos, &key, &value) != 0) {
+ if (pos)
+ str << ", ";
+ str << Shiboken::debugPyObject(key) << '=' << Shiboken::debugPyObject(value);
+ }
+ str << '}';
+}
+
+// Helper to format a 0-terminated character sequence
+template <class Char>
+static void formatCharSequence(const Char *s, std::ostream &str)
+{
+ str << '"';
+ const auto oldFillC = str.fill('0');
+ str << std::hex;
+ for (; *s; ++s) {
+ const unsigned c = *s;
+ if (c < 127)
+ str << char(c);
else
- str << "<unknown>";
+ str << "0x" << std::right << std::setw(sizeof(Char) * 2) << c << std::left;
+ }
+ str << std::dec;
+ str.fill(oldFillC);
+ str << '"';
+}
+
+static void formatPyUnicode(PyObject *obj, std::ostream &str)
+{
+ // Note: The below call create the PyCompactUnicodeObject.utf8 representation
+ str << '"' << _PepUnicode_AsString(obj) << '"';
+ if (!verbose)
+ return;
+
+ str << " (" << PyUnicode_GetLength(obj) << ')';
+ const auto kind = _PepUnicode_KIND(obj);
+ switch (kind) {
+#if PY_VERSION_HEX < 0x030C0000
+ case PepUnicode_WCHAR_KIND:
+ str << " [wchar]";
+ break;
+#endif
+ case PepUnicode_1BYTE_KIND:
+ str << " [1byte]";
+ break;
+ case PepUnicode_2BYTE_KIND:
+ str << " [2byte]";
+ break;
+ case PepUnicode_4BYTE_KIND:
+ str << " [4byte]";
+ break;
+ }
+
+ const bool ascii = _PepUnicode_IS_ASCII(obj);
+ if (ascii)
+ str << " [ascii]";
+ const bool compact = _PepUnicode_IS_COMPACT(obj);
+ if (compact)
+ str << " [compact]";
+ void *data =_PepUnicode_DATA(obj);
+ str << ", data=";
+ switch (kind) {
+#if PY_VERSION_HEX < 0x030C0000
+ case PepUnicode_WCHAR_KIND:
+ formatCharSequence(reinterpret_cast<const wchar_t *>(data), str);
+#endif
+ break;
+ case PepUnicode_1BYTE_KIND:
+ formatCharSequence(reinterpret_cast<const Py_UCS1 *>(data), str);
+ break;
+ case PepUnicode_2BYTE_KIND:
+ formatCharSequence(reinterpret_cast<const Py_UCS2 *>(data), str);
+ break;
+ case PepUnicode_4BYTE_KIND:
+ formatCharSequence(reinterpret_cast<const Py_UCS4 *>(data), str);
+ break;
+ }
+
+#ifndef Py_LIMITED_API
+ const char *utf8 = nullptr;
+ if (!ascii && compact && kind == PepUnicode_1BYTE_KIND) {
+ const auto *compactObj = reinterpret_cast<const PyCompactUnicodeObject *>(obj);
+ if (compactObj->utf8_length)
+ utf8 = compactObj->utf8;
+ }
+ if (utf8) {
+ str << ", utf8=";
+ formatCharSequence(reinterpret_cast<const Py_UCS1 *>(utf8), str);
} else {
- str << '0';
+ str << ", no-utf8";
}
+#endif // !Py_LIMITED_API
+}
+
+static std::string getQualName(PyObject *obj)
+{
+ Shiboken::AutoDecRef result(PyObject_GetAttr(obj, Shiboken::PyMagicName::qualname()));
+ return result.object() != nullptr
+ ? _PepUnicode_AsString(result.object()) : std::string{};
+}
+
+static void formatPyFunction(PyObject *obj, std::ostream &str)
+{
+ str << '"' << getQualName(obj) << "()\"";
+}
+
+static void formatPyMethod(PyObject *obj, std::ostream &str)
+{
+ if (auto *func = PyMethod_Function(obj))
+ formatPyFunction(func, str);
+ str << ", instance=" << PyMethod_Self(obj);
+}
+
+static void formatPyCodeObject(PyObject *obj, std::ostream &str)
+{
+ if (auto name = getStringAttr(obj, "co_name"))
+ str << '"' << name.value() << '"';
+ if (auto qualName = getStringAttr(obj, "co_qualname"))
+ str << ", co_qualname=\"" << qualName.value() << '"';
+ if (auto flags = getIntAttr(obj, "co_flags"))
+ str << ", flags=0x" << std::hex << flags.value() << std::dec;
+ if (auto c = getIntAttr(obj, "co_argcount"))
+ str << ", co_argcounts=" << c.value();
+ if (auto c = getIntAttr(obj, "co_posonlyargcount"))
+ str << ", co_posonlyargcount=" << c.value();
+ if (auto c = getIntAttr(obj, "co_kwonlyargcount"))
+ str << ", co_kwonlyargcount=" << c.value();
+ if (auto fileName = getStringAttr(obj, "co_filename")) {
+ str << " @" << fileName.value();
+ if (auto l = getIntAttr(obj, "co_firstlineno"))
+ str << ':'<< l.value();
+ }
+}
+
+static void formatPyObjectHelper(PyObject *obj, std::ostream &str)
+{
+ str << ", ";
+ if (obj == Py_None) {
+ str << "None";
+ return;
+ }
+ if (obj == Py_True) {
+ str << "True";
+ return;
+ }
+ if (obj == Py_False) {
+ str << "False";
+ return;
+ }
+ const auto refs = Py_REFCNT(obj);
+ if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT
+ str << "immortal, ";
+ else
+ str << "refs=" << refs << ", ";
+ if (PyType_Check(obj)) {
+ str << "type: ";
+ formatPyTypeObject(reinterpret_cast<PyTypeObject *>(obj), str, true);
+ return;
+ }
+ formatPyTypeObject(obj->ob_type, str, false);
+ str << ", ";
+ if (PyLong_Check(obj)) {
+ const auto llv = PyLong_AsLongLong(obj);
+ if (PyErr_Occurred() != PyExc_OverflowError) {
+ str << llv;
+ } else {
+ PyErr_Clear();
+ str << "0x" << std::hex << PyLong_AsUnsignedLongLong(obj) << std::dec;
+ }
+ }
+ else if (PyFloat_Check(obj))
+ str << PyFloat_AsDouble(obj);
+ else if (PyUnicode_Check(obj))
+ formatPyUnicode(obj, str);
+ else if (PyFunction_Check(obj) != 0)
+ formatPyFunction(obj, str);
+ else if (PyMethod_Check(obj) != 0)
+ formatPyMethod(obj, str);
+ else if (PepCode_Check(obj) != 0)
+ formatPyCodeObject(obj, str);
+ else if (PySequence_Check(obj))
+ formatPySequence(obj, str);
+ else if (PyDict_Check(obj))
+ formatPyDict(obj, str);
+ else if (PyTuple_CheckExact(obj))
+ formatPyTuple(obj, str);
+ else
+ str << "<unknown>";
+}
+
+static void formatPyObject(PyObject *obj, std::ostream &str)
+{
+ str << obj;
+ if (obj)
+ formatPyObjectHelper(obj, str);
}
namespace Shiboken
@@ -137,6 +385,10 @@ debugPyObject::debugPyObject(PyObject *o) : m_object(o)
{
}
+debugSbkObject::debugSbkObject(SbkObject *o) : m_object(o)
+{
+}
+
debugPyTypeObject::debugPyTypeObject(const PyTypeObject *o) : m_object(o)
{
}
@@ -148,7 +400,18 @@ debugPyBuffer::debugPyBuffer(const Py_buffer &b) : m_buffer(b)
std::ostream &operator<<(std::ostream &str, const debugPyTypeObject &o)
{
str << "PyTypeObject(";
- formatPyTypeObject(o.m_object, str);
+ formatPyTypeObject(o.m_object, str, true);
+ str << ')';
+ return str;
+}
+
+std::ostream &operator<<(std::ostream &str, const debugSbkObject &o)
+{
+ str << "SbkObject(" << o.m_object;
+ if (o.m_object) {
+ Shiboken::Object::_debugFormat(str, o.m_object);
+ formatPyObjectHelper(reinterpret_cast<PyObject *>(o.m_object), str);
+ }
str << ')';
return str;
}
@@ -173,6 +436,18 @@ std::ostream &operator<<(std::ostream &str, const debugPyBuffer &b)
return str;
}
+std::ios_base &debugVerbose(std::ios_base &s)
+{
+ verbose = true;
+ return s;
+}
+
+std::ios_base &debugBrief(std::ios_base &s)
+{
+ verbose = false;
+ return s;
+}
+
#ifdef _WIN32
// Converts a Unicode string to a string encoded in the Windows console's
// code page via wchar_t for use with argv (PYSIDE-1425).
@@ -255,12 +530,12 @@ int *sequenceToIntArray(PyObject *obj, bool zeroTerminated)
for (int i = 0; i < size; i++) {
PyObject *item = PySequence_Fast_GET_ITEM(seq.object(), i);
- if (!PyInt_Check(item)) {
+ if (!PyLong_Check(item)) {
PyErr_SetString(PyExc_TypeError, "Sequence of ints expected");
delete[] array;
return nullptr;
}
- array[i] = PyInt_AsLong(item);
+ array[i] = PyLong_AsLong(item);
}
if (zeroTerminated)
@@ -316,4 +591,47 @@ ThreadId mainThreadId()
return _mainThreadId;
}
+const char *typeNameOf(const char *typeIdName)
+{
+ auto size = std::strlen(typeIdName);
+#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64"
+ if (auto *lastStar = strchr(typeName, '*')) {
+ // MSVC: "class QPaintDevice * __ptr64"
+ while (*--lastStar == ' ') {
+ }
+ size = lastStar - typeName + 1;
+ }
+#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice"
+ if (size > 2 && typeIdName[0] == 'P' && std::isdigit(typeIdName[1])) {
+ ++typeIdName;
+ --size;
+ }
+#endif
+ char *result = new char[size + 1];
+ result[size] = '\0';
+ std::memcpy(result, typeIdName, size);
+ return result;
+}
+
+#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
+static int _getPyVerbose()
+{
+ PyConfig config;
+ PyConfig_InitPythonConfig(&config);
+ return config.verbose;
+}
+#endif // !Py_LIMITED_API >= 3.10
+
+int pyVerbose()
+{
+#ifdef Py_LIMITED_API
+ return Pep_GetVerboseFlag();
+#elif PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
+ static const int result = _getPyVerbose();
+ return result;
+#else
+ return Py_VerboseFlag;
+#endif
+}
+
} // namespace Shiboken