aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/common')
-rw-r--r--src/qml/common/common.pri35
-rw-r--r--src/qml/common/qjsnumbercoercion.cpp62
-rw-r--r--src/qml/common/qjsnumbercoercion.h120
-rw-r--r--src/qml/common/qqmlapiversion_p.h56
-rw-r--r--src/qml/common/qqmljsdiagnosticmessage_p.h48
-rw-r--r--src/qml/common/qqmljsfixedpoolarray_p.h46
-rw-r--r--src/qml/common/qqmljsmemorypool_p.h66
-rw-r--r--src/qml/common/qqmljssourcelocation_p.h107
-rw-r--r--src/qml/common/qqmlsignalnames.cpp257
-rw-r--r--src/qml/common/qqmlsignalnames_p.h59
-rw-r--r--src/qml/common/qqmltranslation.cpp125
-rw-r--r--src/qml/common/qqmltranslation_p.h74
-rw-r--r--src/qml/common/qv4alloca_p.h78
-rw-r--r--src/qml/common/qv4calldata_p.h42
-rw-r--r--src/qml/common/qv4compileddata.cpp431
-rw-r--r--src/qml/common/qv4compileddata_p.h1110
-rw-r--r--src/qml/common/qv4staticvalue_p.h612
-rw-r--r--src/qml/common/qv4stringtoarrayindex_p.h46
18 files changed, 2466 insertions, 908 deletions
diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri
deleted file mode 100644
index bcc3ea0fa0..0000000000
--- a/src/qml/common/common.pri
+++ /dev/null
@@ -1,35 +0,0 @@
-!build_pass {
- # Create a header containing a hash that describes this library. For a
- # released version of Qt, we'll use the .tag file that is updated by git
- # archive with the commit hash. For unreleased versions, we'll ask git
- # describe. Note that it won't update unless qmake is run again, even if
- # the commit change also changed something in this library.
- tagFile = $$PWD/../../.tag
- tag =
- exists($$tagFile) {
- tag = $$cat($$tagFile, singleline)
- QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile
- }
- !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
- QML_COMPILE_HASH = $$tag
- } else:exists($$PWD/../../.git) {
- commit = $$system(git rev-parse HEAD)
- QML_COMPILE_HASH = $$commit
- }
- compile_hash_contents = \
- "// Generated file, DO NOT EDIT" \
- "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \
- "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)"
- write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error()
-}
-
-HEADERS += \
- $$PWD/qqmlapiversion_p.h \
- $$PWD/qqmljsdiagnosticmessage_p.h \
- $$PWD/qqmljsfixedpoolarray_p.h \
- $$PWD/qqmljsmemorypool_p.h \
- $$PWD/qv4alloca_p.h \
- $$PWD/qv4calldata_p.h \
- $$PWD/qv4compileddata_p.h \
- $$PWD/qv4staticvalue_p.h \
- $$PWD/qv4stringtoarrayindex_p.h
diff --git a/src/qml/common/qjsnumbercoercion.cpp b/src/qml/common/qjsnumbercoercion.cpp
new file mode 100644
index 0000000000..8cd96a4e25
--- /dev/null
+++ b/src/qml/common/qjsnumbercoercion.cpp
@@ -0,0 +1,62 @@
+// 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 "qjsnumbercoercion.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \since 6.1
+ \class QJSNumberCoercion
+ \internal
+
+ \brief Implements the JavaScript double-to-int coercion.
+ */
+
+/*!
+ \fn bool QJSNumberCoercion::isInteger(double d)
+ \internal
+ \deprecated 6.7
+ */
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(double d)
+ \internal
+
+ Checks whether \a d contains a value that can serve as an index into an array.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
+ */
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(qint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
+*/
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(quint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a value representable as an unsigned 32bit int.
+*/
+
+/*!
+ \fn int QJSNumberCoercion::toInteger(double d)
+ \internal
+
+ Coerces the given \a d to a 32bit integer by JavaScript rules and returns
+ the result.
+ */
+
+/*!
+ \fn bool equals(double lhs, double rhs)
+ \internal
+
+ Compares \a lhs and \a rhs bit by bit without causing a compile warning.
+ Returns the \c true if they are equal, or \c false if not.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h
new file mode 100644
index 0000000000..0023bff6e8
--- /dev/null
+++ b/src/qml/common/qjsnumbercoercion.h
@@ -0,0 +1,120 @@
+// 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
+
+#ifndef QJSNUMBERCOERCION_H
+#define QJSNUMBERCOERCION_H
+
+#include <QtCore/qglobal.h>
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+class QJSNumberCoercion
+{
+public:
+
+ static constexpr bool isInteger(double d)
+ {
+ // Comparing d with itself checks for NaN and comparing d with the min and max values
+ // for int also covers infinities.
+ if (!equals(d, d) || d < (std::numeric_limits<int>::min)()
+ || d > (std::numeric_limits<int>::max)()) {
+ return false;
+ }
+
+ return equals(static_cast<int>(d), d);
+ }
+
+ static constexpr bool isArrayIndex(double d)
+ {
+ return d >= 0
+ && equals(d, d)
+ && d <= (std::numeric_limits<uint>::max)()
+ && equals(static_cast<uint>(d), d);
+ }
+
+ static constexpr bool isArrayIndex(qint64 i)
+ {
+ return i >= 0 && i <= (std::numeric_limits<uint>::max)();
+ }
+
+ static constexpr bool isArrayIndex(quint64 i)
+ {
+ return i <= (std::numeric_limits<uint>::max)();
+ }
+
+ static constexpr int toInteger(double d) {
+ // Check for NaN
+ if (!equals(d, d))
+ return 0;
+
+ if (d >= (std::numeric_limits<int>::min)() && d <= (std::numeric_limits<int>::max)()) {
+ const int i = static_cast<int>(d);
+ if (equals(i, d))
+ return i;
+ }
+
+ return QJSNumberCoercion(d).toInteger();
+ }
+
+ static constexpr bool equals(double lhs, double rhs)
+ {
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_FLOAT_COMPARE
+ return lhs == rhs;
+ QT_WARNING_POP
+ }
+
+private:
+ constexpr QJSNumberCoercion(double dbl)
+ {
+ // the dbl == 0 path is guaranteed constexpr. The other one may or may not be, depending
+ // on whether and how the compiler inlines the memcpy.
+ // In order to declare the ctor constexpr we need one guaranteed constexpr path.
+ if (!equals(dbl, 0))
+ memcpy(&d, &dbl, sizeof(double));
+ }
+
+ constexpr int sign() const
+ {
+ return (d >> 63) ? -1 : 1;
+ }
+
+ constexpr bool isDenormal() const
+ {
+ return static_cast<int>((d << 1) >> 53) == 0;
+ }
+
+ constexpr int exponent() const
+ {
+ return static_cast<int>((d << 1) >> 53) - 1023;
+ }
+
+ constexpr quint64 significant() const
+ {
+ quint64 m = (d << 12) >> 12;
+ if (!isDenormal())
+ m |= (static_cast<quint64>(1) << 52);
+ return m;
+ }
+
+ constexpr int toInteger()
+ {
+ int e = exponent() - 52;
+ if (e < 0) {
+ if (e <= -53)
+ return 0;
+ return sign() * static_cast<int>(significant() >> -e);
+ } else {
+ if (e > 31)
+ return 0;
+ return sign() * (static_cast<int>(significant()) << e);
+ }
+ }
+
+ quint64 d = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QJSNUMBERCOERCION_H
diff --git a/src/qml/common/qqmlapiversion_p.h b/src/qml/common/qqmlapiversion_p.h
deleted file mode 100644
index 576619c518..0000000000
--- a/src/qml/common/qqmlapiversion_p.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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$
-**
-****************************************************************************/
-
-#ifndef QQMLAPIVERSION_P_H
-#define QQMLAPIVERSION_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#define Q_QML_PRIVATE_API_VERSION 6
-
-#endif // QQMLAPIVERSION_P_H
diff --git a/src/qml/common/qqmljsdiagnosticmessage_p.h b/src/qml/common/qqmljsdiagnosticmessage_p.h
index 763332ba76..0e7adbc3be 100644
--- a/src/qml/common/qqmljsdiagnosticmessage_p.h
+++ b/src/qml/common/qqmljsdiagnosticmessage_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2019 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
#ifndef QQMLJSDIAGNOSTICMESSAGE_P_H
#define QQMLJSDIAGNOSTICMESSAGE_P_H
@@ -56,7 +20,8 @@
// Include the API version here, to avoid complications when querying it for the
// QQmlSourceLocation -> line/column change.
-#include <private/qqmlapiversion_p.h>
+
+#include "qqmljssourcelocation_p.h"
QT_BEGIN_NAMESPACE
@@ -65,8 +30,7 @@ struct DiagnosticMessage
{
QString message;
QtMsgType type = QtCriticalMsg;
- quint32 line = 0;
- quint32 column = 0;
+ SourceLocation loc;
bool isError() const
{
@@ -85,7 +49,7 @@ struct DiagnosticMessage
};
} // namespace QQmlJS
-Q_DECLARE_TYPEINFO(QQmlJS::DiagnosticMessage, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlJS::DiagnosticMessage, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
diff --git a/src/qml/common/qqmljsfixedpoolarray_p.h b/src/qml/common/qqmljsfixedpoolarray_p.h
index b65b994d6c..e946b1ecfd 100644
--- a/src/qml/common/qqmljsfixedpoolarray_p.h
+++ b/src/qml/common/qqmljsfixedpoolarray_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2016 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
#ifndef QQMLJSFIXEDPOOLARRAY_P_H
#define QQMLJSFIXEDPOOLARRAY_P_H
@@ -80,13 +44,13 @@ public:
void allocate(MemoryPool *pool, const QVector<T> &vector)
{
- count = vector.count();
+ count = vector.size();
data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
if (QTypeInfo<T>::isComplex) {
for (int i = 0; i < count; ++i)
new (data + i) T(vector.at(i));
- } else {
+ } else if (count) {
memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
}
}
@@ -94,7 +58,7 @@ public:
template <typename Container>
void allocate(MemoryPool *pool, const Container &container)
{
- count = container.count();
+ count = container.size();
data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
typename Container::ConstIterator it = container.constBegin();
for (int i = 0; i < count; ++i)
diff --git a/src/qml/common/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h
index 0cf7ea84e6..6f0f8ed491 100644
--- a/src/qml/common/qqmljsmemorypool_p.h
+++ b/src/qml/common/qqmljsmemorypool_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2021 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
#ifndef QQMLJSMEMORYPOOL_P_H
#define QQMLJSMEMORYPOOL_P_H
@@ -51,11 +15,11 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
-#include <QtCore/qshareddata.h>
-#include <QtCore/qdebug.h>
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qvector.h>
-#include <cstring>
+#include <cstdlib>
QT_BEGIN_NAMESPACE
@@ -63,14 +27,12 @@ namespace QQmlJS {
class Managed;
-class MemoryPool : public QSharedData
+class MemoryPool
{
- MemoryPool(const MemoryPool &other);
- void operator =(const MemoryPool &other);
+ Q_DISABLE_COPY_MOVE(MemoryPool);
public:
- MemoryPool() {}
-
+ MemoryPool() = default;
~MemoryPool()
{
if (_blocks) {
@@ -81,13 +43,12 @@ public:
free(_blocks);
}
- qDeleteAll(strings);
}
inline void *allocate(size_t size)
{
size = (size + 7) & ~size_t(7);
- if (Q_LIKELY(_ptr && (_ptr + size < _end))) {
+ if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) {
void *addr = _ptr;
_ptr += size;
return addr;
@@ -105,9 +66,8 @@ public:
template <typename Tp, typename... Ta> Tp *New(Ta... args)
{ return new (this->allocate(sizeof(Tp))) Tp(args...); }
- QStringRef newString(const QString &string) {
- strings.append(new QString(string));
- return QStringRef(strings.last());
+ QStringView newString(QString string) {
+ return strings.emplace_back(std::move(string));
}
private:
@@ -151,7 +111,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
- QVector<QString*> strings;
+ QStringList strings;
enum
{
diff --git a/src/qml/common/qqmljssourcelocation_p.h b/src/qml/common/qqmljssourcelocation_p.h
new file mode 100644
index 0000000000..9a007e4ac5
--- /dev/null
+++ b/src/qml/common/qqmljssourcelocation_p.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2021 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
+
+#ifndef QQMLJSSOURCELOCATION_P_H
+#define QQMLJSSOURCELOCATION_P_H
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qhashfunctions.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+class SourceLocation
+{
+public:
+ explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0)
+ : offset(offset), length(length),
+ startLine(line), startColumn(column)
+ { }
+
+ bool isValid() const { return *this != SourceLocation(); }
+
+ quint32 begin() const { return offset; }
+ quint32 end() const { return offset + length; }
+
+ // Returns a zero length location at the start of the current one.
+ SourceLocation startZeroLengthLocation() const
+ {
+ return SourceLocation(offset, 0, startLine, startColumn);
+ }
+ // Returns a zero length location at the end of the current one.
+ SourceLocation endZeroLengthLocation(QStringView text) const
+ {
+ quint32 i = offset;
+ quint32 endLine = startLine;
+ quint32 endColumn = startColumn;
+ while (i < end()) {
+ QChar c = text.at(i);
+ switch (c.unicode()) {
+ case '\n':
+ if (i + 1 < end() && text.at(i + 1) == QLatin1Char('\r'))
+ ++i;
+ Q_FALLTHROUGH();
+ case '\r':
+ ++endLine;
+ endColumn = 1;
+ break;
+ default:
+ ++endColumn;
+ }
+ ++i;
+ }
+ return SourceLocation(offset + length, 0, endLine, endColumn);
+ }
+
+// attributes
+ // ### encode
+ quint32 offset;
+ quint32 length;
+ quint32 startLine;
+ quint32 startColumn;
+
+ friend size_t qHash(const SourceLocation &location, size_t seed = 0)
+ {
+ return qHashMulti(seed, location.offset, location.length,
+ location.startLine, location.startColumn);
+ }
+
+ friend bool operator==(const SourceLocation &a, const SourceLocation &b)
+ {
+ return a.offset == b.offset && a.length == b.length
+ && a.startLine == b.startLine && a.startColumn == b.startColumn;
+ }
+
+ friend bool operator!=(const SourceLocation &a, const SourceLocation &b) { return !(a == b); }
+
+ // Returns a source location starting at the beginning of l1, l2 and ending at the end of them.
+ // Ignores invalid source locations.
+ friend SourceLocation combine(const SourceLocation &l1, const SourceLocation &l2) {
+ quint32 e = qMax(l1.end(), l2.end());
+ SourceLocation res;
+ if (l1.offset <= l2.offset)
+ res = (l1.isValid() ? l1 : l2);
+ else
+ res = (l2.isValid() ? l2 : l1);
+ res.length = e - res.offset;
+ return res;
+ }
+};
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/common/qqmlsignalnames.cpp b/src/qml/common/qqmlsignalnames.cpp
new file mode 100644
index 0000000000..d2a23205a6
--- /dev/null
+++ b/src/qml/common/qqmlsignalnames.cpp
@@ -0,0 +1,257 @@
+// Copyright (C) 2023 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 "qqmlsignalnames_p.h"
+#include <iterator>
+#include <algorithm>
+#include <optional>
+#include <string>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::Literals;
+
+static constexpr const QLatin1String On("on");
+static constexpr const QLatin1String Changed("Changed");
+
+static constexpr const qsizetype StrlenOn = On.length();
+static constexpr const qsizetype StrlenChanged = Changed.length();
+
+static std::optional<qsizetype> firstLetterIdx(QStringView name, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ auto end = std::prev(name.cend(), removeSuffix);
+ auto result = std::find_if(std::next(name.cbegin(), removePrefix), end,
+ [](const QChar &c) { return c.isLetter(); });
+ if (result != end)
+ return std::distance(name.begin(), result);
+
+ return {};
+}
+
+static std::optional<QChar> firstLetter(QStringView name, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ if (auto idx = firstLetterIdx(name, removePrefix, removeSuffix))
+ return name[*idx];
+ return {};
+}
+
+enum ChangeCase { ToUpper, ToLower };
+static void changeCaseOfFirstLetter(QString &name, ChangeCase option, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ auto idx = firstLetterIdx(name, removePrefix, removeSuffix);
+ if (!idx)
+ return;
+
+ QChar &changeMe = name[*idx];
+ changeMe = option == ToUpper ? changeMe.toUpper() : changeMe.toLower();
+};
+
+static std::optional<QString> toQStringData(std::optional<QStringView> view)
+{
+ if (view)
+ return view->toString();
+ return std::nullopt;
+}
+
+static QByteArray toUtf8Data(QUtf8StringView view)
+{
+ return QByteArray(view.data(), view.size());
+}
+
+static std::optional<QByteArray> toUtf8Data(std::optional<QUtf8StringView> view)
+{
+ if (view)
+ return toUtf8Data(*view);
+ return std::nullopt;
+}
+
+/*!
+\internal
+\class QQmlSignalNames
+
+QQmlSignalNames contains a list of helper methods to manipulate signal names.
+Always try to use the most specific one, as combining them might lead to incorrect
+results like wrong upper/lower case, for example.
+*/
+
+/*!
+\internal
+Concatenate a prefix to a property name and uppercases the first letter of the property name.
+*/
+QString QQmlSignalNames::addPrefixToPropertyName(QStringView prefix, QStringView propertyName)
+{
+ QString result = prefix.toString().append(propertyName);
+ changeCaseOfFirstLetter(result, ToUpper, prefix.size());
+ return result;
+}
+
+QString QQmlSignalNames::propertyNameToChangedSignalName(QStringView property)
+{
+ return property.toString().append(Changed);
+}
+
+QByteArray QQmlSignalNames::propertyNameToChangedSignalName(QUtf8StringView property)
+{
+ return toUtf8Data(property).append(QByteArrayView(Changed));
+}
+
+QString QQmlSignalNames::propertyNameToChangedHandlerName(QStringView property)
+{
+ return propertyNameToChangedSignalName(signalNameToHandlerName(property));
+}
+
+template<typename View>
+std::optional<View> changedSignalNameToPropertyNameTemplate(View changeSignal)
+{
+ const qsizetype changeSignalSize = changeSignal.size();
+ if (changeSignalSize < StrlenChanged || changeSignal.last(StrlenChanged).compare(Changed) != 0)
+ return std::nullopt;
+
+ const View result = changeSignal.sliced(0, changeSignalSize - StrlenChanged);
+ if (!result.isEmpty())
+ return result;
+
+ return {};
+}
+
+/*!
+\internal
+Obtain a propertyName from its changed signal handler.
+Do not call this on a value obtained from handlerNameToSignalName! Instead use
+changedHandlerNameToPropertyName() directly. Otherwise you might end up with a wrong
+capitalization of _Changed for "on_Changed", for example.
+*/
+
+std::optional<QString> QQmlSignalNames::changedSignalNameToPropertyName(QStringView signalName)
+{
+ return toQStringData(changedSignalNameToPropertyNameTemplate(signalName));
+}
+std::optional<QByteArray>
+QQmlSignalNames::changedSignalNameToPropertyName(QUtf8StringView signalName)
+{
+ return toUtf8Data(changedSignalNameToPropertyNameTemplate(signalName));
+}
+
+/*!
+\internal
+Returns a property name from \a changedHandler.
+This fails for property names starting with an upper-case letter, as it will lower-case it in the
+process.
+*/
+std::optional<QString> QQmlSignalNames::changedHandlerNameToPropertyName(QStringView handler)
+{
+ if (!isChangedHandlerName(handler))
+ return {};
+
+ if (auto withoutChangedSuffix = changedSignalNameToPropertyName(handler)) {
+ return handlerNameToSignalName(*withoutChangedSuffix);
+ }
+ return {};
+}
+
+QString QQmlSignalNames::signalNameToHandlerName(QAnyStringView signal)
+{
+ QString handlerName;
+ handlerName.reserve(StrlenOn + signal.length());
+ handlerName.append(On);
+
+ signal.visit([&handlerName](auto &&s) { handlerName.append(s); });
+
+ changeCaseOfFirstLetter(handlerName, ToUpper, StrlenOn);
+ return handlerName;
+}
+
+enum HandlerType { ChangedHandler, Handler };
+
+template<HandlerType type>
+static std::optional<QString> handlerNameToSignalNameHelper(QStringView handler)
+{
+ if (!QQmlSignalNames::isHandlerName(handler))
+ return {};
+
+ QString signalName = handler.sliced(StrlenOn).toString();
+ Q_ASSERT(!signalName.isEmpty());
+
+ changeCaseOfFirstLetter(signalName, ToLower, 0, type == ChangedHandler ? StrlenChanged : 0);
+ return signalName;
+}
+
+/*!
+\internal
+Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
+changedHandlerNameToSignalName for that!
+*/
+std::optional<QString> QQmlSignalNames::handlerNameToSignalName(QStringView handler)
+{
+ return handlerNameToSignalNameHelper<Handler>(handler);
+}
+
+/*!
+\internal
+Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
+changedHandlerNameToSignalName for that! Accepts improperly capitalized handler names and
+incorrectly resolves signal names that start with '_' or '$'.
+*/
+std::optional<QString> QQmlSignalNames::badHandlerNameToSignalName(QStringView handler)
+{
+ if (handler.size() <= StrlenOn || !handler.startsWith(On))
+ return {};
+
+ QString signalName = handler.sliced(StrlenOn).toString();
+
+ // This is quite wrong. But we need it for backwards compatibility.
+ signalName.front() = signalName.front().toLower();
+
+ return signalName;
+}
+
+/*!
+\internal
+Returns a signal name from \a changedHandlerName string. Makes sure not to lowercase the 'C' from
+Changed.
+*/
+std::optional<QString> QQmlSignalNames::changedHandlerNameToSignalName(QStringView handler)
+{
+ return handlerNameToSignalNameHelper<ChangedHandler>(handler);
+}
+
+bool QQmlSignalNames::isChangedSignalName(QStringView signalName)
+{
+ if (signalName.size() <= StrlenChanged || !signalName.endsWith(Changed))
+ return false;
+
+ if (auto letter = firstLetter(signalName, 0, StrlenChanged))
+ return letter->isLower();
+
+ return true;
+}
+
+bool QQmlSignalNames::isChangedHandlerName(QStringView signalName)
+{
+ if (signalName.size() <= (StrlenOn + StrlenChanged)
+ || !signalName.startsWith(On)
+ || !signalName.endsWith(Changed)) {
+ return false;
+ }
+
+ if (auto letter = firstLetter(signalName, StrlenOn, StrlenChanged))
+ return letter->isUpper();
+
+ return true;
+}
+
+bool QQmlSignalNames::isHandlerName(QStringView signalName)
+{
+ if (signalName.size() <= StrlenOn || !signalName.startsWith(On))
+ return false;
+
+ if (auto letter = firstLetter(signalName, StrlenOn))
+ return letter->isUpper();
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qqmlsignalnames_p.h b/src/qml/common/qqmlsignalnames_p.h
new file mode 100644
index 0000000000..9c3d192666
--- /dev/null
+++ b/src/qml/common/qqmlsignalnames_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 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
+
+#ifndef QQMLSIGNALANDPROPERTYNAMES_P_H
+#define QQMLSIGNALANDPROPERTYNAMES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <cstddef>
+#include <optional>
+
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtCore/qstringview.h>
+#include <QtCore/qstring.h>
+#include <type_traits>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_EXPORT QQmlSignalNames
+{
+public:
+ static QString propertyNameToChangedSignalName(QStringView property);
+ static QByteArray propertyNameToChangedSignalName(QUtf8StringView property);
+
+ static QString propertyNameToChangedHandlerName(QStringView property);
+
+ static QString signalNameToHandlerName(QAnyStringView signal);
+
+ static std::optional<QString> changedSignalNameToPropertyName(QStringView changeSignal);
+ static std::optional<QByteArray> changedSignalNameToPropertyName(QUtf8StringView changeSignal);
+
+ static std::optional<QString> changedHandlerNameToPropertyName(QStringView handler);
+ static std::optional<QByteArray> changedHandlerNameToPropertyName(QUtf8StringView handler);
+
+ static std::optional<QString> handlerNameToSignalName(QStringView handler);
+ static std::optional<QString> changedHandlerNameToSignalName(QStringView changedHandler);
+
+ static bool isChangedHandlerName(QStringView signalName);
+ static bool isChangedSignalName(QStringView signalName);
+ static bool isHandlerName(QStringView signalName);
+
+ static QString addPrefixToPropertyName(QStringView prefix, QStringView propertyName);
+
+ // ### Qt7: remove this
+ static std::optional<QString> badHandlerNameToSignalName(QStringView handler);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSIGNALANDPROPERTYNAMES_P_H
diff --git a/src/qml/common/qqmltranslation.cpp b/src/qml/common/qqmltranslation.cpp
new file mode 100644
index 0000000000..7120071b1a
--- /dev/null
+++ b/src/qml/common/qqmltranslation.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2022 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 "private/qqmltranslation_p.h"
+
+QQmlTranslation::QQmlTranslation(const Data &d) : data(d) { }
+QQmlTranslation::QQmlTranslation() : data(nullptr) { }
+
+QString QQmlTranslation::translate() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::nullptr_t>)
+ return arg.translate();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QString QQmlTranslation::serializeForQmltc() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::nullptr_t>)
+ return arg.serializeForQmltc();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QString QQmlTranslation::idForQmlDebug() const
+{
+ return std::visit(
+ [](auto &&arg) -> QString {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (!std::is_same_v<T, std::nullptr_t>)
+ return arg.idForQmlDebug();
+ else {
+ Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
+ return {};
+ }
+ },
+ data);
+}
+
+QQmlTranslation::QsTrData::QsTrData(const QString &context, const QString &text,
+ const QString &comment, int number)
+ : context(context.toUtf8()), text(text.toUtf8()), comment(comment.toUtf8()), number(number)
+{
+}
+
+QString QQmlTranslation::contextFromQmlFilename(const QString &qmlFilename)
+{
+ int lastSlash = qmlFilename.lastIndexOf(QLatin1Char('/'));
+ QStringView contextView = (lastSlash > -1)
+ ? QStringView{ qmlFilename }.mid(lastSlash + 1, qmlFilename.size() - lastSlash - 5)
+ : QStringView();
+ return contextView.toString();
+}
+
+QString QQmlTranslation::QsTrData::translate() const
+{
+#if !QT_CONFIG(translation)
+ return QString();
+#else
+ return QCoreApplication::translate(context, text, comment, number);
+#endif
+}
+
+QString QQmlTranslation::QsTrData::idForQmlDebug() const
+{
+ return QString::fromUtf8(text);
+}
+
+QString QQmlTranslation::QsTrData::serializeForQmltc() const
+{
+ QString result = QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrData(
+ QStringLiteral("%1"),
+ QStringLiteral("%2"),
+ QStringLiteral("%3"),
+ %4)))")
+ .arg(QString::fromUtf8(context), QString::fromUtf8(text),
+ QString::fromUtf8(comment))
+ .arg(number);
+
+ return result;
+}
+
+QQmlTranslation::QsTrIdData::QsTrIdData(const QString &id, int number)
+ : id(id.toUtf8()), number(number)
+{
+}
+
+QString QQmlTranslation::QsTrIdData::translate() const
+{
+#if !QT_CONFIG(translation)
+ return QString();
+#else
+ return qtTrId(id, number);
+#endif
+}
+
+QString QQmlTranslation::QsTrIdData::serializeForQmltc() const
+{
+ QString result = QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrIdData(
+ QStringLiteral("%1"),
+ %4)))")
+ .arg(QString::fromUtf8(id))
+ .arg(number);
+
+ return result;
+}
+
+QString QQmlTranslation::QsTrIdData::idForQmlDebug() const
+{
+ return QString::fromUtf8(id);
+}
diff --git a/src/qml/common/qqmltranslation_p.h b/src/qml/common/qqmltranslation_p.h
new file mode 100644
index 0000000000..9849203abe
--- /dev/null
+++ b/src/qml/common/qqmltranslation_p.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2022 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
+
+#ifndef QQMLTRANSLATION_P_H
+#define QQMLTRANSLATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+
+#include <private/qv4qmlcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_EXPORT QQmlTranslation
+{
+public:
+ class Q_QML_EXPORT QsTrData
+ {
+ QByteArray context;
+ QByteArray text;
+ QByteArray comment;
+ int number;
+
+ public:
+ QsTrData(const QString &fileNameForContext, const QString &text, const QString &comment,
+ int number);
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+ };
+
+ class Q_QML_EXPORT QsTrIdData
+ {
+ QByteArray id;
+ int number;
+
+ public:
+ QsTrIdData(const QString &id, int number);
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+ };
+
+ // The static analyzer hates std::monostate in std::variant because
+ // that results in various uninitialized memory "problems". Just use
+ // std::nullptr_t to indicate "empty".
+ using Data = std::variant<std::nullptr_t, QsTrData, QsTrIdData>;
+
+private:
+ Data data;
+
+public:
+ QQmlTranslation(const Data &d);
+ QQmlTranslation();
+ QString translate() const;
+ QString serializeForQmltc() const;
+ QString idForQmlDebug() const;
+
+ static QString contextFromQmlFilename(const QString &qmlFilename);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTRANSLATION_P_H
diff --git a/src/qml/common/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h
index 65c3e4d65a..51c99192d3 100644
--- a/src/qml/common/qv4alloca_p.h
+++ b/src/qml/common/qv4alloca_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2016 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
#ifndef QV4_ALLOCA_H
#define QV4_ALLOCA_H
@@ -53,16 +17,17 @@
#include <QtCore/private/qglobal_p.h>
-#if QT_CONFIG(alloca_h)
+#include <stdlib.h>
+#if __has_include(<alloca.h>)
# include <alloca.h>
-#elif QT_CONFIG(alloca_malloc_h)
+#endif
+#if __has_include(<malloc.h>)
# include <malloc.h>
+#endif
+
+#ifdef Q_CC_MSVC
// This does not matter unless compiling in strict standard mode.
-# ifdef Q_CC_MSVC
-# define alloca _alloca
-# endif
-#else
-# include <stdlib.h>
+# define alloca _alloca
#endif
// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing
@@ -73,7 +38,7 @@
Q_ALLOCA_DECLARE(type, name); \
Q_ALLOCA_ASSIGN(type, name, size)
-#if QT_CONFIG(alloca)
+#ifdef alloca
#define Q_ALLOCA_DECLARE(type, name) \
type *name = 0
@@ -82,26 +47,17 @@
name = static_cast<type*>(alloca(size))
#else
-QT_BEGIN_NAMESPACE
-class Qt_AllocaWrapper
-{
-public:
- Qt_AllocaWrapper() { m_data = 0; }
- ~Qt_AllocaWrapper() { free(m_data); }
- void *data() { return m_data; }
- void allocate(int size) { m_data = malloc(size); memset(m_data, 0, size); }
-private:
- void *m_data;
-};
-QT_END_NAMESPACE
+# include <memory>
#define Q_ALLOCA_DECLARE(type, name) \
- Qt_AllocaWrapper _qt_alloca_##name; \
+ std::unique_ptr<char[]> _qt_alloca_##name; \
type *name = nullptr
#define Q_ALLOCA_ASSIGN(type, name, size) \
- _qt_alloca_##name.allocate(size); \
- name = static_cast<type*>(_qt_alloca_##name.data())
+ do { \
+ _qt_alloca_##name.reset(new char[size]); \
+ name = reinterpret_cast<type*>(_qt_alloca_##name.get()); \
+ } while (false)
#endif
diff --git a/src/qml/common/qv4calldata_p.h b/src/qml/common/qv4calldata_p.h
index 5a5280cb86..8d193bae4d 100644
--- a/src/qml/common/qv4calldata_p.h
+++ b/src/qml/common/qv4calldata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2019 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
#ifndef QV4CALLDATA_P_H
#define QV4CALLDATA_P_H
@@ -95,7 +59,7 @@ struct CallData
StaticValue args[1];
- static Q_DECL_CONSTEXPR int HeaderSize()
+ static constexpr int HeaderSize()
{
return offsetof(CallData, args) / sizeof(QV4::StaticValue);
}
diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp
new file mode 100644
index 0000000000..9dee91f713
--- /dev/null
+++ b/src/qml/common/qv4compileddata.cpp
@@ -0,0 +1,431 @@
+// Copyright (C) 2024 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 "qv4compileddata_p.h"
+
+#include <private/inlinecomponentutils_p.h>
+#include <private/qml_compile_hash_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmltypenamecache_p.h>
+#include <private/qv4resolvedtypereference_p.h>
+
+#include <QtQml/qqmlfile.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qstandardpaths.h>
+
+static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
+
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
+# ifdef Q_OS_LINUX
+// Place on a separate section on Linux so it's easier to check from outside
+// what the hash version is.
+__attribute__((section(".qml_compile_hash")))
+# endif
+const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
+ "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
+#else
+# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace CompiledData {
+
+
+bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
+{
+ if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
+
+ if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
+ .arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ return false;
+ }
+
+ if (qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
+ .arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ return false;
+ }
+
+ if (sourceTimeStamp) {
+ // Files from the resource system do not have any time stamps, so fall back to the application
+ // executable.
+ if (!expectedSourceTimeStamp.isValid())
+ expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
+
+ if (expectedSourceTimeStamp.isValid()
+ && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
+
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
+ if (qstrncmp(qml_compile_hash, libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
+ *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
+ .arg(QString::fromLatin1(
+ QByteArray(libraryVersionHash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()),
+ QString::fromLatin1(
+ QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()));
+ return false;
+ }
+#else
+#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+ return true;
+}
+
+/*!
+ \internal
+ This function creates a temporary key vector and sorts it to guarantuee a stable
+ hash. This is used to calculate a check-sum on dependent meta-objects.
+ */
+bool ResolvedTypeReferenceMap::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
+{
+ std::vector<int> keys (size());
+ int i = 0;
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ keys[i] = it.key();
+ ++i;
+ }
+ std::sort(keys.begin(), keys.end());
+ for (int key: keys) {
+ if (!this->operator[](key)->addToHash(hash, checksums))
+ return false;
+ }
+
+ return true;
+}
+
+CompilationUnit::CompilationUnit(
+ const Unit *unitData, const QString &fileName, const QString &finalUrlString)
+{
+ setUnitData(unitData, nullptr, fileName, finalUrlString);
+}
+
+CompilationUnit::~CompilationUnit()
+{
+ qDeleteAll(resolvedTypes);
+
+ if (data) {
+ if (data->qmlUnit() != qmlData)
+ free(const_cast<QmlUnit *>(qmlData));
+ qmlData = nullptr;
+
+ if (!(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ }
+ data = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+ constants = nullptr;
+#endif
+}
+
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH");
+
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix
+ = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = envCachePath.isEmpty()
+ ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ + QLatin1String("/qmlcache/")
+ : QString::fromLocal8Bit(envCachePath) + QLatin1String("/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex())
+ + QLatin1Char('.') + cacheFileSuffix;
+}
+
+bool CompilationUnit::loadFromDisk(
+ const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
+{
+ if (!QQmlFile::isLocalFile(url)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ auto cacheFile = std::make_unique<CompilationUnitMapper>();
+
+ const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
+ for (const QString &cachePath : cachePaths) {
+ Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
+ if (!mappedUnit)
+ continue;
+
+ const Unit *oldData = unitData();
+ const Unit * const oldDataPtr
+ = (oldData && !(oldData->flags & Unit::StaticData))
+ ? oldData
+ : nullptr;
+
+ auto dataPtrRevert = qScopeGuard([this, oldData](){
+ setUnitData(oldData);
+ });
+ setUnitData(mappedUnit);
+
+ if (mappedUnit->sourceFileIndex != 0) {
+ if (mappedUnit->sourceFileIndex >=
+ mappedUnit->stringTableSize + dynamicStrings.size()) {
+ *errorString = QStringLiteral("QML source file index is invalid.");
+ continue;
+ }
+ if (sourcePath !=
+ QQmlFile::urlToLocalFileOrQrc(stringAt(mappedUnit->sourceFileIndex))) {
+ *errorString = QStringLiteral("QML source file has moved to a different location.");
+ continue;
+ }
+ }
+
+ dataPtrRevert.dismiss();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile = std::move(cacheFile);
+ return true;
+ }
+
+ return false;
+}
+
+bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
+{
+ if (unitData()->sourceTimeStamp == 0) {
+ *errorString = QStringLiteral("Missing time stamp for source file");
+ return false;
+ }
+
+ if (!QQmlFile::isLocalFile(unitUrl)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ return SaveableUnitPointer(unitData()).saveToDisk<char>(
+ [&unitUrl, errorString](const char *data, quint32 size) {
+ const QString cachePath = localCacheFilePath(unitUrl);
+ if (SaveableUnitPointer::writeDataToFile(
+ cachePath, data, size, errorString)) {
+ CompilationUnitMapper::invalidate(cachePath);
+ return true;
+ }
+
+ return false;
+ });
+}
+
+QStringList CompilationUnit::moduleRequests() const
+{
+ QStringList requests;
+ requests.reserve(data->moduleRequestTableSize);
+ for (uint i = 0; i < data->moduleRequestTableSize; ++i)
+ requests << stringAt(data->moduleRequestTable()[i]);
+ return requests;
+}
+
+ResolvedTypeReference *CompilationUnit::resolvedType(QMetaType type) const
+{
+ for (ResolvedTypeReference *ref : std::as_const(resolvedTypes)) {
+ if (ref->type().typeId() == type)
+ return ref;
+ }
+ return nullptr;
+
+}
+
+int CompilationUnit::totalBindingsCount() const
+{
+ if (!icRootName)
+ return m_totalBindingsCount;
+ return inlineComponentData[*icRootName].totalBindingCount;
+}
+
+int CompilationUnit::totalObjectCount() const
+{
+ if (!icRootName)
+ return m_totalObjectCount;
+ return inlineComponentData[*icRootName].totalObjectCount;
+}
+
+template<typename F>
+void processInlinComponentType(
+ const QQmlType &type,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ F &&populateIcData)
+{
+ if (type.isInlineComponentType()) {
+ QString icRootName;
+ if (compilationUnit->icRootName) {
+ icRootName = type.elementName();
+ std::swap(*compilationUnit->icRootName, icRootName);
+ } else {
+ compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
+ }
+
+ populateIcData();
+
+ if (icRootName.isEmpty())
+ compilationUnit->icRootName.reset();
+ else
+ std::swap(*compilationUnit->icRootName, icRootName);
+ } else {
+ populateIcData();
+ }
+}
+
+void CompiledData::CompilationUnit::finalizeCompositeType(const QQmlType &type)
+{
+ // Add to type registry of composites
+ if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
+ // qmlType is only valid for types that have references to themselves.
+ if (type.isValid()) {
+ qmlType = type;
+ } else {
+ qmlType = QQmlMetaType::findCompositeType(
+ finalUrl(), this, (unitData()->flags & CompiledData::Unit::IsSingleton)
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton);
+ }
+
+ QQmlMetaType::registerInternalCompositeType(this);
+ } else {
+ const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
+ auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ if (const auto compilationUnit = typeRef->compilationUnit())
+ qmlType = compilationUnit->qmlType;
+ else
+ qmlType = typeRef->type();
+ }
+
+ // Collect some data for instantiation later.
+ using namespace icutils;
+ std::vector<QV4::CompiledData::InlineComponent> allICs {};
+ for (int i=0; i != objectCount(); ++i) {
+ const CompiledObject *obj = objectAt(i);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ allICs.push_back(*it);
+ }
+ }
+ NodeList nodes;
+ nodes.resize(allICs.size());
+ std::iota(nodes.begin(), nodes.end(), 0);
+ AdjacencyList adjacencyList;
+ adjacencyList.resize(nodes.size());
+ fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs);
+ bool hasCycle = false;
+ auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
+ Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
+
+ // We need to first iterate over all inline components,
+ // as the containing component might create instances of them
+ // and in that case we need to add its object count
+ for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
+ const auto &ic = allICs.at(nodeIt->index());
+ const int lastICRoot = ic.objectIndex;
+ for (int i = ic.objectIndex; i<objectCount(); ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
+ bool leftCurrentInlineComponent
+ = (i != lastICRoot
+ && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
+ || !obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
+ if (leftCurrentInlineComponent)
+ break;
+ const QString lastICRootName = stringAt(ic.nameIndex);
+ inlineComponentData[lastICRootName].totalBindingCount
+ += obj->nBindings;
+
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ const auto type = typeRef->type();
+ if (type.isValid() && type.parserStatusCast() != -1)
+ ++inlineComponentData[lastICRootName].totalParserStatusCount;
+
+ ++inlineComponentData[lastICRootName].totalObjectCount;
+ if (const auto compilationUnit = typeRef->compilationUnit()) {
+ // if the type is an inline component type, we have to extract the information
+ // from it.
+ // This requires that inline components are visited in the correct order.
+ processInlinComponentType(type, compilationUnit, [&]() {
+ auto &icData = inlineComponentData[lastICRootName];
+ icData.totalBindingCount += compilationUnit->totalBindingsCount();
+ icData.totalParserStatusCount += compilationUnit->totalParserStatusCount();
+ icData.totalObjectCount += compilationUnit->totalObjectCount();
+ });
+ }
+ }
+ }
+ }
+ int bindingCount = 0;
+ int parserStatusCount = 0;
+ int objectCount = 0;
+ for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
+ if (obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent))
+ continue;
+
+ bindingCount += obj->nBindings;
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ const auto type = typeRef->type();
+ if (type.isValid() && type.parserStatusCast() != -1)
+ ++parserStatusCount;
+ ++objectCount;
+ if (const auto compilationUnit = typeRef->compilationUnit()) {
+ processInlinComponentType(type, compilationUnit, [&](){
+ bindingCount += compilationUnit->totalBindingsCount();
+ parserStatusCount += compilationUnit->totalParserStatusCount();
+ objectCount += compilationUnit->totalObjectCount();
+ });
+ }
+ }
+ }
+
+ m_totalBindingsCount = bindingCount;
+ m_totalParserStatusCount = parserStatusCount;
+ m_totalObjectCount = objectCount;
+}
+
+int CompilationUnit::totalParserStatusCount() const
+{
+ if (!icRootName)
+ return m_totalParserStatusCount;
+ return inlineComponentData[*icRootName].totalParserStatusCount;
+}
+
+bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
+{
+ if (!dependencyHasher) {
+ for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
+ if (data->dependencyMD5Checksum[i] != 0)
+ return false;
+ }
+ return true;
+ }
+ const QByteArray checksum = dependencyHasher();
+ return checksum.size() == sizeof(data->dependencyMD5Checksum)
+ && memcmp(data->dependencyMD5Checksum, checksum.constData(),
+ sizeof(data->dependencyMD5Checksum)) == 0;
+}
+
+QQmlType CompilationUnit::qmlTypeForComponent(const QString &inlineComponentName) const
+{
+ if (inlineComponentName.isEmpty())
+ return qmlType;
+ return inlineComponentData[inlineComponentName].qmlType;
+}
+
+} // namespace CompiledData
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 11de506a53..c21fc19fa9 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2016 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
#ifndef QV4COMPILEDDATA_P_H
#define QV4COMPILEDDATA_P_H
@@ -52,19 +16,31 @@
#include <functional>
-#include <QtCore/qstring.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qhashfunctions.h>
+#include <QtCore/qlocale.h>
#include <QtCore/qscopeguard.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
-#include <QtCore/qhash.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qversionnumber.h>
#if QT_CONFIG(temporaryfile)
#include <QtCore/qsavefile.h>
#endif
#include <private/qendian_p.h>
+#include <private/qqmlnullablevalue_p.h>
+#include <private/qqmlpropertycachevector_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qv4compilationunitmapper_p.h>
#include <private/qv4staticvalue_p.h>
+
#include <functional>
+#include <limits.h>
QT_BEGIN_NAMESPACE
@@ -75,12 +51,18 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x26// support required properties
+#define QV4_DATA_STRUCTURE_VERSION 0x42 // Change metatype computation of AOT-compiled functions
class QIODevice;
class QQmlTypeNameCache;
class QQmlType;
class QQmlEngine;
+class QQmlPropertyData;
+class QQmlScriptData;
+
+namespace QQmlPrivate {
+struct AOTCompiledFunction;
+}
namespace QmlIR {
struct Document;
@@ -95,9 +77,19 @@ struct InternalClass;
struct Function;
class EvalISelFactory;
+class ResolvedTypeReference;
namespace CompiledData {
+// index is per-object binding index
+using BindingPropertyData = QVector<const QQmlPropertyData *>;
+
+// map from name index
+struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
+};
+
struct String;
struct Function;
struct Lookup;
@@ -112,6 +104,7 @@ struct TableIterator
int index;
const ItemType *operator->() { return (container->*IndexedGetter)(index); }
+ ItemType operator*() {return *operator->();}
void operator++() { ++index; }
bool operator==(const TableIterator &rhs) const { return index == rhs.index; }
bool operator!=(const TableIterator &rhs) const { return index != rhs.index; }
@@ -119,18 +112,45 @@ struct TableIterator
struct Location
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 20> line;
- quint32_le_bitfield<20, 12> column;
- };
-
- Location() : _dummy(0) { }
+ Location() : m_data(QSpecialIntegerBitfieldZero) {}
+ Location(quint32 l, quint32 c) : Location()
+ {
+ m_data.set<LineField>(l);
+ m_data.set<ColumnField>(c);
+ Q_ASSERT(m_data.get<LineField>() == l);
+ Q_ASSERT(m_data.get<ColumnField>() == c);
+ }
inline bool operator<(const Location &other) const {
- return line < other.line ||
- (line == other.line && column < other.column);
+ return m_data.get<LineField>() < other.m_data.get<LineField>()
+ || (m_data.get<LineField>() == other.m_data.get<LineField>()
+ && m_data.get<ColumnField>() < other.m_data.get<ColumnField>());
+ }
+
+ friend size_t qHash(const Location &location, size_t seed = 0)
+ {
+ return QT_PREPEND_NAMESPACE(qHash)(location.m_data.data(), seed);
}
+
+ friend bool operator==(const Location &a, const Location &b)
+ {
+ return a.m_data.data()== b.m_data.data();
+ }
+
+ void set(quint32 line, quint32 column)
+ {
+ m_data.set<LineField>(line);
+ m_data.set<ColumnField>(column);
+ }
+
+ quint32 line() const { return m_data.get<LineField>(); }
+ quint32 column() const { return m_data.get<ColumnField>(); }
+
+private:
+ using LineField = quint32_le_bitfield_member<0, 20>;
+ using ColumnField = quint32_le_bitfield_member<20, 12>;
+
+ quint32_le_bitfield_union<LineField, ColumnField> m_data;
};
static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -141,16 +161,24 @@ struct RegExp
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04,
- RegExp_Unicode = 0x08,
- RegExp_Sticky = 0x10
- };
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 5> flags;
- quint32_le_bitfield<5, 27> stringIndex;
+ RegExp_Sticky = 0x08,
+ RegExp_Unicode = 0x10,
};
- RegExp() : _dummy(0) { }
+ RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
+ RegExp(quint32 flags, quint32 stringIndex) : RegExp()
+ {
+ m_data.set<FlagsField>(flags);
+ m_data.set<StringIndexField>(stringIndex);
+ }
+
+ quint32 flags() const { return m_data.get<FlagsField>(); }
+ quint32 stringIndex() const { return m_data.get<StringIndexField>(); }
+
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 5>;
+ using StringIndexField = quint32_le_bitfield_member<5, 27>;
+ quint32_le_bitfield_union<FlagsField, StringIndexField> m_data;
};
static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -163,25 +191,49 @@ struct Lookup
Type_QmlContextPropertyGetter = 3
};
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 4> type_and_flags;
- quint32_le_bitfield<4, 28> nameIndex;
+ enum Mode : unsigned int {
+ Mode_ForStorage = 0,
+ Mode_ForCall = 1
};
- Lookup() : _dummy(0) { }
+ quint32 type() const { return m_data.get<TypeField>(); }
+ quint32 nameIndex() const { return m_data.get<NameIndexField>(); }
+ quint32 mode() const { return m_data.get<ModeField>(); }
+
+ Lookup() : m_data(QSpecialIntegerBitfieldZero) {}
+ Lookup(Type type, Mode mode, quint32 nameIndex) : Lookup()
+ {
+ m_data.set<TypeField>(type);
+ m_data.set<ModeField>(mode);
+ m_data.set<NameIndexField>(nameIndex);
+ }
+
+private:
+ using TypeField = quint32_le_bitfield_member<0, 2>;
+ using ModeField = quint32_le_bitfield_member<2, 1>;
+ // 1 bit left
+ using NameIndexField = quint32_le_bitfield_member<4, 28>;
+ quint32_le_bitfield_union<TypeField, ModeField, NameIndexField> m_data;
};
static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 31> nameOffset;
- quint32_le_bitfield<31, 1> isAccessor;
- };
+ JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {}
- JSClassMember() : _dummy(0) { }
+ void set(quint32 nameOffset, bool isAccessor)
+ {
+ m_data.set<NameOffsetField>(nameOffset);
+ m_data.set<IsAccessorField>(isAccessor ? 1 : 0);
+ }
+
+ quint32 nameOffset() const { return m_data.get<NameOffsetField>(); }
+ bool isAccessor() const { return m_data.get<IsAccessorField>() != 0; }
+
+private:
+ using NameOffsetField = quint32_le_bitfield_member<0, 31>;
+ using IsAccessorField = quint32_le_bitfield_member<31, 1>;
+ quint32_le_bitfield_union<NameOffsetField, IsAccessorField> m_data;
};
static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -194,45 +246,25 @@ struct JSClass
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
-// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
-// from a file must be castable to a QStringData regardless of the pointer size. With the first
-// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
-// ptrdiff_t and thus variable in size.
-// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
-// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
-// the same value.
struct String
{
- qint32_le refcount; // -1
qint32_le size;
- quint32_le allocAndCapacityReservedFlag; // 0
- quint32_le offsetOn32Bit;
- quint64_le offsetOn64Bit;
- // uint16 strdata[]
static int calculateSize(const QString &str) {
- return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
+ // we cannot enconuter strings larger than INT_MAX anyway, as such a string
+ // would already break in other parts of the compilation process
+ return (sizeof(String) + (int(str.size()) + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
-static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-
-// Ensure compatibility with QString
-static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
-static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
-static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
-static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
-#if QT_POINTER_SIZE == 8
-static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
-#else
-static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
-#endif
-struct CodeOffsetToLine {
+static_assert (sizeof (String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct CodeOffsetToLineAndStatement {
quint32_le codeOffset;
- quint32_le line;
+ qint32_le line; // signed because debug instructions get negative line numbers
+ quint32_le statement;
};
-static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(CodeOffsetToLineAndStatement) == 12, "CodeOffsetToLineAndStatement structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Block
{
@@ -256,19 +288,65 @@ struct Block
};
static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-enum class BuiltinType : unsigned int {
- Var = 0, Variant, Int, Bool, Real, String, Url, Color,
- Font, Time, Date, DateTime, Rect, Point, Size,
- Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin
+enum class NamedBuiltin: unsigned int {
+ Void, Var, Int, Bool, Real, String, Url, DateTime, RegExp
+};
+
+enum class CommonType : unsigned int {
+ // Actual named builtins
+ Void = uint(NamedBuiltin::Void),
+ Var = uint(NamedBuiltin::Var),
+ Int = uint(NamedBuiltin::Int),
+ Bool = uint(NamedBuiltin::Bool),
+ Real = uint(NamedBuiltin::Real),
+ String = uint(NamedBuiltin::String),
+ Url = uint(NamedBuiltin::Url),
+ DateTime = uint(NamedBuiltin::DateTime),
+ RegExp = uint(NamedBuiltin::RegExp),
+
+ // Optimization for very common other types
+ Time, Date, Rect, Point, Size,
+
+ // No type specified or not recognized
+ Invalid
};
struct ParameterType
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 1> indexIsBuiltinType;
- quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType;
+ enum Flag {
+ NoFlag = 0x0,
+ Common = 0x1,
+ List = 0x2,
};
+ Q_DECLARE_FLAGS(Flags, Flag);
+
+ void set(Flags flags, quint32 typeNameIndexOrCommonType)
+ {
+ m_data.set<IsListField>(flags.testFlag(List) ? 1 : 0);
+ m_data.set<IndexIsCommonTypeField>(flags.testFlag(Common) ? 1 : 0);
+ m_data.set<TypeNameIndexOrCommonTypeField>(typeNameIndexOrCommonType);
+ }
+
+ bool indexIsCommonType() const
+ {
+ return m_data.get<IndexIsCommonTypeField>() != 0;
+ }
+
+ bool isList() const
+ {
+ return m_data.get<IsListField>() != 0;
+ }
+
+ quint32 typeNameIndexOrCommonType() const
+ {
+ return m_data.get<TypeNameIndexOrCommonTypeField>();
+ }
+
+private:
+ using IndexIsCommonTypeField = quint32_le_bitfield_member<0, 1>;
+ using IsListField = quint32_le_bitfield_member<1, 1>;
+ using TypeNameIndexOrCommonTypeField = quint32_le_bitfield_member<2, 30>;
+ quint32_le_bitfield_union<IndexIsCommonTypeField, IsListField, TypeNameIndexOrCommonTypeField> m_data;
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -286,7 +364,8 @@ struct Function
enum Flags : unsigned int {
IsStrict = 0x1,
IsArrowFunction = 0x2,
- IsGenerator = 0x4
+ IsGenerator = 0x4,
+ IsClosureWrapper = 0x8,
};
// Absolute offset into file where the code for this function is located.
@@ -300,17 +379,22 @@ struct Function
ParameterType returnType;
quint32_le localsOffset;
quint16_le nLocals;
- quint16_le nLineNumbers;
- size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
+ quint16_le nLineAndStatementNumbers;
+ size_t lineAndStatementNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
+
+ quint32_le nRegisters;
+ Location location;
+ quint32_le nLabelInfos;
+
quint16_le sizeOfLocalTemporalDeadZone;
quint16_le firstTemporalDeadZoneRegister;
quint16_le sizeOfRegisterTemporalDeadZone;
- quint16_le nRegisters;
- Location location;
- quint32_le nLabelInfos;
- size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
+ size_t labelInfosOffset() const
+ {
+ return lineAndStatementNumberOffset() + nLineAndStatementNumbers * sizeof(CodeOffsetToLineAndStatement);
+ }
// Keep all unaligned data at the end
quint8 flags;
@@ -319,9 +403,21 @@ struct Function
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
- const Parameter *formalsTable() const { return reinterpret_cast<const Parameter *>(reinterpret_cast<const char *>(this) + formalsOffset); }
- const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
- const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
+ const Parameter *formalsTable() const
+ {
+ return reinterpret_cast<const Parameter *>(
+ reinterpret_cast<const char *>(this) + formalsOffset);
+ }
+ const quint32_le *localsTable() const
+ {
+ return reinterpret_cast<const quint32_le *>(
+ reinterpret_cast<const char *>(this) + localsOffset);
+ }
+ const CodeOffsetToLineAndStatement *lineAndStatementNumberTable() const
+ {
+ return reinterpret_cast<const CodeOffsetToLineAndStatement *>(
+ reinterpret_cast<const char *>(this) + lineAndStatementNumberOffset());
+ }
// --- QQmlPropertyCacheCreator interface
const Parameter *formalsBegin() const { return formalsTable(); }
@@ -332,9 +428,13 @@ struct Function
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
- static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
- int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
- + nLines*sizeof(CodeOffsetToLine);
+ static int calculateSize(
+ int nFormals, int nLocals, int nLinesAndStatements, int nInnerfunctions,
+ int labelInfoSize, int codeSize)
+ {
+ int trailingData = nFormals * sizeof(Parameter)
+ + (nLocals + nInnerfunctions + labelInfoSize) * sizeof (quint32)
+ + nLinesAndStatements * sizeof(CodeOffsetToLineAndStatement);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
return int(size);
@@ -434,10 +534,11 @@ static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have th
struct TranslationData
{
+ enum { NoContextIndex = std::numeric_limits<quint32>::max() };
quint32_le stringIndex;
quint32_le commentIndex;
qint32_le number;
- quint32_le padding;
+ quint32_le contextIndex;
};
static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -445,7 +546,7 @@ struct Binding
{
quint32_le propertyNameIndex;
- enum ValueType : unsigned int {
+ enum Type : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
@@ -459,7 +560,7 @@ struct Binding
Type_GroupProperty
};
- enum Flags : unsigned int {
+ enum Flag : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
@@ -469,13 +570,23 @@ struct Binding
IsBindingToAlias = 0x40,
IsDeferredBinding = 0x80,
IsCustomParserBinding = 0x100,
- IsFunctionExpression = 0x200
+ IsFunctionExpression = 0x200,
+ IsPropertyObserver = 0x400
};
+ Q_DECLARE_FLAGS(Flags, Flag);
+
+ using FlagsField = quint32_le_bitfield_member<0, 16>;
+ using TypeField = quint32_le_bitfield_member<16, 16>;
+ quint32_le_bitfield_union<FlagsField, TypeField> flagsAndType;
+
+ void clearFlags() { flagsAndType.set<FlagsField>(0); }
+ void setFlag(Flag flag) { flagsAndType.set<FlagsField>(flagsAndType.get<FlagsField>() | flag); }
+ bool hasFlag(Flag flag) const { return Flags(flagsAndType.get<FlagsField>()) & flag; }
+ Flags flags() const { return Flags(flagsAndType.get<FlagsField>()); }
+
+ void setType(Type type) { flagsAndType.set<TypeField>(type); }
+ Type type() const { return Type(flagsAndType.get<TypeField>()); }
- union {
- quint32_le_bitfield<0, 16> flags;
- quint32_le_bitfield<16, 16> type;
- };
union {
bool b;
quint32_le constantValueIndex;
@@ -489,23 +600,31 @@ struct Binding
Location location;
Location valueLocation;
+ bool hasSignalHandlerBindingFlag() const
+ {
+ const Flags bindingFlags = flags();
+ return bindingFlags & IsSignalHandlerExpression
+ || bindingFlags & IsSignalHandlerObject
+ || bindingFlags & IsPropertyObserver;
+ }
+
bool isValueBinding() const
{
- if (type == Type_AttachedProperty
- || type == Type_GroupProperty)
- return false;
- if (flags & IsSignalHandlerExpression
- || flags & IsSignalHandlerObject)
+ switch (type()) {
+ case Type_AttachedProperty:
+ case Type_GroupProperty:
return false;
- return true;
+ default:
+ return !hasSignalHandlerBindingFlag();
+ }
}
- bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); }
- bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); }
+ bool isValueBindingNoAlias() const { return isValueBinding() && !hasFlag(IsBindingToAlias); }
+ bool isValueBindingToAlias() const { return isValueBinding() && hasFlag(IsBindingToAlias); }
bool isSignalHandler() const
{
- if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) {
+ if (hasSignalHandlerBindingFlag()) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isAttachedProperty());
Q_ASSERT(!isGroupProperty());
@@ -516,7 +635,7 @@ struct Binding
bool isAttachedProperty() const
{
- if (type == Type_AttachedProperty) {
+ if (type() == Type_AttachedProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isGroupProperty());
@@ -527,7 +646,7 @@ struct Binding
bool isGroupProperty() const
{
- if (type == Type_GroupProperty) {
+ if (type() == Type_GroupProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isAttachedProperty());
@@ -536,13 +655,13 @@ struct Binding
return false;
}
- bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
+ bool isFunctionExpression() const { return hasFlag(IsFunctionExpression); }
//reverse of Lexer::singleEscape()
static QString escapedString(const QString &string)
{
QString tmp = QLatin1String("\"");
- for (int i = 0; i < string.length(); ++i) {
+ for (int i = 0; i < string.size(); ++i) {
const QChar &c = string.at(i);
switch (c.unicode()) {
case 0x08:
@@ -581,20 +700,34 @@ struct Binding
return tmp;
}
- bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
+ bool isTranslationBinding() const
+ {
+ const Binding::Type bindingType = type();
+ return bindingType == Type_Translation || bindingType == Type_TranslationById;
+ }
+ bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); }
+
+ bool isNumberBinding() const { return type() == Type_Number; }
bool valueAsBoolean() const
{
- if (type == Type_Boolean)
+ if (type() == Type_Boolean)
return value.b;
return false;
}
-
};
static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct InlineComponent
+{
+ quint32_le objectIndex;
+ quint32_le nameIndex;
+ Location location;
+};
+
+static_assert(sizeof(InlineComponent) == 12, "InlineComponent structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
struct EnumValue
{
quint32_le nameIndex;
@@ -654,50 +787,89 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected
struct Property
{
- quint32_le nameIndex;
- union {
- quint32_le_bitfield<0, 28> builtinTypeOrTypeNameIndex;
- quint32_le_bitfield<28, 1> isRequired;
- quint32_le_bitfield<29, 1> isBuiltinType;
- quint32_le_bitfield<30, 1> isList;
- quint32_le_bitfield<31, 1> isReadOnly;
- };
+private:
+ using CommonTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>;
+ using IsRequiredField = quint32_le_bitfield_member<28, 1>;
+ using IsCommonTypeField = quint32_le_bitfield_member<29, 1>;
+ using IsListField = quint32_le_bitfield_member<30, 1>;
+ using IsReadOnlyField = quint32_le_bitfield_member<31, 1>;
+public:
+ quint32_le nameIndex;
+ quint32_le_bitfield_union<
+ CommonTypeOrTypeNameIndexField,
+ IsRequiredField,
+ IsCommonTypeField,
+ IsListField,
+ IsReadOnlyField> data;
Location location;
- void setBuiltinType(BuiltinType t)
+ void setCommonType(CommonType t)
{
- builtinTypeOrTypeNameIndex = static_cast<quint32>(t);
- isBuiltinType = true;
+ data.set<CommonTypeOrTypeNameIndexField>(static_cast<quint32>(t));
+ data.set<IsCommonTypeField>(true);
}
- BuiltinType builtinType() const {
- if (isBuiltinType)
- return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex));
- return BuiltinType::InvalidBuiltin;
+
+ CommonType commonType() const {
+ if (data.get<IsCommonTypeField>() != 0)
+ return CommonType(data.get<CommonTypeOrTypeNameIndexField>());
+ return CommonType::Invalid;
}
- void setCustomType(int nameIndex)
+
+ void setTypeNameIndex(int nameIndex)
+ {
+ data.set<CommonTypeOrTypeNameIndexField>(nameIndex);
+ data.set<IsCommonTypeField>(false);
+ }
+
+ int typeNameIndex() const
{
- builtinTypeOrTypeNameIndex = nameIndex;
- isBuiltinType = false;
+ return data.get<IsCommonTypeField>() ? -1 : data.get<CommonTypeOrTypeNameIndexField>();
}
+
+ bool isCommonType() const { return data.get<IsCommonTypeField>(); }
+ uint commonTypeOrTypeNameIndex() const { return data.get<CommonTypeOrTypeNameIndexField>(); }
+
+ bool isList() const { return data.get<IsListField>(); }
+ void setIsList(bool isList) { data.set<IsListField>(isList); }
+
+ bool isRequired() const { return data.get<IsRequiredField>(); }
+ void setIsRequired(bool isRequired) { data.set<IsRequiredField>(isRequired); }
+
+ bool isReadOnly() const { return data.get<IsReadOnlyField>(); }
+ void setIsReadOnly(bool isReadOnly) { data.set<IsReadOnlyField>(isReadOnly); }
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct RequiredPropertyExtraData {
+ quint32_le nameIndex;
+};
+
+static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
struct Alias {
- enum Flags : unsigned int {
+private:
+ using NameIndexField = quint32_le_bitfield_member<0, 29>;
+ using FlagsField = quint32_le_bitfield_member<29, 3>;
+
+ // object id index (in QQmlContextData::idValues)
+ using TargetObjectIdField = quint32_le_bitfield_member<0, 31>;
+ using AliasToLocalAliasField = quint32_le_bitfield_member<31, 1>;
+ using IdIndexField = quint32_le_bitfield_member<0, 32>;
+
+public:
+
+ enum Flag : unsigned int {
IsReadOnly = 0x1,
Resolved = 0x2,
AliasPointsToPointerObject = 0x4
};
- union {
- quint32_le_bitfield<0, 29> nameIndex;
- quint32_le_bitfield<29, 3> flags;
- };
- union {
- quint32_le idIndex; // string index
- quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
- quint32_le_bitfield<31, 1> aliasToLocalAlias;
- };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ quint32_le_bitfield_union<NameIndexField, FlagsField> nameIndexAndFlags;
+ quint32_le_bitfield_union<IdIndexField, TargetObjectIdField, AliasToLocalAliasField>
+ idIndexAndTargetObjectIdAndAliasToLocalAlias;
+
union {
quint32_le propertyNameIndex; // string index
qint32_le encodedMetaPropertyIndex;
@@ -706,32 +878,94 @@ struct Alias {
Location location;
Location referenceLocation;
- bool isObjectAlias() const {
- Q_ASSERT(flags & Resolved);
+ bool hasFlag(Flag flag) const
+ {
+ return nameIndexAndFlags.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ nameIndexAndFlags.set<FlagsField>(nameIndexAndFlags.get<FlagsField>() | flag);
+ }
+
+ void clearFlags()
+ {
+ nameIndexAndFlags.set<FlagsField>(0);
+ }
+
+ quint32 nameIndex() const
+ {
+ return nameIndexAndFlags.get<NameIndexField>();
+ }
+
+ void setNameIndex(quint32 nameIndex)
+ {
+ nameIndexAndFlags.set<NameIndexField>(nameIndex);
+ }
+
+ bool isObjectAlias() const
+ {
+ Q_ASSERT(hasFlag(Resolved));
return encodedMetaPropertyIndex == -1;
}
+
+ quint32 idIndex() const
+ {
+ return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<IdIndexField>();
+ }
+
+ void setIdIndex(quint32 idIndex)
+ {
+ idIndexAndTargetObjectIdAndAliasToLocalAlias.set<IdIndexField>(idIndex);
+ }
+
+
+ bool isAliasToLocalAlias() const
+ {
+ return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<AliasToLocalAliasField>();
+ }
+
+ void setIsAliasToLocalAlias(bool isAliasToLocalAlias)
+ {
+ idIndexAndTargetObjectIdAndAliasToLocalAlias.set<AliasToLocalAliasField>(isAliasToLocalAlias);
+ }
+
+ quint32 targetObjectId() const
+ {
+ return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<TargetObjectIdField>();
+ }
+
+ void setTargetObjectId(quint32 targetObjectId)
+ {
+ idIndexAndTargetObjectIdAndAliasToLocalAlias.set<TargetObjectIdField>(targetObjectId);
+ }
};
static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Object
{
- enum Flags : unsigned int {
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 15>;
+ using DefaultPropertyIsAliasField = quint32_le_bitfield_member<15, 1>;
+ using IdField = quint32_le_bitfield_member<16, 16, qint32>;
+public:
+ enum Flag : unsigned int {
NoFlag = 0x0,
IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
HasDeferredBindings = 0x2, // any of the bindings are deferred
- HasCustomParserBindings = 0x4
+ HasCustomParserBindings = 0x4,
+ IsInlineComponentRoot = 0x8,
+ IsPartOfInlineComponent = 0x10
};
+ Q_DECLARE_FLAGS(Flags, Flag);
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
quint32_le inheritedTypeNameIndex;
quint32_le idNameIndex;
- union {
- quint32_le_bitfield<0, 15> flags;
- quint32_le_bitfield<15, 1> defaultPropertyIsAlias;
- qint32_le_bitfield<16, 16> id;
- };
+ quint32_le_bitfield_union<FlagsField, DefaultPropertyIsAliasField, IdField>
+ flagsAndDefaultPropertyIsAliasAndId;
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
quint16_le nFunctions;
quint16_le nProperties;
@@ -749,12 +983,60 @@ struct Object
quint32_le offsetToNamedObjectsInComponent;
Location location;
Location locationOfIdProperty;
+ quint32_le offsetToInlineComponents;
+ quint16_le nInlineComponents;
+ quint32_le offsetToRequiredPropertyExtraData;
+ quint16_le nRequiredPropertyExtraData;
// Function[]
// Property[]
// Signal[]
// Binding[]
+// InlineComponent[]
+// RequiredPropertyExtraData[]
+
+ Flags flags() const
+ {
+ return Flags(flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>());
+ }
+
+ bool hasFlag(Flag flag) const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(
+ flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() | flag);
+ }
+
+ void setFlags(Flags flags)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(flags);
+ }
+
+ bool hasAliasAsDefaultProperty() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<DefaultPropertyIsAliasField>();
+ }
+
+ void setHasAliasAsDefaultProperty(bool defaultAlias)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<DefaultPropertyIsAliasField>(defaultAlias);
+ }
- static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent)
+ qint32 objectId() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<IdField>();
+ }
+
+ void setObjectId(qint32 id)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<IdField>(id);
+ }
+
+
+ static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
@@ -764,6 +1046,8 @@ struct Object
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ nNamedObjectsInComponent * sizeof(int)
+ + nInlineComponents * sizeof(InlineComponent)
+ + nRequiredPropertyExtraData * sizeof(RequiredPropertyExtraData)
+ 0x7
) & ~0x7;
}
@@ -802,11 +1086,31 @@ struct Object
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
+ const InlineComponent *inlineComponentAt(int idx) const
+ {
+ return inlineComponentTable() + idx;
+ }
+
const quint32_le *namedObjectsInComponentTable() const
{
return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
}
+ const InlineComponent *inlineComponentTable() const
+ {
+ return reinterpret_cast<const InlineComponent*>(reinterpret_cast<const char *>(this) + offsetToInlineComponents);
+ }
+
+ const RequiredPropertyExtraData *requiredPropertyExtraDataAt(int idx) const
+ {
+ return requiredPropertyExtraDataTable() + idx;
+ }
+
+ const RequiredPropertyExtraData *requiredPropertyExtraDataTable() const
+ {
+ return reinterpret_cast<const RequiredPropertyExtraData*>(reinterpret_cast<const char *>(this) + offsetToRequiredPropertyExtraData);
+ }
+
// --- QQmlPropertyCacheCreator interface
int propertyCount() const { return nProperties; }
int aliasCount() const { return nAliases; }
@@ -816,6 +1120,7 @@ struct Object
const Binding *bindingsBegin() const { return bindingTable(); }
const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
+ int bindingCount() const { return nBindings; }
const Property *propertiesBegin() const { return propertyTable(); }
const Property *propertiesEnd() const { return propertyTable() + nProperties; }
@@ -831,31 +1136,42 @@ struct Object
SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
+ typedef TableIterator<InlineComponent, Object, &Object::inlineComponentAt> InlineComponentIterator;
+ InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);}
+ InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);}
+
+ typedef TableIterator<RequiredPropertyExtraData, Object, &Object::requiredPropertyExtraDataAt> RequiredPropertyExtraDataIterator;
+ RequiredPropertyExtraDataIterator requiredPropertyExtraDataBegin() const {return RequiredPropertyExtraDataIterator(this, 0); }
+ RequiredPropertyExtraDataIterator requiredPropertyExtraDataEnd() const {return RequiredPropertyExtraDataIterator(this, nRequiredPropertyExtraData); }
+
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
-static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Object) == 84, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
enum ImportType : unsigned int {
ImportLibrary = 0x1,
ImportFile = 0x2,
- ImportScript = 0x3
+ ImportScript = 0x3,
+ ImportInlineComponent = 0x4
};
quint32_le type;
quint32_le uriIndex;
quint32_le qualifierIndex;
- qint32_le majorVersion;
- qint32_le minorVersion;
-
Location location;
+ QTypeRevision version;
+ quint16_le reserved;
- Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; }
+ Import()
+ {
+ type = 0; uriIndex = 0; qualifierIndex = 0; version = QTypeRevision::zero(); reserved = 0;
+ }
};
-static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Import) == 20, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct QmlUnit
{
@@ -900,7 +1216,18 @@ struct Unit
IsSingleton = 0x4,
IsSharedLibrary = 0x8, // .pragma shared?
IsESModule = 0x10,
- PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
+ PendingTypeCompilation = 0x20, // the QML data structures present are incomplete and require type compilation
+ IsStrict = 0x40,
+ ListPropertyAssignReplaceIfDefault = 0x80,
+ ListPropertyAssignReplaceIfNotDefault = 0x100,
+ ListPropertyAssignReplace
+ = ListPropertyAssignReplaceIfDefault | ListPropertyAssignReplaceIfNotDefault,
+ ComponentsBound = 0x200,
+ FunctionSignaturesIgnored = 0x400,
+ NativeMethodsAcceptThisObject = 0x800,
+ ValueTypesCopied = 0x1000,
+ ValueTypesAddressable = 0x2000,
+ ValueTypesAssertable = 0x4000,
};
quint32_le flags;
quint32_le stringTableSize;
@@ -954,19 +1281,18 @@ struct Unit
}
/* end QML specific fields*/
- QString stringAtInternal(int idx) const {
- Q_ASSERT(idx < int(stringTableSize));
+ QString stringAtInternal(uint idx) const {
+ Q_ASSERT(idx < stringTableSize);
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
+ Q_ASSERT(str->size >= 0);
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- if (flags & StaticData) {
- const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
- return QString(holder);
- }
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
+ if (flags & StaticData)
+ return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
@@ -1028,12 +1354,28 @@ struct Unit
return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
}
+ const quint32_le *translationContextIndex() const{
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
+ quint32_le *translationContextIndex() {
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<quint32_le*>((reinterpret_cast<char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
+
+ bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
};
static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -1070,20 +1412,26 @@ struct TypeReferenceMap : QHash<int, TypeReference>
}
auto prop = obj->propertiesBegin();
- auto propEnd = obj->propertiesEnd();
+ auto const propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
- if (!prop->isBuiltinType) {
- TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location);
+ if (!prop->isCommonType()) {
+ TypeReference &r = this->add(prop->commonTypeOrTypeNameIndex(), prop->location);
r.errorWhenNotFound = true;
}
}
auto binding = obj->bindingsBegin();
- auto bindingEnd = obj->bindingsEnd();
+ auto const bindingEnd = obj->bindingsEnd();
for ( ; binding != bindingEnd; ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty)
this->add(binding->propertyNameIndex, binding->location);
}
+
+ auto ic = obj->inlineComponentsBegin();
+ auto const icEnd = obj->inlineComponentsEnd();
+ for (; ic != icEnd; ++ic) {
+ this->add(ic->nameIndex, ic->location);
+ }
}
template <typename Iterator>
@@ -1096,110 +1444,93 @@ struct TypeReferenceMap : QHash<int, TypeReference>
using DependentTypesHasher = std::function<QByteArray()>;
-// This is how this hooks into the existing structures:
-
-struct CompilationUnitBase
-{
- Q_DISABLE_COPY(CompilationUnitBase)
-
- CompilationUnitBase() = default;
- ~CompilationUnitBase() = default;
-
- CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); }
-
- CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept
- {
- if (this != &other) {
- runtimeStrings = other.runtimeStrings;
- other.runtimeStrings = nullptr;
- constants = other.constants;
- other.constants = nullptr;
- runtimeRegularExpressions = other.runtimeRegularExpressions;
- other.runtimeRegularExpressions = nullptr;
- runtimeClasses = other.runtimeClasses;
- other.runtimeClasses = nullptr;
- imports = other.imports;
- other.imports = nullptr;
- }
- return *this;
- }
+struct InlineComponentData {
+
+ InlineComponentData() = default;
+ InlineComponentData(
+ const QQmlType &qmlType, int objectIndex, int nameIndex, int totalObjectCount,
+ int totalBindingCount, int totalParserStatusCount)
+ : qmlType(qmlType)
+ , objectIndex(objectIndex)
+ , nameIndex(nameIndex)
+ , totalObjectCount(totalObjectCount)
+ , totalBindingCount(totalBindingCount)
+ , totalParserStatusCount(totalParserStatusCount)
+ {}
- // pointers either to data->constants() or little-endian memory copy.
- Heap::String **runtimeStrings = nullptr; // Array
- const StaticValue* constants = nullptr;
- QV4::StaticValue *runtimeRegularExpressions = nullptr;
- Heap::InternalClass **runtimeClasses = nullptr;
- const StaticValue** imports = nullptr;
+ QQmlType qmlType;
+ int objectIndex = -1;
+ int nameIndex = -1;
+ int totalObjectCount = 0;
+ int totalBindingCount = 0;
+ int totalParserStatusCount = 0;
};
-Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *));
-
-struct CompilationUnit : public CompilationUnitBase
+struct CompilationUnit final : public QQmlRefCounted<CompilationUnit>
{
- Q_DISABLE_COPY(CompilationUnit)
+ Q_DISABLE_COPY_MOVE(CompilationUnit)
const Unit *data = nullptr;
const QmlUnit *qmlData = nullptr;
QStringList dynamicStrings;
-public:
- using CompiledObject = CompiledData::Object;
+ const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions = nullptr;
- CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(),
- const QString &finalUrlString = QString())
- {
- setUnitData(unitData, nullptr, fileName, finalUrlString);
- }
+ // pointers either to data->constants() or little-endian memory copy.
+ const StaticValue *constants = nullptr;
- ~CompilationUnit()
- {
- if (data) {
- if (data->qmlUnit() != qmlData)
- free(const_cast<QmlUnit *>(qmlData));
- qmlData = nullptr;
+ std::unique_ptr<CompilationUnitMapper> backingFile;
- if (!(data->flags & QV4::CompiledData::Unit::StaticData))
- free(const_cast<Unit *>(data));
- }
- data = nullptr;
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
- constants = nullptr;
-#endif
+ int m_totalBindingsCount = 0; // Number of bindings used in this type
+ int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int m_totalObjectCount = 0; // Number of objects explicitly instantiated
- delete [] imports;
- imports = nullptr;
- }
+ std::unique_ptr<QString> icRootName;
+ QHash<QString, InlineComponentData> inlineComponentData;
+
+ // index is object index. This allows fast access to the
+ // property data when initializing bindings, avoiding expensive
+ // lookups by string (property name).
+ QVector<BindingPropertyData> bindingPropertyDataPerObject;
+
+ ResolvedTypeReferenceMap resolvedTypes;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+
+ QQmlPropertyCacheVector propertyCaches;
- CompilationUnit(CompilationUnit &&other) noexcept
+ QQmlType qmlType;
+
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
+
+public:
+ // --- interface for QQmlPropertyCacheCreator
+ using CompiledObject = const CompiledData::Object;
+ using CompiledFunction = const CompiledData::Function;
+ using CompiledBinding = const CompiledData::Binding;
+
+ // Empty dummy. We don't need to do this when loading from cache.
+ class IdToObjectMap
{
- *this = std::move(other);
- }
+ public:
+ void insert(int, int) {}
+ void clear() {}
- CompilationUnit &operator=(CompilationUnit &&other) noexcept
+ // We have already checked uniqueness of IDs when creating the CU
+ bool contains(int) { return false; }
+ };
+
+ explicit CompilationUnit(const Unit *unitData, const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions,
+ const QString &fileName = QString(), const QString &finalUrlString = QString())
+ : CompilationUnit(unitData, fileName, finalUrlString)
{
- if (this != &other) {
- data = other.data;
- other.data = nullptr;
- qmlData = other.qmlData;
- other.qmlData = nullptr;
- dynamicStrings = std::move(other.dynamicStrings);
- other.dynamicStrings.clear();
- m_fileName = std::move(other.m_fileName);
- other.m_fileName.clear();
- m_finalUrlString = std::move(other.m_finalUrlString);
- other.m_finalUrlString.clear();
- m_module = other.m_module;
- other.m_module = nullptr;
- CompilationUnitBase::operator=(std::move(other));
- }
- return *this;
+ this->aotCompiledFunctions = aotCompiledFunctions;
}
+ Q_QML_EXPORT CompilationUnit(
+ const Unit *unitData = nullptr, const QString &fileName = QString(),
+ const QString &finalUrlString = QString());
+
+ Q_QML_EXPORT ~CompilationUnit();
+
const Unit *unitData() const { return data; }
void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
@@ -1232,24 +1563,207 @@ public:
m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
- QString stringAt(int index) const
+ QString stringAt(uint index) const
{
- if (uint(index) >= data->stringTableSize)
- return dynamicStrings.at(index - data->stringTableSize);
- return data->stringAtInternal(index);
+ if (index < data->stringTableSize)
+ return data->stringAtInternal(index);
+
+ const qsizetype dynamicIndex = index - data->stringTableSize;
+ Q_ASSERT(dynamicIndex < dynamicStrings.size());
+ return dynamicStrings.at(dynamicIndex);
}
QString fileName() const { return m_fileName; }
QString finalUrlString() const { return m_finalUrlString; }
- Heap::Module *module() const { return m_module; }
- void setModule(Heap::Module *module) { m_module = module; }
+ QString bindingValueAsString(const CompiledData::Binding *binding) const
+ {
+ using namespace CompiledData;
+ switch (binding->type()) {
+ case Binding::Type_Script:
+ case Binding::Type_String:
+ return stringAt(binding->stringIndex);
+ case Binding::Type_Null:
+ return QStringLiteral("null");
+ case Binding::Type_Boolean:
+ return binding->value.b ? QStringLiteral("true") : QStringLiteral("false");
+ case Binding::Type_Number:
+ return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest);
+ case Binding::Type_Invalid:
+ return QString();
+ case Binding::Type_TranslationById:
+ case Binding::Type_Translation:
+ return stringAt(data->translations()[binding->value.translationDataIndex].stringIndex);
+ default:
+ break;
+ }
+ return QString();
+ }
+
+ QString bindingValueAsScriptString(const CompiledData::Binding *binding) const
+ {
+ return (binding->type() == CompiledData::Binding::Type_String)
+ ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
+ : bindingValueAsString(binding);
+ }
+
+ double bindingValueAsNumber(const CompiledData::Binding *binding) const
+ {
+ if (binding->type() != CompiledData::Binding::Type_Number)
+ return 0.0;
+ return constants[binding->value.constantValueIndex].doubleValue();
+ }
+
+ Q_QML_EXPORT static QString localCacheFilePath(const QUrl &url);
+ Q_QML_EXPORT bool loadFromDisk(
+ const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+ Q_QML_EXPORT bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+
+ int importCount() const { return qmlData->nImports; }
+ const CompiledData::Import *importAt(int index) const { return qmlData->importAt(index); }
+
+ Q_QML_EXPORT QStringList moduleRequests() const;
+
+ // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
+ // warnings about that code. They include any potential URL interceptions and thus represent the
+ // "physical" location of the code.
+ //
+ // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
+ // They are _not_ intercepted and thus represent the "logical" name for the code.
+
+ QUrl url() const
+ {
+ if (!m_url.isValid())
+ m_url = QUrl(fileName());
+ return m_url;
+ }
+
+ QUrl finalUrl() const
+ {
+ if (!m_finalUrl.isValid())
+ m_finalUrl = QUrl(finalUrlString());
+ return m_finalUrl;
+ }
+
+ ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+ ResolvedTypeReference *resolvedType(QMetaType type) const;
+
+ QQmlPropertyCache::ConstPtr rootPropertyCache() const
+ {
+ return propertyCaches.at(/*root object*/0);
+ }
+
+ int objectCount() const { return qmlData->nObjects; }
+ const CompiledObject *objectAt(int index) const { return qmlData->objectAt(index); }
+
+ int totalBindingsCount() const;
+ int totalParserStatusCount() const;
+ int totalObjectCount() const;
+
+ int inlineComponentId(const QString &inlineComponentName) const
+ {
+ for (uint i = 0; i < qmlData->nObjects; ++i) {
+ auto *object = qmlData->objectAt(i);
+ for (auto it = object->inlineComponentsBegin(), end = object->inlineComponentsEnd();
+ it != end; ++it) {
+ if (stringAt(it->nameIndex) == inlineComponentName)
+ return it->objectIndex;
+ }
+ }
+ return -1;
+ }
+
+ void finalizeCompositeType(const QQmlType &type);
+
+ bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
+
+ enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault };
+ ListPropertyAssignBehavior listPropertyAssignBehavior() const
+ {
+ if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplace)
+ return ListPropertyAssignBehavior::Replace;
+ if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplaceIfNotDefault)
+ return ListPropertyAssignBehavior::ReplaceIfNotDefault;
+ return ListPropertyAssignBehavior::Append;
+ }
+
+ bool ignoresFunctionSignature() const
+ {
+ return unitData()->flags & CompiledData::Unit::FunctionSignaturesIgnored;
+ }
+
+ bool nativeMethodsAcceptThisObjects() const
+ {
+ return unitData()->flags & CompiledData::Unit::NativeMethodsAcceptThisObject;
+ }
+
+ bool valueTypesAreCopied() const
+ {
+ return unitData()->flags & CompiledData::Unit::ValueTypesCopied;
+ }
+
+ bool valueTypesAreAddressable() const
+ {
+ return unitData()->flags & CompiledData::Unit::ValueTypesAddressable;
+ }
+
+ bool valueTypesAreAssertable() const
+ {
+ return unitData()->flags & CompiledData::Unit::ValueTypesAssertable;
+ }
+
+ bool componentsAreBound() const
+ {
+ return unitData()->flags & CompiledData::Unit::ComponentsBound;
+ }
+
+ bool isESModule() const
+ {
+ return unitData()->flags & CompiledData::Unit::IsESModule;
+ }
+
+ bool isSharedLibrary() const
+ {
+ return unitData()->flags & CompiledData::Unit::IsSharedLibrary;
+ }
+
+ struct FunctionIterator
+ {
+ FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index)
+ : unit(unit), object(object), index(index) {}
+ const CompiledData::Unit *unit;
+ const CompiledObject *object;
+ int index;
+
+ const CompiledFunction *operator->() const
+ {
+ return unit->functionAt(object->functionOffsetTable()[index]);
+ }
+
+ void operator++() { ++index; }
+ bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
+ };
+
+ FunctionIterator objectFunctionsBegin(const CompiledObject *object) const
+ {
+ return FunctionIterator(unitData(), object, 0);
+ }
+
+ FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
+ {
+ return FunctionIterator(unitData(), object, object->nFunctions);
+ }
+
+ QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const;
+ QMetaType metaType() const { return qmlType.typeId(); }
private:
QString m_fileName; // initialized from data->sourceFileIndex
QString m_finalUrlString; // initialized from data->finalUrlIndex
- Heap::Module *m_module = nullptr;
+ mutable QQmlNullableValue<QUrl> m_url;
+ mutable QQmlNullableValue<QUrl> m_finalUrl;
};
class SaveableUnitPointer
@@ -1267,7 +1781,8 @@ public:
template<typename Char>
bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
{
- auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; });
+ const quint32_le oldFlags = mutableFlags();
+ auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; });
mutableFlags() |= temporaryFlags;
return writer(data<Char>(), size());
}
@@ -1287,7 +1802,7 @@ public:
errorString->clear();
return true;
#else
- Q_UNUSED(outputFileName)
+ Q_UNUSED(outputFileName);
*errorString = QStringLiteral("features.temporaryfile is disabled.");
return false;
#endif
@@ -1321,6 +1836,7 @@ private:
} // CompiledData namespace
} // QV4 namespace
+Q_DECLARE_OPERATORS_FOR_FLAGS(QV4::CompiledData::ParameterType::Flags);
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h
index 0716f7ea20..9b4f582c00 100644
--- a/src/qml/common/qv4staticvalue_p.h
+++ b/src/qml/common/qv4staticvalue_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2019 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
#ifndef QV4STATICVALUE_P_H
#define QV4STATICVALUE_P_H
@@ -50,7 +14,11 @@
// We mean it.
//
+#include <qjsnumbercoercion.h>
+
#include <QtCore/private/qnumeric_p.h>
+#include <private/qtqmlglobal_p.h>
+
#include <cstring>
#ifdef QT_NO_DEBUG
@@ -69,55 +37,14 @@ namespace QV4 {
// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm
typedef quint64 ReturnedValue;
-struct Double {
- quint64 d;
-
- Double(double dbl) {
- memcpy(&d, &dbl, sizeof(double));
- }
-
- int sign() const {
- return (d >> 63) ? -1 : 1;
- }
-
- bool isDenormal() const {
- return static_cast<int>((d << 1) >> 53) == 0;
- }
-
- int exponent() const {
- return static_cast<int>((d << 1) >> 53) - 1023;
- }
-
- quint64 significant() const {
- quint64 m = (d << 12) >> 12;
- if (!isDenormal())
- m |= (static_cast<quint64>(1) << 52);
- return m;
- }
-
- static int toInt32(double d) {
- int i = static_cast<int>(d);
- if (i == d)
- return i;
- return Double(d).toInt32();
- }
-
- int toInt32() {
- int e = exponent() - 52;
- if (e < 0) {
- if (e <= -53)
- return 0;
- return sign() * static_cast<int>(significant() >> -e);
- } else {
- if (e > 31)
- return 0;
- return sign() * (static_cast<int>(significant()) << e);
- }
- }
-};
+namespace Heap {
+struct Base;
+}
struct StaticValue
{
+ using HeapBasePtr = Heap::Base *;
+
StaticValue() = default;
constexpr StaticValue(quint64 val) : _val(val) {}
@@ -137,21 +64,31 @@ struct StaticValue
Value &asValue();
/*
- We use 8 bytes for a value and a different variant of NaN boxing. A Double
- NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
- signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
- processor, and are thus free for us to store other data. We keep pointers in there for
- managed objects, and encode the other types using the free space given to use by the unused
- bits for NaN values. This also works for pointers on 64 bit systems, as they all currently
- only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
- pointers.)
-
- We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
- get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
- managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
- the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
- set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
- used to encode Null/Int/Bool.
+ We use 8 bytes for a value. In order to store all possible values we employ a variant of NaN
+ boxing. A "special" Double is indicated by a number that has the 11 exponent bits set to 1.
+ Those can be NaN, positive or negative infinity. We only store one variant of NaN: The sign
+ bit has to be off and the bit after the exponent ("quiet bit") has to be on. However, since
+ the exponent bits are enough to identify special doubles, we can use a different bit as
+ discriminator to tell us how the rest of the bits (including quiet and sign) are to be
+ interpreted. This bit is bit 48. If set, we have an unmanaged value, which includes the
+ special doubles and various other values. If unset, we have a managed value, and all of the
+ other bits can be used to assemble a pointer.
+
+ On 32bit systems the pointer can just live in the lower 4 bytes. On 64 bit systems the lower
+ 48 bits can be used for verbatim pointer bits. However, since all our heap objects are
+ aligned to 32 bytes, we can use the 5 least significant bits of the pointer to store, e.g.
+ pointer tags on android. The same holds for the 3 bits between the double exponent and
+ bit 48.
+
+ With that out of the way, we can use the other bits to store different values.
+
+ We xor Doubles with (0x7ff48000 << 32). That has the effect that any double with all the
+ exponent bits set to 0 is one of our special doubles. Those special doubles then get the
+ other two bits in the mask (Special and Number) set to 1, as they cannot have 1s in those
+ positions to begin with.
+
+ We dedicate further bits to integer-convertible and bool-or-int. With those bits we can
+ describe all values we need to store.
Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
@@ -159,41 +96,38 @@ struct StaticValue
0 = always 0
1 = always 1
x = stored value
- a,b,c,d = specific bit values, see notes
+ y = stored value, shifted to different position
+ a = xor-ed bits, where at least one bit is set
+ b = xor-ed bits
32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
------------------------------------------------------------------------+--------------
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
- 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
- a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
- dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
- 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
- 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null
- 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool
- 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
-
- Notes:
- - a: xor-ed signbit, always 1 for NaN
- - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value
- - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0
- - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++
- and JS
- - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0
- - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1,
- so: (val >> (64-15)) == 1
- - Null, Bool, and Int have bit 48 set, indicating integer-convertible
- - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
- any non double results in a NaN
- - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to
- 63) are zero. No need to shift.
+ y0000000 0000yyy0 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxyyyyy | Managed (heap pointer)
+ 00000000 00001101 10000000 00000000 00000000 00000000 00000000 00000000 | NaN
+ 00000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | +Inf
+ 10000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | -Inf
+ xaaaaaaa aaaaxbxb bxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
+ 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
+ 00000000 00000011 00000000 00000000 00000000 00000000 00000000 00000000 | Null
+ 00000000 00000011 10000000 00000000 00000000 00000000 00000000 0000000x | Bool
+ 00000000 00000011 11000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+ ^ ^^^ ^^
+ | ||| ||
+ | ||| |+-> Number
+ | ||| +--> Int or Bool
+ | ||+----> Unmanaged
+ | |+-----> Integer compatible
+ | +------> Special double
+ +--------------------> Double sign, also used for special doubles
*/
quint64 _val;
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr quint64 &rawValueRef() { return _val; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr quint64 rawValue() const { return _val; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr void setRawValue(quint64 raw) { _val = raw; }
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
static inline int valueOffset() { return 0; }
@@ -202,108 +136,145 @@ struct StaticValue
static inline int valueOffset() { return 4; }
static inline int tagOffset() { return 0; }
#endif
- static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; }
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
+ static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << Tag_Shift | value; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << Tag_Shift | value; }
QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
- QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); }
+ QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> Tag_Shift; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr void setTag(quint32 tag) { setTagValue(tag, value()); }
QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const
{
return int(value());
}
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
+ QV4_NEARLY_ALWAYS_INLINE constexpr void setInt_32(int i)
{
- setTagValue(quint32(ValueTypeInternal::Integer), quint32(i));
+ setTagValue(quint32(QuickType::Integer), quint32(i));
}
QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
- QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty()
+ QV4_NEARLY_ALWAYS_INLINE constexpr void setEmpty()
{
- setTagValue(quint32(ValueTypeInternal::Empty), 0);
- }
-
- // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
- // and use negative numbers here
- enum QuickType {
- QT_ManagedOrUndefined = 0,
- QT_ManagedOrUndefined1 = 1,
- QT_ManagedOrUndefined2 = 2,
- QT_ManagedOrUndefined3 = 3,
- QT_Empty = 4,
- QT_Null = 5,
- QT_Bool = 6,
- QT_Int = 7
- // all other values are doubles
- };
-
- enum Type {
- Undefined_Type = 0,
- Managed_Type = 1,
- Empty_Type = 4,
- Null_Type = 5,
- Boolean_Type = 6,
- Integer_Type = 7,
- Double_Type = 8
+ setTagValue(quint32(QuickType::Empty), 0);
+ }
+
+ enum class TagBit {
+ // s: sign bit
+ // e: double exponent bit
+ // u: upper 3 bits if managed
+ // m: bit 48, denotes "unmanaged" if 1
+ // p: significant pointer bits (some re-used for non-managed)
+ // seeeeeeeeeeeuuumpppp
+ SpecialNegative = 0b10000000000000000000 << 12,
+ SpecialQNaN = 0b00000000000010000000 << 12,
+ Special = 0b00000000000001000000 << 12,
+ IntCompat = 0b00000000000000100000 << 12,
+ Unmanaged = 0b00000000000000010000 << 12,
+ IntOrBool = 0b00000000000000001000 << 12,
+ Number = 0b00000000000000000100 << 12,
};
- inline Type type() const {
- int t = quickType();
- if (t < QT_Empty)
- return _val ? Managed_Type : Undefined_Type;
- if (t > QT_Int)
- return Double_Type;
- return static_cast<Type>(t);
- }
+ static inline constexpr quint64 tagBitMask(TagBit bit) { return quint64(bit) << Tag_Shift; }
- // Shared between 32-bit and 64-bit encoding
- enum {
- Tag_Shift = 32
+ enum Type {
+ // Managed, Double and undefined are not directly encoded
+ Managed_Type = 0,
+ Double_Type = 1,
+ Undefined_Type = 2,
+
+ Empty_Type = quint32(TagBit::Unmanaged),
+ Null_Type = Empty_Type | quint32(TagBit::IntCompat),
+ Boolean_Type = Null_Type | quint32(TagBit::IntOrBool),
+ Integer_Type = Boolean_Type | quint32(TagBit::Number)
};
- // Used only by 64-bit encoding
- static const quint64 NaNEncodeMask = 0xfffc000000000000ull;
enum {
- IsDouble_Shift = 64-14,
- IsManagedOrUndefined_Shift = 64-15,
- IsIntegerConvertible_Shift = 64-15,
- IsIntegerOrBool_Shift = 64-16,
- QuickType_Shift = 64 - 17,
- IsPositiveIntShift = 31
- };
-
- static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
+ Tag_Shift = 32,
- enum class ValueTypeInternal_64 {
- Empty = Immediate_Mask_64 | 0,
- Null = Immediate_Mask_64 | 0x08000u,
- Boolean = Immediate_Mask_64 | 0x10000u,
- Integer = Immediate_Mask_64 | 0x18000u
- };
+ IsIntegerConvertible_Shift = 48,
+ IsIntegerConvertible_Value = 3, // Unmanaged | IntCompat after shifting
- // Used only by 32-bit encoding
- enum Masks {
- SilentNaNBit = 0x00040000,
- NotDouble_Mask = 0x7ffa0000,
+ IsIntegerOrBool_Shift = 47,
+ IsIntegerOrBool_Value = 7, // Unmanaged | IntCompat | IntOrBool after shifting
};
- static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit;
- enum class ValueTypeInternal_32 {
- Empty = Immediate_Mask_32 | 0,
- Null = Immediate_Mask_32 | 0x08000u,
- Boolean = Immediate_Mask_32 | 0x10000u,
- Integer = Immediate_Mask_32 | 0x18000u
+ static_assert(IsIntegerConvertible_Value ==
+ (quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged))
+ >> (IsIntegerConvertible_Shift - Tag_Shift));
+
+ static_assert(IsIntegerOrBool_Value ==
+ (quint32(TagBit::IntOrBool) | quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged))
+ >> (IsIntegerOrBool_Shift - Tag_Shift));
+
+ static constexpr quint64 ExponentMask = 0b0111111111110000ull << 48;
+
+ static constexpr quint64 Top1Mask = 0b1000000000000000ull << 48;
+ static constexpr quint64 Upper3Mask = 0b0000000000001110ull << 48;
+ static constexpr quint64 Lower5Mask = 0b0000000000011111ull;
+
+ static constexpr quint64 ManagedMask = ExponentMask | quint64(TagBit::Unmanaged) << Tag_Shift;
+ static constexpr quint64 DoubleMask = ManagedMask | quint64(TagBit::Special) << Tag_Shift;
+ static constexpr quint64 NumberMask = ManagedMask | quint64(TagBit::Number) << Tag_Shift;
+ static constexpr quint64 IntOrBoolMask = ManagedMask | quint64(TagBit::IntOrBool) << Tag_Shift;
+ static constexpr quint64 IntCompatMask = ManagedMask | quint64(TagBit::IntCompat) << Tag_Shift;
+
+ static constexpr quint64 EncodeMask = DoubleMask | NumberMask;
+
+ static constexpr quint64 DoubleDiscriminator
+ = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Special)) << Tag_Shift);
+ static constexpr quint64 NumberDiscriminator
+ = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Number)) << Tag_Shift);
+
+ // Things we can immediately determine by just looking at the upper 4 bytes.
+ enum class QuickType : quint32 {
+ // Managed takes precedence over all others. That is, other bits may be set if it's managed.
+ // However, since all others include the Unmanaged bit, we can still check them with simple
+ // equality operations.
+ Managed = Managed_Type,
+
+ Empty = Empty_Type,
+ Null = Null_Type,
+ Boolean = Boolean_Type,
+ Integer = Integer_Type,
+
+ PlusInf = quint32(TagBit::Number) | quint32(TagBit::Special) | quint32(TagBit::Unmanaged),
+ MinusInf = PlusInf | quint32(TagBit::SpecialNegative),
+ NaN = PlusInf | quint32(TagBit::SpecialQNaN),
+ MinusNaN = NaN | quint32(TagBit::SpecialNegative), // Can happen with UMinus on NaN
+ // All other values are doubles
};
+ // Aliases for easier porting. Remove those when possible
+ using ValueTypeInternal = QuickType;
enum {
- Managed_Type_Internal = 0
+ QT_Empty = Empty_Type,
+ QT_Null = Null_Type,
+ QT_Bool = Boolean_Type,
+ QT_Int = Integer_Type,
+ QuickType_Shift = Tag_Shift,
};
- using ValueTypeInternal = ValueTypeInternal_64;
+ inline Type type() const
+ {
+ const quint64 masked = _val & DoubleMask;
+ if (masked >= DoubleDiscriminator)
+ return Double_Type;
- enum {
- NaN_Mask = 0x7ff80000,
- };
+ // Any bit set in the exponent would have been caught above, as well as both bits being set.
+ // None of them being set as well as only Special being set means "managed".
+ // Only Unmanaged being set means "unmanaged". That's all remaining options.
+ if (masked != tagBitMask(TagBit::Unmanaged)) {
+ Q_ASSERT((_val & tagBitMask(TagBit::Unmanaged)) == 0);
+ return isUndefined() ? Undefined_Type : Managed_Type;
+ }
+
+ const Type ret = Type(tag());
+ Q_ASSERT(
+ ret == Empty_Type ||
+ ret == Null_Type ||
+ ret == Boolean_Type ||
+ ret == Integer_Type);
+ return ret;
+ }
inline quint64 quickType() const { return (_val >> QuickType_Shift); }
@@ -313,34 +284,52 @@ struct StaticValue
inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
- inline bool isNumber() const { return quickType() >= QT_Int; }
-
inline bool isUndefined() const { return _val == 0; }
- inline bool isDouble() const { return (_val >> IsDouble_Shift); }
- inline bool isManaged() const
+
+ inline bool isDouble() const
{
-#if QT_POINTER_SIZE == 4
- return value() && tag() == Managed_Type_Internal;
-#else
- return _val && ((_val >> IsManagedOrUndefined_Shift) == 0);
-#endif
+ // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is
+ // larger than Unmanaged | Special.
+ //
+ // If all (flipped) exponent bits are 0:
+ // 1. If Unmanaged bit is 0, it's managed
+ // 2. If the Unmanaged bit it is 1, and the Special bit is 0, it's not a special double
+ // 3. If both are 1, it is a special double and the masked tag equals Unmanaged | Special.
+
+ return (_val & DoubleMask) >= DoubleDiscriminator;
}
- inline bool isManagedOrUndefined() const
+
+ inline bool isNumber() const
{
-#if QT_POINTER_SIZE == 4
- return tag() == Managed_Type_Internal;
-#else
- return ((_val >> IsManagedOrUndefined_Shift) == 0);
-#endif
+ // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is
+ // larger than Unmanaged | Number.
+ //
+ // If all (flipped) exponent bits are 0:
+ // 1. If Unmanaged bit is 0, it's managed
+ // 2. If the Unmanaged bit it is 1, and the Number bit is 0, it's not number
+ // 3. If both are 1, it is a number and masked tag equals Unmanaged | Number.
+
+ return (_val & NumberMask) >= NumberDiscriminator;
}
- inline bool isIntOrBool() const {
- return (_val >> IsIntegerOrBool_Shift) == 3;
+ inline bool isManagedOrUndefined() const { return (_val & ManagedMask) == 0; }
+
+ // If any other bit is set in addition to the managed mask, it's not undefined.
+ inline bool isManaged() const
+ {
+ return isManagedOrUndefined() && !isUndefined();
+ }
+
+ inline bool isIntOrBool() const
+ {
+ // It's an int or bool if all the exponent bits are 0,
+ // and the "int or bool" bit as well as the "umanaged" bit are set,
+ return (_val >> IsIntegerOrBool_Shift) == IsIntegerOrBool_Value;
}
inline bool integerCompatible() const {
Q_ASSERT(!isEmpty());
- return (_val >> IsIntegerConvertible_Shift) == 1;
+ return (_val >> IsIntegerConvertible_Shift) == IsIntegerConvertible_Value;
}
static inline bool integerCompatible(StaticValue a, StaticValue b) {
@@ -353,36 +342,45 @@ struct StaticValue
inline bool isNaN() const
{
- return (tag() & 0x7ffc0000 ) == 0x00040000;
+ switch (QuickType(tag())) {
+ case QuickType::NaN:
+ case QuickType::MinusNaN:
+ return true;
+ default:
+ return false;
+ }
}
inline bool isPositiveInt() const {
-#if QT_POINTER_SIZE == 4
return isInteger() && int_32() >= 0;
-#else
- return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
-#endif
}
QV4_NEARLY_ALWAYS_INLINE double doubleValue() const {
Q_ASSERT(isDouble());
double d;
- StaticValue v = *this;
- v._val ^= NaNEncodeMask;
- memcpy(&d, &v._val, 8);
+ const quint64 unmasked = _val ^ EncodeMask;
+ memcpy(&d, &unmasked, 8);
return d;
}
QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) {
- if (qt_is_nan(d))
- d = qt_qnan();
- memcpy(&_val, &d, 8);
- _val ^= NaNEncodeMask;
+ if (qt_is_nan(d)) {
+ // We cannot store just any NaN. It has to be a NaN with only the quiet bit
+ // set in the upper bits of the mantissa and the sign bit either on or off.
+ // qt_qnan() happens to produce such a thing via std::numeric_limits,
+ // but this is actually not guaranteed. Therefore, we make our own.
+ _val = (quint64(std::signbit(d) ? QuickType::MinusNaN : QuickType::NaN) << Tag_Shift);
+ Q_ASSERT(isNaN());
+ } else {
+ memcpy(&_val, &d, 8);
+ _val ^= EncodeMask;
+ }
+
Q_ASSERT(isDouble());
}
inline bool isInt32() {
- if (tag() == quint32(ValueTypeInternal::Integer))
+ if (tag() == quint32(QuickType::Integer))
return true;
if (isDouble()) {
double d = doubleValue();
@@ -395,12 +393,12 @@ struct StaticValue
}
QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
- int i = int(d);
+ int i = QJSNumberCoercion::toInteger(d);
return (i == d && !(d == 0 && std::signbit(d)));
}
double asDouble() const {
- if (tag() == quint32(ValueTypeInternal::Integer))
+ if (tag() == quint32(QuickType::Integer))
return int_32();
return doubleValue();
}
@@ -416,7 +414,7 @@ struct StaticValue
inline bool tryIntegerConversion() {
bool b = integerCompatible();
if (b)
- setTagValue(quint32(ValueTypeInternal::Integer), value());
+ setTagValue(quint32(QuickType::Integer), value());
return b;
}
@@ -440,24 +438,25 @@ struct StaticValue
case Integer_Type:
return int_32();
case Double_Type:
- return Double::toInt32(doubleValue());
+ return QJSNumberCoercion::toInteger(doubleValue());
case Empty_Type:
case Undefined_Type:
case Managed_Type:
- break;
+ return 0; // Coercion of NaN to int, results in 0;
}
- return Double::toInt32(std::numeric_limits<double>::quiet_NaN());
+
+ Q_UNREACHABLE_RETURN(0);
}
ReturnedValue *data_ptr() { return &_val; }
constexpr ReturnedValue asReturnedValue() const { return _val; }
constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; }
- inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; }
- static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; }
- static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; }
+ inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(QuickType::Empty), 0) }; }
+ static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(QuickType::Boolean), b) }; }
+ static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(QuickType::Integer), quint32(i)) }; }
inline static constexpr StaticValue undefinedValue() { return { 0 }; }
- static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; }
+ static inline constexpr StaticValue nullValue() { return { tagValue(quint32(QuickType::Null), 0) }; }
static inline StaticValue fromDouble(double d)
{
@@ -470,7 +469,7 @@ struct StaticValue
{
StaticValue v;
if (i < uint(std::numeric_limits<int>::max())) {
- v.setTagValue(quint32(ValueTypeInternal::Integer), i);
+ v.setTagValue(quint32(QuickType::Integer), i);
} else {
v.setDouble(i);
}
@@ -488,15 +487,140 @@ struct StaticValue
static int toInt32(double d)
{
- return Double::toInt32(d);
+ return QJSNumberCoercion::toInteger(d);
}
static unsigned int toUInt32(double d)
{
return static_cast<uint>(toInt32(d));
}
+
+ // While a value containing a Heap::Base* is not actually static, we still implement
+ // the setting and retrieving of heap pointers here in order to have the encoding
+ // scheme completely in one place.
+
+#if QT_POINTER_SIZE == 8
+
+ // All pointer shifts are from more significant to less significant bits.
+ // When encoding, we shift right by that amount. When decoding, we shift left.
+ // Negative numbers mean shifting the other direction. 0 means no shifting.
+ //
+ // The IA64 and Sparc64 cases are mostly there to demonstrate the idea. Sparc64
+ // and IA64 are not officially supported, but we can expect more platforms with
+ // similar "problems" in the future.
+ enum PointerShift {
+#if 0 && defined(Q_OS_ANDROID) && defined(Q_PROCESSOR_ARM_64)
+ // We used to assume that Android on arm64 uses the top byte to store pointer tags.
+ // However, at least currently, the pointer tags are only applied on new/malloc and
+ // delete/free, not on mmap() and munmap(). We manage the JS heap directly using
+ // mmap, so we don't have to preserve any tags.
+ //
+ // If this ever changes, here is how to preserve the top byte:
+ // Move it to Upper3 and Lower5.
+ Top1Shift = 0,
+ Upper3Shift = 12,
+ Lower5Shift = 56,
+#elif defined(Q_PROCESSOR_IA64)
+ // On ia64, bits 63-61 in a 64-bit pointer are used to store the virtual region
+ // number. We can move those to Upper3.
+ Top1Shift = 0,
+ Upper3Shift = 12,
+ Lower5Shift = 0,
+#elif defined(Q_PROCESSOR_SPARC_64)
+ // Sparc64 wants to use 52 bits for pointers.
+ // Upper3 can stay where it is, bit48 moves to the top bit.
+ Top1Shift = -15,
+ Upper3Shift = 0,
+ Lower5Shift = 0,
+#elif 0 // TODO: Once we need 5-level page tables, add the appropriate check here.
+ // With 5-level page tables (as possible on linux) we need 57 address bits.
+ // Upper3 can stay where it is, bit48 moves to the top bit, the rest moves to Lower5.
+ Top1Shift = -15,
+ Upper3Shift = 0,
+ Lower5Shift = 52,
+#else
+ Top1Shift = 0,
+ Upper3Shift = 0,
+ Lower5Shift = 0
+#endif
+ };
+
+ template<int Offset, quint64 Mask>
+ static constexpr quint64 movePointerBits(quint64 val)
+ {
+ if constexpr (Offset > 0)
+ return (val & ~Mask) | ((val & Mask) >> Offset);
+ if constexpr (Offset < 0)
+ return (val & ~Mask) | ((val & Mask) << -Offset);
+ return val;
+ }
+
+ template<int Offset, quint64 Mask>
+ static constexpr quint64 storePointerBits(quint64 val)
+ {
+ constexpr quint64 OriginMask = movePointerBits<-Offset, Mask>(Mask);
+ return movePointerBits<Offset, OriginMask>(val);
+ }
+
+ template<int Offset, quint64 Mask>
+ static constexpr quint64 retrievePointerBits(quint64 val)
+ {
+ return movePointerBits<-Offset, Mask>(val);
+ }
+
+ QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
+ {
+ Q_ASSERT(!(_val & ManagedMask));
+
+ // Re-assemble the pointer from its fragments.
+ const quint64 tmp = retrievePointerBits<Top1Shift, Top1Mask>(
+ retrievePointerBits<Upper3Shift, Upper3Mask>(
+ retrievePointerBits<Lower5Shift, Lower5Mask>(_val)));
+
+ HeapBasePtr b;
+ memcpy(&b, &tmp, 8);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
+ {
+ quint64 tmp;
+ memcpy(&tmp, &b, 8);
+
+ // Has to be aligned to 32 bytes
+ Q_ASSERT(!(tmp & Lower5Mask));
+
+ // MinGW produces a bogus warning about array bounds.
+ // There is no array access here.
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Warray-bounds")
+
+ // Encode the pointer.
+ _val = storePointerBits<Top1Shift, Top1Mask>(
+ storePointerBits<Upper3Shift, Upper3Mask>(
+ storePointerBits<Lower5Shift, Lower5Mask>(tmp)));
+
+ QT_WARNING_POP
+ }
+#elif QT_POINTER_SIZE == 4
+ QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
+ {
+ Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32));
+ HeapBasePtr b;
+ quint32 v = value();
+ memcpy(&b, &v, 4);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
+ {
+ quint32 v;
+ memcpy(&v, &b, 4);
+ setTagValue(quint32(QuickType::Managed), v);
+ }
+#else
+# error "unsupported pointer size"
+#endif
};
-Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value);
+Q_STATIC_ASSERT(std::is_trivial_v<StaticValue>);
struct Encode {
static constexpr ReturnedValue undefined() {
diff --git a/src/qml/common/qv4stringtoarrayindex_p.h b/src/qml/common/qv4stringtoarrayindex_p.h
index 61bd988d1e..7bdabd4f3f 100644
--- a/src/qml/common/qv4stringtoarrayindex_p.h
+++ b/src/qml/common/qv4stringtoarrayindex_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $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) 2019 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
#ifndef QV4STRINGTOARRAYINDEX_P_H
#define QV4STRINGTOARRAYINDEX_P_H
@@ -65,6 +29,8 @@ inline uint charToUInt(const char *ch) { return static_cast<unsigned char>(*ch);
template <typename T>
uint stringToArrayIndex(const T *ch, const T *end)
{
+ if (ch == end)
+ return std::numeric_limits<uint>::max();
uint i = charToUInt(ch) - '0';
if (i > 9)
return std::numeric_limits<uint>::max();
@@ -77,7 +43,7 @@ uint stringToArrayIndex(const T *ch, const T *end)
uint x = charToUInt(ch) - '0';
if (x > 9)
return std::numeric_limits<uint>::max();
- if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x
+ if (qMulOverflow(i, uint(10), &i) || qAddOverflow(i, x, &i)) // i = i * 10 + x
return std::numeric_limits<uint>::max();
++ch;
}
@@ -86,7 +52,7 @@ uint stringToArrayIndex(const T *ch, const T *end)
inline uint stringToArrayIndex(const QString &str)
{
- return stringToArrayIndex(str.constData(), str.constData() + str.length());
+ return stringToArrayIndex(str.constData(), str.constData() + str.size());
}
} // namespace QV4